@tuturuuu/ui 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/package.json +41 -34
  3. package/src/components/ui/currency-input.tsx +65 -23
  4. package/src/components/ui/custom/__tests__/sidebar-context.test.tsx +64 -0
  5. package/src/components/ui/custom/__tests__/sidebar-remote-behavior-bridge.test.tsx +109 -0
  6. package/src/components/ui/custom/combobox.test.tsx +141 -0
  7. package/src/components/ui/custom/combobox.tsx +105 -36
  8. package/src/components/ui/custom/settings/task-settings.tsx +126 -0
  9. package/src/components/ui/custom/settings/task-sound-settings.test.tsx +146 -0
  10. package/src/components/ui/custom/sidebar-context.tsx +68 -6
  11. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +21 -2
  12. package/src/components/ui/finance/finance-layout.tsx +2 -4
  13. package/src/components/ui/finance/shared/balance-mode-toggle.tsx +35 -0
  14. package/src/components/ui/finance/shared/finance-layout-controls.tsx +43 -0
  15. package/src/components/ui/finance/shared/quick-actions.tsx +14 -6
  16. package/src/components/ui/finance/shared/use-finance-balance-mode.ts +72 -0
  17. package/src/components/ui/finance/shared/wallet-balance-mode.test.ts +66 -0
  18. package/src/components/ui/finance/shared/wallet-balance-mode.ts +42 -0
  19. package/src/components/ui/finance/transactions/form-types.ts +23 -0
  20. package/src/components/ui/finance/transactions/form.tsx +81 -22
  21. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +29 -18
  22. package/src/components/ui/finance/transactions/transaction-card.tsx +75 -43
  23. package/src/components/ui/finance/transactions/transfer-merge.test.ts +90 -0
  24. package/src/components/ui/finance/transactions/transfer-merge.ts +52 -0
  25. package/src/components/ui/finance/transactions/wallet-filter.tsx +21 -2
  26. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-adjustment-dialog.tsx +219 -0
  27. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-amount.tsx +32 -0
  28. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-delete-dialog.tsx +50 -0
  29. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-dialog.tsx +138 -0
  30. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-history-dialog.tsx +617 -0
  31. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-panel.tsx +197 -0
  32. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-sections.tsx +201 -0
  33. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoints.test.tsx +541 -0
  34. package/src/components/ui/finance/wallets/checkpoints/wallet-total-check-dialog.tsx +362 -0
  35. package/src/components/ui/finance/wallets/columns-rendering.test.tsx +125 -0
  36. package/src/components/ui/finance/wallets/columns.test.ts +56 -0
  37. package/src/components/ui/finance/wallets/columns.tsx +196 -43
  38. package/src/components/ui/finance/wallets/form.test.tsx +79 -14
  39. package/src/components/ui/finance/wallets/form.tsx +41 -197
  40. package/src/components/ui/finance/wallets/query-invalidation.ts +3 -0
  41. package/src/components/ui/finance/wallets/wallet-basics-fields.tsx +141 -0
  42. package/src/components/ui/finance/wallets/wallet-credit-fields.tsx +136 -0
  43. package/src/components/ui/finance/wallets/walletId/credit-wallet-summary.tsx +143 -68
  44. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.test.tsx +105 -0
  45. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +120 -16
  46. package/src/components/ui/finance/wallets/walletId/wallet-details-amount.test.tsx +64 -0
  47. package/src/components/ui/finance/wallets/walletId/wallet-details-amount.tsx +226 -6
  48. package/src/components/ui/finance/wallets/walletId/wallet-details-page.test.tsx +71 -5
  49. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +52 -35
  50. package/src/components/ui/finance/wallets/wallets-data-table.test.tsx +171 -0
  51. package/src/components/ui/finance/wallets/wallets-data-table.tsx +132 -29
  52. package/src/components/ui/finance/wallets/wallets-page.test.tsx +117 -36
  53. package/src/components/ui/finance/wallets/wallets-page.tsx +40 -64
  54. package/src/components/ui/storefront/accent-button.tsx +33 -0
  55. package/src/components/ui/storefront/cart-summary.tsx +140 -0
  56. package/src/components/ui/storefront/empty-listings.tsx +32 -0
  57. package/src/components/ui/storefront/hero-panel.tsx +70 -0
  58. package/src/components/ui/storefront/image-panel.tsx +40 -0
  59. package/src/components/ui/storefront/index.ts +12 -0
  60. package/src/components/ui/storefront/listing-card.tsx +129 -0
  61. package/src/components/ui/storefront/storefront-surface.test.tsx +85 -0
  62. package/src/components/ui/storefront/storefront-surface.tsx +235 -0
  63. package/src/components/ui/storefront/types.ts +99 -0
  64. package/src/components/ui/storefront/utils.ts +90 -0
  65. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-move.test.tsx +14 -0
  66. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +29 -0
  67. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +134 -0
  68. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +127 -0
  69. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +17 -42
  70. package/src/components/ui/tu-do/boards/boardId/timeline-board-open-task.test.tsx +164 -0
  71. package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +25 -16
  72. package/src/components/ui/tu-do/hooks/useTaskDialog.ts +15 -1
  73. package/src/components/ui/tu-do/my-tasks/__tests__/use-task-context-actions.test.ts +11 -0
  74. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +2 -0
  75. package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +124 -7
  76. package/src/components/ui/tu-do/providers/__tests__/task-dialog-provider.test.tsx +217 -5
  77. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +180 -35
  78. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +222 -26
  79. package/src/components/ui/tu-do/shared/board-client.tsx +1 -3
  80. package/src/components/ui/tu-do/shared/list-view-context-menu.test.tsx +55 -2
  81. package/src/components/ui/tu-do/shared/list-view.tsx +23 -16
  82. package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +93 -76
  83. package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +11 -0
  84. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +268 -0
  85. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +243 -0
  86. package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +26 -0
  87. package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.test.tsx +129 -0
  88. package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.tsx +358 -0
  89. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +1 -1
  90. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +6 -2
  91. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +36 -20
  92. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.test.tsx +41 -1
  93. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.tsx +157 -102
  94. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-form-reset.ts +18 -2
  95. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-realtime-sync.ts +1 -2
  96. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.test.ts +84 -1
  97. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +5 -1
  98. package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +5 -3
  99. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +300 -172
  100. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +959 -340
  101. package/src/components/ui/tu-do/shared/task-sound-effects.test.ts +189 -0
  102. package/src/components/ui/tu-do/shared/task-sound-effects.tsx +468 -0
  103. package/src/hooks/__tests__/use-task-actions.test.tsx +61 -0
  104. package/src/hooks/use-task-actions.ts +45 -0
  105. package/src/hooks/useBoardRealtime.ts +54 -1
  106. package/src/hooks/useBoardRealtimeEventHandler.ts +169 -4
  107. package/src/hooks/useTaskUserRealtime.ts +338 -0
