|
1 | 1 | <script setup lang="ts"> |
2 | | -import TextInput from '@/packages/ui/src/Input/TextInput.vue'; |
3 | | -import { formatCents } from '@/packages/ui/src/utils/money'; |
4 | | -import { ref, watch, inject, type ComputedRef } from 'vue'; |
| 2 | +import { ref } from 'vue'; |
5 | 3 | import { useFocus } from '@vueuse/core'; |
6 | | -import type { Organization } from '@/packages/api/src'; |
| 4 | +import { |
| 5 | + NumberField, |
| 6 | + NumberFieldContent, |
| 7 | + NumberFieldDecrement, |
| 8 | + NumberFieldIncrement, |
| 9 | + NumberFieldInput, |
| 10 | +} from '@/Components/ui/number-field'; |
7 | 11 |
|
8 | 12 | const props = defineProps<{ |
9 | 13 | name: string; |
10 | 14 | focus?: boolean; |
11 | 15 | currency: string; |
12 | 16 | }>(); |
13 | 17 |
|
14 | | -const organization = inject<ComputedRef<Organization>>('organization'); |
15 | | -
|
16 | 18 | const model = defineModel<number | null>({ |
17 | 19 | default: null, |
18 | 20 | }); |
19 | 21 |
|
20 | 22 | const billableRateInput = ref<HTMLInputElement | null>(null); |
21 | 23 | useFocus(billableRateInput, { initialValue: props.focus }); |
22 | 24 |
|
23 | | -function cleanUpDecimalValue(value: string) { |
24 | | - value = value.replace(/,/g, ''); |
25 | | - value = value.replace(props.currency, ''); |
26 | | - return value.replace(/\./g, ''); |
27 | | -} |
28 | | -
|
29 | | -function updateRate(value: string) { |
30 | | - value = value.trim(); |
31 | | - if (value.includes(',')) { |
32 | | - const parts = value.split(','); |
33 | | - const lastPart = (parts[parts.length - 1] = parts[parts.length - 1]); |
34 | | - if (lastPart.length === 2) { |
35 | | - // we detected a decimal number with 2 digits after the comma |
36 | | - value = cleanUpDecimalValue(value); |
37 | | - model.value = parseInt(value); |
38 | | - } |
39 | | - } else if (value.includes('.')) { |
40 | | - const parts = value.split('.'); |
41 | | - const lastPart = (parts[parts.length - 1] = parts[parts.length - 1]); |
42 | | - if (lastPart.length === 2) { |
43 | | - value = cleanUpDecimalValue(value); |
44 | | - model.value = parseInt(value); |
45 | | - } |
46 | | - } else if (value === '') { |
47 | | - model.value = 0; |
48 | | - } else { |
49 | | - // if it doesn't contain a comma or a dot, it's probably a whole number so let's convert it to cents |
50 | | - const parsedValue = parseInt(cleanUpDecimalValue(value)) * 100; |
51 | | - if (parsedValue) { |
52 | | - model.value = parsedValue; |
53 | | - } else { |
54 | | - model.value = 0; |
55 | | - } |
56 | | - } |
57 | | - inputValue.value = formatValue(model.value); |
58 | | -} |
59 | | -
|
60 | 25 | function formatValue(modelValue: number | null) { |
61 | | - const formattedValue = formatCents( |
62 | | - modelValue ?? 0, |
63 | | - props.currency, |
64 | | - organization?.value?.currency_format, |
65 | | - organization?.value?.currency_symbol, |
66 | | - organization?.value?.number_format |
67 | | - ); |
68 | | - return formattedValue |
69 | | - ?.replace(organization?.value?.currency_symbol ?? '', '') |
70 | | - .trim(); |
| 26 | + return modelValue ? modelValue / 100 : 0; |
71 | 27 | } |
72 | | -
|
73 | | -watch(model, (newValue) => { |
74 | | - inputValue.value = formatValue(newValue); |
75 | | -}); |
76 | | -
|
77 | | -const inputValue = ref(formatValue(model.value)); |
78 | 28 | </script> |
79 | 29 |
|
80 | 30 | <template> |
81 | 31 | <div class="relative"> |
82 | | - <TextInput |
| 32 | + <NumberField |
83 | 33 | :id="name" |
84 | 34 | ref="billableRateInput" |
85 | | - v-model="inputValue" |
86 | | - type="text" |
87 | | - :name="name" |
88 | | - placeholder="Billable Rate" |
| 35 | + :model-value="formatValue(model)" |
| 36 | + :step-snapping="false" |
89 | 37 | class="block w-full" |
90 | | - autocomplete="teamMemberRate" |
91 | | - @blur="updateRate($event.target.value)" |
92 | | - @keydown.enter="updateRate($event.target.value)" /> |
93 | | - <div |
94 | | - class="absolute top-0 right-0 h-full flex items-center px-4 font-medium pointer-events-none"> |
95 | | - <span> |
96 | | - {{ currency }} |
97 | | - </span> |
98 | | - </div> |
| 38 | + :format-options="{ |
| 39 | + style: 'currency', |
| 40 | + currency: currency, |
| 41 | + currencyDisplay: 'code', |
| 42 | + currencySign: 'accounting', |
| 43 | + }" |
| 44 | + @update:model-value="(value) => model = value * 100"> |
| 45 | + <NumberFieldContent> |
| 46 | + <NumberFieldDecrement /> |
| 47 | + <NumberFieldInput /> |
| 48 | + <NumberFieldIncrement /> |
| 49 | + </NumberFieldContent> |
| 50 | + </NumberField> |
99 | 51 | </div> |
100 | 52 | </template> |
101 | 53 |
|
|
0 commit comments