@@ -8,6 +8,7 @@ import { SelectField } from '@tuturuuu/ui/custom/select-field';
8
8
  import {
9
9
  Form,
10
10
  FormControl,
11
+ FormDescription,
11
12
  FormField,
12
13
  FormItem,
13
14
  FormLabel,
@@ -23,7 +24,7 @@ import {
23
24
  } from '@tuturuuu/utils/currencies';
24
25
  import { useRouter } from 'next/navigation';
25
26
  import { useTranslations } from 'next-intl';
26
- import { useCallback, useMemo, useState } from 'react';
27
+ import { useMemo, useState } from 'react';
27
28
  import * as z from 'zod';
28
29
  import { toast } from '../../sonner';
29
30
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../tabs';
@@ -32,7 +33,8 @@ import {
32
33
  useFinanceConfidentialVisibility,
33
34
  } from '../shared/use-finance-confidential-visibility';
34
35
  import { invalidateWalletMutationQueries } from './query-invalidation';
35
- import { WalletIconImagePicker } from './wallet-icon-image-picker';
36
+ import { WalletBasicsFields } from './wallet-basics-fields';
37
+ import { WalletCreditFields } from './wallet-credit-fields';
36
38
  import WalletRoleAccess from './walletId/wallet-role-access';
37
39
 
38
40
  const walletValidationMessageKeys = {
@@ -87,11 +89,14 @@ const createWalletFormSchema = (
87
89
  }
88
90
  });
89
91
 
90
- type WalletFormValues = z.infer<ReturnType<typeof createWalletFormSchema>>;
92
+ export type WalletFormValues = z.infer<
93
+ ReturnType<typeof createWalletFormSchema>
94
+ >;
91
95
 
92
96
  interface Props {
93
97
  wsId: string;
94
98
  data?: Wallet;
99
+ defaultType?: WalletFormValues['type'];
95
100
  onFinish?: (data: WalletFormValues) => void;
96
101
  isPersonalWorkspace?: boolean;
97
102
  }
@@ -99,6 +104,7 @@ interface Props {
99
104
  export function WalletForm({
100
105
  wsId,
101
106
  data,
107
+ defaultType = 'STANDARD',
102
108
  onFinish,
103
109
  isPersonalWorkspace,
104
110
  }: Props) {
@@ -111,11 +117,6 @@ export function WalletForm({
111
117
  const [loading, setLoading] = useState(false);
112
118
  const router = useRouter();
113
119
 
114
- // Local state for immediate UI updates (icon/image)
115
- const [localIcon, setLocalIcon] = useState<string | null>(data?.icon || null);
116
- const [localImageSrc, setLocalImageSrc] = useState<string | null>(
117
- data?.image_src || null
118
- );
119
120
  const formSchema = useMemo(() => createWalletFormSchema(t), [t]);
120
121
 
121
122
  const form = useForm({
@@ -125,37 +126,21 @@ export function WalletForm({
125
126
  name: data?.name || '',
126
127
  description: data?.description || '',
127
128
  balance: data?.balance || 0,
128
- type: data?.type || 'STANDARD',
129
+ type: data?.type || defaultType,
129
130
  currency: data?.currency || workspaceCurrency || 'USD',
130
131
  icon: data?.icon || null,
131
132
  image_src: data?.image_src || null,
132
133
  limit: data?.limit ?? undefined,
133
- statement_date: data?.statement_date ?? undefined,
134
- payment_date: data?.payment_date ?? undefined,
134
+ statement_date:
135
+ data?.statement_date ?? (defaultType === 'CREDIT' ? 1 : undefined),
136
+ payment_date:
137
+ data?.payment_date ?? (defaultType === 'CREDIT' ? 15 : undefined),
135
138
  },
136
139
  });
137
140
 
138
141
  const walletType = useWatch({ control: form.control, name: 'type' });
139
142
  const walletCurrency = useWatch({ control: form.control, name: 'currency' });
140
143
 
141
- // Handle icon change - update both local state and form
142
- const handleIconChange = useCallback(
143
- (value: string | null) => {
144
- setLocalIcon(value);
145
- form.setValue('icon', value);
146
- },
147
- [form]
148
- );
149
-
150
- // Handle image_src change - update both local state and form
151
- const handleImageSrcChange = useCallback(
152
- (value: string | null) => {
153
- setLocalImageSrc(value);
154
- form.setValue('image_src', value);
155
- },
156
- [form]
157
- );
158
-
159
144
  async function onSubmit(formData: WalletFormValues) {
160
145
  setLoading(true);
161
146
 
@@ -184,88 +169,11 @@ export function WalletForm({
184
169
  const formContent = (
185
170
  <Form {...form}>
186
171
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
187
- <div className="flex items-end gap-2">
188
- <FormField
189
- control={form.control}
190
- name="icon"
191
- render={() => (
192
- <FormItem>
193
- <FormLabel>{t('wallet-data-table.icon')}</FormLabel>
194
- <FormControl>
195
- <WalletIconImagePicker
196
- icon={localIcon}
197
- imageSrc={localImageSrc}
198
- onIconChange={handleIconChange}
199
- onImageSrcChange={handleImageSrcChange}
200
- disabled={loading}
201
- translations={{
202
- selectIconOrImage: t(
203
- 'wallet-data-table.select_icon_or_image'
204
- ),
205
- iconTab: t('wallet-data-table.icon_tab'),
206
- bankTab: t('wallet-data-table.bank_tab'),
207
- mobileTab: t('wallet-data-table.mobile_tab'),
208
- searchPlaceholder: t('wallet-data-table.search'),
209
- clear: t('common.clear'),
210
- selectIcon: t('wallet-data-table.select_icon'),
211
- iconDescription: t('wallet-data-table.icon_description'),
212
- changeIconOrImageDescription: t(
213
- 'wallet-data-table.change_icon_or_image_description'
214
- ),
215
- chooseIconOrImageDescription: t(
216
- 'wallet-data-table.choose_icon_or_image_description'
217
- ),
218
- searchIcons: t('wallet-data-table.search_icons'),
219
- noIcon: t('wallet-data-table.no_icon'),
220
- banksAvailable: (count) =>
221
- t('wallet-data-table.banks_available', { count }),
222
- mobileProvidersAvailable: (count) =>
223
- t('wallet-data-table.mobile_providers_available', {
224
- count,
225
- }),
226
- }}
227
- />
228
- </FormControl>
229
- <FormMessage />
230
- </FormItem>
231
- )}
232
- />
233
-
234
- <FormField
235
- control={form.control}
236
- name="name"
237
- disabled={loading}
238
- render={({ field }) => (
239
- <FormItem className="flex-1">
240
- <FormLabel>{t('wallet-data-table.wallet_name')}</FormLabel>
241
- <FormControl>
242
- <Input
243
- placeholder={t('wallet-data-table.wallet_name_placeholder')}
244
- {...field}
245
- />
246
- </FormControl>
247
- <FormMessage />
248
- </FormItem>
249
- )}
250
- />
251
- </div>
252
-
253
- <FormField
254
- control={form.control}
255
- name="description"
256
- disabled={loading}
257
- render={({ field }) => (
258
- <FormItem>
259
- <FormLabel>{t('wallet-data-table.description')}</FormLabel>
260
- <FormControl>
261
- <Input
262
- placeholder={t('wallet-data-table.description_placeholder')}
263
- {...field}
264
- />
265
- </FormControl>
266
- <FormMessage />
267
- </FormItem>
268
- )}
172
+ <WalletBasicsFields
173
+ form={form}
174
+ loading={loading}
175
+ defaultIcon={data?.icon}
176
+ defaultImageSrc={data?.image_src}
269
177
  />
270
178
 
271
179
  <FormField
@@ -336,9 +244,25 @@ export function WalletForm({
336
244
  ]}
337
245
  classNames={{ root: 'w-full' }}
338
246
  value={field.value}
339
- onValueChange={field.onChange}
247
+ onValueChange={(value) => {
248
+ field.onChange(value);
249
+
250
+ if (value === 'CREDIT') {
251
+ if (!form.getValues('statement_date')) {
252
+ form.setValue('statement_date', 1);
253
+ }
254
+ if (!form.getValues('payment_date')) {
255
+ form.setValue('payment_date', 15);
256
+ }
257
+ }
258
+ }}
340
259
  />
341
260
  </FormControl>
261
+ <FormDescription>
262
+ {walletType === 'CREDIT'
263
+ ? t('wallet-data-table.wallet_type_credit_description')
264
+ : t('wallet-data-table.wallet_type_standard_description')}
265
+ </FormDescription>
342
266
  <FormMessage />
343
267
  </FormItem>
344
268
  )}
@@ -369,91 +293,11 @@ export function WalletForm({
369
293
  />
370
294
 
371
295
  {walletType === 'CREDIT' && (
372
- <div className="space-y-2 rounded-lg border p-3">
373
- <div className="font-medium text-sm">
374
- {t('wallet-data-table.credit_details')}
375
- </div>
376
-
377
- <FormField
378
- control={form.control}
379
- name="limit"
380
- disabled={loading}
381
- render={({ field }) => (
382
- <FormItem>
383
- <FormLabel>{t('wallet-data-table.credit_limit')}</FormLabel>
384
- <FormControl>
385
- <Input
386
- type="number"
387
- placeholder="0"
388
- {...field}
389
- value={field.value ?? ''}
390
- onChange={(e) => {
391
- const val = parseFloat(e.target.value);
392
- field.onChange(Number.isNaN(val) ? undefined : val);
393
- }}
394
- />
395
- </FormControl>
396
- <FormMessage />
397
- </FormItem>
398
- )}
399
- />
400
-
401
- <div className="grid grid-cols-2 gap-2">
402
- <FormField
403
- control={form.control}
404
- name="statement_date"
405
- disabled={loading}
406
- render={({ field }) => (
407
- <FormItem>
408
- <FormLabel>
409
- {t('wallet-data-table.statement_date')}
410
- </FormLabel>
411
- <FormControl>
412
- <Input
413
- type="number"
414
- min={1}
415
- max={31}
416
- placeholder="1"
417
- {...field}
418
- value={field.value ?? ''}
419
- onChange={(e) => {
420
- const val = parseInt(e.target.value, 10);
421
- field.onChange(Number.isNaN(val) ? undefined : val);
422
- }}
423
- />
424
- </FormControl>
425
- <FormMessage />
426
- </FormItem>
427
- )}
428
- />
429
-
430
- <FormField
431
- control={form.control}
432
- name="payment_date"
433
- disabled={loading}
434
- render={({ field }) => (
435
- <FormItem>
436
- <FormLabel>{t('wallet-data-table.payment_date')}</FormLabel>
437
- <FormControl>
438
- <Input
439
- type="number"
440
- min={1}
441
- max={31}
442
- placeholder="1"
443
- {...field}
444
- value={field.value ?? ''}
445
- onChange={(e) => {
446
- const val = parseInt(e.target.value, 10);
447
- field.onChange(Number.isNaN(val) ? undefined : val);
448
- }}
449
- />
450
- </FormControl>
451
- <FormMessage />
452
- </FormItem>
453
- )}
454
- />
455
- </div>
456
- </div>
296
+ <WalletCreditFields
297
+ form={form}
298
+ loading={loading}
299
+ currency={walletCurrency || workspaceCurrency || 'USD'}
300
+ />
457
301
  )}
458
302
 
459
303
  <div className="h-2" />
@@ -14,6 +14,9 @@ const WALLET_MUTATION_QUERY_ROOTS = new Set([
14
14
  'opening-balance',
15
15
  'recurring_transactions',
16
16
  'spending_trends',
17
+ 'wallet-checkpoint-summary',
18
+ 'wallet-checkpoint-history',
19
+ 'wallet-checkpoints',
17
20
  'upcoming_recurring_transactions',
18
21
  'wallets',
19
22
  'workspace-invoices',
@@ -0,0 +1,141 @@
1
+ 'use client';
2
+
3
+ import {
4
+ FormControl,
5
+ FormField,
6
+ FormItem,
7
+ FormLabel,
8
+ FormMessage,
9
+ } from '@tuturuuu/ui/form';
10
+ import type { UseFormReturn } from '@tuturuuu/ui/hooks/use-form';
11
+ import { Input } from '@tuturuuu/ui/input';
12
+ import { useTranslations } from 'next-intl';
13
+ import { useCallback, useState } from 'react';
14
+ import type { WalletFormValues } from './form';
15
+ import { WalletIconImagePicker } from './wallet-icon-image-picker';
16
+
17
+ interface WalletBasicsFieldsProps {
18
+ defaultIcon?: string | null;
19
+ defaultImageSrc?: string | null;
20
+ form: UseFormReturn<WalletFormValues>;
21
+ loading: boolean;
22
+ }
23
+
24
+ export function WalletBasicsFields({
25
+ defaultIcon,
26
+ defaultImageSrc,
27
+ form,
28
+ loading,
29
+ }: WalletBasicsFieldsProps) {
30
+ const t = useTranslations();
31
+ const [localIcon, setLocalIcon] = useState<string | null>(
32
+ defaultIcon || null
33
+ );
34
+ const [localImageSrc, setLocalImageSrc] = useState<string | null>(
35
+ defaultImageSrc || null
36
+ );
37
+
38
+ const handleIconChange = useCallback(
39
+ (value: string | null) => {
40
+ setLocalIcon(value);
41
+ form.setValue('icon', value);
42
+ },
43
+ [form]
44
+ );
45
+
46
+ const handleImageSrcChange = useCallback(
47
+ (value: string | null) => {
48
+ setLocalImageSrc(value);
49
+ form.setValue('image_src', value);
50
+ },
51
+ [form]
52
+ );
53
+
54
+ return (
55
+ <>
56
+ <div className="flex items-end gap-2">
57
+ <FormField
58
+ control={form.control}
59
+ name="icon"
60
+ render={() => (
61
+ <FormItem>
62
+ <FormLabel>{t('wallet-data-table.icon')}</FormLabel>
63
+ <FormControl>
64
+ <WalletIconImagePicker
65
+ icon={localIcon}
66
+ imageSrc={localImageSrc}
67
+ onIconChange={handleIconChange}
68
+ onImageSrcChange={handleImageSrcChange}
69
+ disabled={loading}
70
+ translations={{
71
+ selectIconOrImage: t(
72
+ 'wallet-data-table.select_icon_or_image'
73
+ ),
74
+ iconTab: t('wallet-data-table.icon_tab'),
75
+ bankTab: t('wallet-data-table.bank_tab'),
76
+ mobileTab: t('wallet-data-table.mobile_tab'),
77
+ searchPlaceholder: t('wallet-data-table.search'),
78
+ clear: t('common.clear'),
79
+ selectIcon: t('wallet-data-table.select_icon'),
80
+ iconDescription: t('wallet-data-table.icon_description'),
81
+ changeIconOrImageDescription: t(
82
+ 'wallet-data-table.change_icon_or_image_description'
83
+ ),
84
+ chooseIconOrImageDescription: t(
85
+ 'wallet-data-table.choose_icon_or_image_description'
86
+ ),
87
+ searchIcons: t('wallet-data-table.search_icons'),
88
+ noIcon: t('wallet-data-table.no_icon'),
89
+ banksAvailable: (count) =>
90
+ t('wallet-data-table.banks_available', { count }),
91
+ mobileProvidersAvailable: (count) =>
92
+ t('wallet-data-table.mobile_providers_available', {
93
+ count,
94
+ }),
95
+ }}
96
+ />
97
+ </FormControl>
98
+ <FormMessage />
99
+ </FormItem>
100
+ )}
101
+ />
102
+
103
+ <FormField
104
+ control={form.control}
105
+ name="name"
106
+ disabled={loading}
107
+ render={({ field }) => (
108
+ <FormItem className="flex-1">
109
+ <FormLabel>{t('wallet-data-table.wallet_name')}</FormLabel>
110
+ <FormControl>
111
+ <Input
112
+ placeholder={t('wallet-data-table.wallet_name_placeholder')}
113
+ {...field}
114
+ />
115
+ </FormControl>
116
+ <FormMessage />
117
+ </FormItem>
118
+ )}
119
+ />
120
+ </div>
121
+
122
+ <FormField
123
+ control={form.control}
124
+ name="description"
125
+ disabled={loading}
126
+ render={({ field }) => (
127
+ <FormItem>
128
+ <FormLabel>{t('wallet-data-table.description')}</FormLabel>
129
+ <FormControl>
130
+ <Input
131
+ placeholder={t('wallet-data-table.description_placeholder')}
132
+ {...field}
133
+ />
134
+ </FormControl>
135
+ <FormMessage />
136
+ </FormItem>
137
+ )}
138
+ />
139
+ </>
140
+ );
141
+ }
@@ -0,0 +1,136 @@
1
+ 'use client';
2
+
3
+ import { CreditCard } from '@tuturuuu/icons';
4
+ import { CurrencyInput } from '@tuturuuu/ui/currency-input';
5
+ import {
6
+ FormControl,
7
+ FormDescription,
8
+ FormField,
9
+ FormItem,
10
+ FormLabel,
11
+ FormMessage,
12
+ } from '@tuturuuu/ui/form';
13
+ import type { UseFormReturn } from '@tuturuuu/ui/hooks/use-form';
14
+ import { Input } from '@tuturuuu/ui/input';
15
+ import { getCurrencyLocale } from '@tuturuuu/utils/currencies';
16
+ import { useTranslations } from 'next-intl';
17
+ import type { WalletFormValues } from './form';
18
+
19
+ interface WalletCreditFieldsProps {
20
+ currency?: string;
21
+ form: UseFormReturn<WalletFormValues>;
22
+ loading: boolean;
23
+ }
24
+
25
+ export function WalletCreditFields({
26
+ currency,
27
+ form,
28
+ loading,
29
+ }: WalletCreditFieldsProps) {
30
+ const t = useTranslations();
31
+ const resolvedCurrency = currency || 'USD';
32
+
33
+ return (
34
+ <div className="space-y-3 rounded-lg border p-3">
35
+ <div className="flex items-start gap-2">
36
+ <CreditCard className="mt-0.5 h-4 w-4 text-muted-foreground" />
37
+ <div>
38
+ <div className="font-medium text-sm">
39
+ {t('wallet-data-table.credit_details')}
40
+ </div>
41
+ <p className="text-muted-foreground text-xs">
42
+ {t('wallet-data-table.credit_details_description')}
43
+ </p>
44
+ </div>
45
+ </div>
46
+
47
+ <FormField
48
+ control={form.control}
49
+ name="limit"
50
+ disabled={loading}
51
+ render={({ field }) => (
52
+ <FormItem>
53
+ <FormLabel>{t('wallet-data-table.credit_limit')}</FormLabel>
54
+ <FormControl>
55
+ <CurrencyInput
56
+ value={field.value}
57
+ onChange={(value) =>
58
+ field.onChange(value > 0 ? value : undefined)
59
+ }
60
+ disabled={field.disabled}
61
+ placeholder="0"
62
+ currencySuffix={resolvedCurrency}
63
+ locale={getCurrencyLocale(resolvedCurrency)}
64
+ hideHelpers
65
+ />
66
+ </FormControl>
67
+ <FormDescription>
68
+ {t('wallet-data-table.credit_limit_hint')}
69
+ </FormDescription>
70
+ <FormMessage />
71
+ </FormItem>
72
+ )}
73
+ />
74
+
75
+ <div className="grid gap-2 sm:grid-cols-2">
76
+ <FormField
77
+ control={form.control}
78
+ name="statement_date"
79
+ disabled={loading}
80
+ render={({ field }) => (
81
+ <FormItem>
82
+ <FormLabel>{t('wallet-data-table.statement_date')}</FormLabel>
83
+ <FormControl>
84
+ <Input
85
+ type="number"
86
+ min={1}
87
+ max={31}
88
+ placeholder="1"
89
+ {...field}
90
+ value={field.value ?? ''}
91
+ onChange={(e) => {
92
+ const value = Number.parseInt(e.target.value, 10);
93
+ field.onChange(Number.isNaN(value) ? undefined : value);
94
+ }}
95
+ />
96
+ </FormControl>
97
+ <FormDescription>
98
+ {t('wallet-data-table.statement_date_hint')}
99
+ </FormDescription>
100
+ <FormMessage />
101
+ </FormItem>
102
+ )}
103
+ />
104
+
105
+ <FormField
106
+ control={form.control}
107
+ name="payment_date"
108
+ disabled={loading}
109
+ render={({ field }) => (
110
+ <FormItem>
111
+ <FormLabel>{t('wallet-data-table.payment_date')}</FormLabel>
112
+ <FormControl>
113
+ <Input
114
+ type="number"
115
+ min={1}
116
+ max={31}
117
+ placeholder="15"
118
+ {...field}
119
+ value={field.value ?? ''}
120
+ onChange={(e) => {
121
+ const value = Number.parseInt(e.target.value, 10);
122
+ field.onChange(Number.isNaN(value) ? undefined : value);
123
+ }}
124
+ />
125
+ </FormControl>
126
+ <FormDescription>
127
+ {t('wallet-data-table.payment_date_hint')}
128
+ </FormDescription>
129
+ <FormMessage />
130
+ </FormItem>
131
+ )}
132
+ />
133
+ </div>
134
+ </div>
135
+ );
136
+ }