@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.
- package/CHANGELOG.md +43 -0
- package/package.json +41 -34
- package/src/components/ui/currency-input.tsx +65 -23
- package/src/components/ui/custom/__tests__/sidebar-context.test.tsx +64 -0
- package/src/components/ui/custom/__tests__/sidebar-remote-behavior-bridge.test.tsx +109 -0
- package/src/components/ui/custom/combobox.test.tsx +141 -0
- package/src/components/ui/custom/combobox.tsx +105 -36
- package/src/components/ui/custom/settings/task-settings.tsx +126 -0
- package/src/components/ui/custom/settings/task-sound-settings.test.tsx +146 -0
- package/src/components/ui/custom/sidebar-context.tsx +68 -6
- package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +21 -2
- package/src/components/ui/finance/finance-layout.tsx +2 -4
- package/src/components/ui/finance/shared/balance-mode-toggle.tsx +35 -0
- package/src/components/ui/finance/shared/finance-layout-controls.tsx +43 -0
- package/src/components/ui/finance/shared/quick-actions.tsx +14 -6
- package/src/components/ui/finance/shared/use-finance-balance-mode.ts +72 -0
- package/src/components/ui/finance/shared/wallet-balance-mode.test.ts +66 -0
- package/src/components/ui/finance/shared/wallet-balance-mode.ts +42 -0
- package/src/components/ui/finance/transactions/form-types.ts +23 -0
- package/src/components/ui/finance/transactions/form.tsx +81 -22
- package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +29 -18
- package/src/components/ui/finance/transactions/transaction-card.tsx +75 -43
- package/src/components/ui/finance/transactions/transfer-merge.test.ts +90 -0
- package/src/components/ui/finance/transactions/transfer-merge.ts +52 -0
- package/src/components/ui/finance/transactions/wallet-filter.tsx +21 -2
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-adjustment-dialog.tsx +219 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-amount.tsx +32 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-delete-dialog.tsx +50 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-dialog.tsx +138 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-history-dialog.tsx +617 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-panel.tsx +197 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-sections.tsx +201 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoints.test.tsx +541 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-total-check-dialog.tsx +362 -0
- package/src/components/ui/finance/wallets/columns-rendering.test.tsx +125 -0
- package/src/components/ui/finance/wallets/columns.test.ts +56 -0
- package/src/components/ui/finance/wallets/columns.tsx +196 -43
- package/src/components/ui/finance/wallets/form.test.tsx +79 -14
- package/src/components/ui/finance/wallets/form.tsx +41 -197
- package/src/components/ui/finance/wallets/query-invalidation.ts +3 -0
- package/src/components/ui/finance/wallets/wallet-basics-fields.tsx +141 -0
- package/src/components/ui/finance/wallets/wallet-credit-fields.tsx +136 -0
- package/src/components/ui/finance/wallets/walletId/credit-wallet-summary.tsx +143 -68
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.test.tsx +105 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +120 -16
- package/src/components/ui/finance/wallets/walletId/wallet-details-amount.test.tsx +64 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-amount.tsx +226 -6
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.test.tsx +71 -5
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +52 -35
- package/src/components/ui/finance/wallets/wallets-data-table.test.tsx +171 -0
- package/src/components/ui/finance/wallets/wallets-data-table.tsx +132 -29
- package/src/components/ui/finance/wallets/wallets-page.test.tsx +117 -36
- package/src/components/ui/finance/wallets/wallets-page.tsx +40 -64
- package/src/components/ui/storefront/accent-button.tsx +33 -0
- package/src/components/ui/storefront/cart-summary.tsx +140 -0
- package/src/components/ui/storefront/empty-listings.tsx +32 -0
- package/src/components/ui/storefront/hero-panel.tsx +70 -0
- package/src/components/ui/storefront/image-panel.tsx +40 -0
- package/src/components/ui/storefront/index.ts +12 -0
- package/src/components/ui/storefront/listing-card.tsx +129 -0
- package/src/components/ui/storefront/storefront-surface.test.tsx +85 -0
- package/src/components/ui/storefront/storefront-surface.tsx +235 -0
- package/src/components/ui/storefront/types.ts +99 -0
- package/src/components/ui/storefront/utils.ts +90 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-move.test.tsx +14 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +29 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +134 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +127 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +17 -42
- package/src/components/ui/tu-do/boards/boardId/timeline-board-open-task.test.tsx +164 -0
- package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +25 -16
- package/src/components/ui/tu-do/hooks/useTaskDialog.ts +15 -1
- package/src/components/ui/tu-do/my-tasks/__tests__/use-task-context-actions.test.ts +11 -0
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +2 -0
- package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +124 -7
- package/src/components/ui/tu-do/providers/__tests__/task-dialog-provider.test.tsx +217 -5
- package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +180 -35
- package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +222 -26
- package/src/components/ui/tu-do/shared/board-client.tsx +1 -3
- package/src/components/ui/tu-do/shared/list-view-context-menu.test.tsx +55 -2
- package/src/components/ui/tu-do/shared/list-view.tsx +23 -16
- package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +93 -76
- package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +11 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +268 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +243 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +26 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.test.tsx +129 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.tsx +358 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +1 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +6 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +36 -20
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.test.tsx +41 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.tsx +157 -102
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-form-reset.ts +18 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-realtime-sync.ts +1 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.test.ts +84 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +5 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +5 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +300 -172
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +959 -340
- package/src/components/ui/tu-do/shared/task-sound-effects.test.ts +189 -0
- package/src/components/ui/tu-do/shared/task-sound-effects.tsx +468 -0
- package/src/hooks/__tests__/use-task-actions.test.tsx +61 -0
- package/src/hooks/use-task-actions.ts +45 -0
- package/src/hooks/useBoardRealtime.ts +54 -1
- package/src/hooks/useBoardRealtimeEventHandler.ts +169 -4
- package/src/hooks/useTaskUserRealtime.ts +338 -0
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import type { ColumnDef } from '@tanstack/react-table';
|
|
4
4
|
import {
|
|
5
|
+
BookOpen,
|
|
5
6
|
Check,
|
|
6
7
|
CreditCard,
|
|
8
|
+
Scale,
|
|
7
9
|
TrendingDown,
|
|
8
10
|
TrendingUp,
|
|
11
|
+
TriangleAlert,
|
|
9
12
|
Wallet as WalletIcon,
|
|
10
13
|
X,
|
|
11
14
|
} from '@tuturuuu/icons';
|
|
@@ -19,13 +22,22 @@ import { convertCurrency } from '@tuturuuu/utils/exchange-rates';
|
|
|
19
22
|
import { cn, formatCurrency } from '@tuturuuu/utils/format';
|
|
20
23
|
import moment from 'moment';
|
|
21
24
|
import Link from 'next/link';
|
|
25
|
+
import { useTranslations } from 'next-intl';
|
|
26
|
+
import type { FocusEvent, ReactNode } from 'react';
|
|
27
|
+
import { useState } from 'react';
|
|
28
|
+
import type { FinanceBalanceMode } from '../shared/use-finance-balance-mode';
|
|
22
29
|
import {
|
|
23
30
|
FINANCE_HIDDEN_AMOUNT,
|
|
24
31
|
useFinanceConfidentialVisibility,
|
|
25
32
|
} from '../shared/use-finance-confidential-visibility';
|
|
33
|
+
import {
|
|
34
|
+
getWalletBalanceTone,
|
|
35
|
+
resolveWalletBalanceForMode,
|
|
36
|
+
} from '../shared/wallet-balance-mode';
|
|
26
37
|
import { WalletIconDisplay } from './wallet-icon-display';
|
|
27
38
|
|
|
28
39
|
interface WalletExtraData {
|
|
40
|
+
balanceMode?: FinanceBalanceMode;
|
|
29
41
|
canUpdateWallets?: boolean;
|
|
30
42
|
canDeleteWallets?: boolean;
|
|
31
43
|
currency?: string;
|
|
@@ -33,33 +45,138 @@ interface WalletExtraData {
|
|
|
33
45
|
isPersonalWorkspace?: boolean;
|
|
34
46
|
}
|
|
35
47
|
|
|
48
|
+
type AmountBadgeTone = ReturnType<typeof getWalletBalanceTone> | 'varied';
|
|
49
|
+
|
|
50
|
+
function getAmountBadgeClassName(tone: AmountBadgeTone) {
|
|
51
|
+
if (tone === 'varied') {
|
|
52
|
+
return 'border-dynamic-orange/40 bg-dynamic-orange/10 font-semibold text-dynamic-orange';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (tone === 'positive') {
|
|
56
|
+
return 'border-dynamic-green/30 bg-dynamic-green/10 font-semibold text-dynamic-green';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (tone === 'negative') {
|
|
60
|
+
return 'border-dynamic-red/30 bg-dynamic-red/10 font-semibold text-dynamic-red';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return 'font-semibold text-muted-foreground';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getContextAmountBadgeClassName(tone: 'ledger' | 'variance') {
|
|
67
|
+
if (tone === 'ledger') {
|
|
68
|
+
return 'border-dynamic-blue/30 bg-dynamic-blue/10 font-semibold text-dynamic-blue';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return 'border-dynamic-purple/30 bg-dynamic-purple/10 font-semibold text-dynamic-purple';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function AmountBadge({
|
|
75
|
+
children,
|
|
76
|
+
icon,
|
|
77
|
+
label,
|
|
78
|
+
tone,
|
|
79
|
+
}: {
|
|
80
|
+
children: ReactNode;
|
|
81
|
+
icon?: ReactNode;
|
|
82
|
+
label?: string;
|
|
83
|
+
tone: AmountBadgeTone;
|
|
84
|
+
}) {
|
|
85
|
+
return (
|
|
86
|
+
<Badge
|
|
87
|
+
variant="outline"
|
|
88
|
+
data-wallet-balance-badge={tone}
|
|
89
|
+
className={cn(
|
|
90
|
+
'flex w-fit items-center gap-1 whitespace-nowrap',
|
|
91
|
+
getAmountBadgeClassName(tone)
|
|
92
|
+
)}
|
|
93
|
+
>
|
|
94
|
+
{icon}
|
|
95
|
+
{label && <span className="font-medium opacity-75">{label}</span>}
|
|
96
|
+
<span>{children}</span>
|
|
97
|
+
</Badge>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ContextAmountBadge({
|
|
102
|
+
children,
|
|
103
|
+
icon,
|
|
104
|
+
label,
|
|
105
|
+
tone,
|
|
106
|
+
}: {
|
|
107
|
+
children: ReactNode;
|
|
108
|
+
icon?: ReactNode;
|
|
109
|
+
label: string;
|
|
110
|
+
tone: 'ledger' | 'variance';
|
|
111
|
+
}) {
|
|
112
|
+
return (
|
|
113
|
+
<Badge
|
|
114
|
+
variant="outline"
|
|
115
|
+
data-wallet-balance-context-badge={tone}
|
|
116
|
+
className={cn(
|
|
117
|
+
'flex w-fit items-center gap-1 whitespace-nowrap',
|
|
118
|
+
getContextAmountBadgeClassName(tone)
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{icon}
|
|
122
|
+
<span className="font-medium opacity-75">{label}</span>
|
|
123
|
+
<span>{children}</span>
|
|
124
|
+
</Badge>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
36
128
|
function WalletBalanceCell({
|
|
37
|
-
|
|
129
|
+
balanceMode,
|
|
130
|
+
wallet,
|
|
38
131
|
walletCurrency,
|
|
39
132
|
workspaceCurrency,
|
|
40
133
|
exchangeRates,
|
|
41
134
|
}: {
|
|
42
|
-
|
|
135
|
+
balanceMode: FinanceBalanceMode;
|
|
136
|
+
wallet: Wallet;
|
|
43
137
|
walletCurrency: string;
|
|
44
138
|
workspaceCurrency: string;
|
|
45
139
|
exchangeRates?: ExchangeRate[];
|
|
46
140
|
}) {
|
|
141
|
+
const t = useTranslations('wallet-checkpoints');
|
|
142
|
+
const [isAuditContextOpen, setIsAuditContextOpen] = useState(false);
|
|
47
143
|
const { isConfidential: areNumbersHidden } =
|
|
48
144
|
useFinanceConfidentialVisibility();
|
|
145
|
+
const {
|
|
146
|
+
auditStatus,
|
|
147
|
+
auditVariance,
|
|
148
|
+
contextBalance,
|
|
149
|
+
displayBalance,
|
|
150
|
+
hasAuditedBalance,
|
|
151
|
+
isAuditedMode,
|
|
152
|
+
} = resolveWalletBalanceForMode(wallet, balanceMode);
|
|
153
|
+
const contextLabel = isAuditedMode ? t('ledger') : t('audited');
|
|
154
|
+
const showAuditContext =
|
|
155
|
+
hasAuditedBalance &&
|
|
156
|
+
auditStatus &&
|
|
157
|
+
auditStatus !== 'clean' &&
|
|
158
|
+
auditStatus !== 'no_checkpoint' &&
|
|
159
|
+
auditVariance !== null &&
|
|
160
|
+
auditVariance !== 0;
|
|
49
161
|
|
|
50
|
-
const formattedBalance = formatCurrency(
|
|
51
|
-
|
|
52
|
-
|
|
162
|
+
const formattedBalance = formatCurrency(
|
|
163
|
+
displayBalance,
|
|
164
|
+
walletCurrency,
|
|
165
|
+
undefined,
|
|
166
|
+
{
|
|
167
|
+
signDisplay: 'auto',
|
|
168
|
+
}
|
|
169
|
+
);
|
|
53
170
|
|
|
54
171
|
let convertedText: string | null = null;
|
|
55
172
|
if (
|
|
56
173
|
walletCurrency !== workspaceCurrency &&
|
|
57
174
|
exchangeRates &&
|
|
58
175
|
exchangeRates.length > 0 &&
|
|
59
|
-
|
|
176
|
+
displayBalance !== 0
|
|
60
177
|
) {
|
|
61
178
|
const converted = convertCurrency(
|
|
62
|
-
|
|
179
|
+
displayBalance,
|
|
63
180
|
walletCurrency,
|
|
64
181
|
workspaceCurrency,
|
|
65
182
|
exchangeRates
|
|
@@ -82,51 +199,84 @@ function WalletBalanceCell({
|
|
|
82
199
|
);
|
|
83
200
|
}
|
|
84
201
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
202
|
+
const balanceTone = showAuditContext
|
|
203
|
+
? 'varied'
|
|
204
|
+
: getWalletBalanceTone(displayBalance);
|
|
205
|
+
|
|
206
|
+
function handleAuditContextBlur(event: FocusEvent<HTMLDivElement>) {
|
|
207
|
+
const nextTarget = event.relatedTarget as Node | null;
|
|
208
|
+
if (!nextTarget || !event.currentTarget.contains(nextTarget)) {
|
|
209
|
+
setIsAuditContextOpen(false);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
88
212
|
|
|
89
213
|
return (
|
|
90
214
|
<div className="flex flex-col gap-0.5">
|
|
91
|
-
<div className="flex items-center gap-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
215
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
216
|
+
<div
|
|
217
|
+
data-wallet-balance-trigger
|
|
218
|
+
className="relative w-fit rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
219
|
+
onBlur={handleAuditContextBlur}
|
|
220
|
+
onFocus={() => setIsAuditContextOpen(true)}
|
|
221
|
+
onMouseEnter={() => setIsAuditContextOpen(true)}
|
|
222
|
+
onMouseLeave={() => setIsAuditContextOpen(false)}
|
|
223
|
+
tabIndex={showAuditContext ? 0 : undefined}
|
|
224
|
+
>
|
|
225
|
+
<AmountBadge
|
|
226
|
+
tone={balanceTone}
|
|
227
|
+
icon={
|
|
228
|
+
balanceTone === 'positive' ? (
|
|
229
|
+
<TrendingUp className="h-3 w-3" />
|
|
230
|
+
) : balanceTone === 'negative' ? (
|
|
231
|
+
<TrendingDown className="h-3 w-3" />
|
|
232
|
+
) : balanceTone === 'varied' ? (
|
|
233
|
+
<TriangleAlert className="h-3 w-3" />
|
|
234
|
+
) : undefined
|
|
235
|
+
}
|
|
99
236
|
>
|
|
100
|
-
<TrendingUp className="h-3 w-3" />
|
|
101
237
|
{formattedBalance}
|
|
102
|
-
</
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
238
|
+
</AmountBadge>
|
|
239
|
+
{showAuditContext &&
|
|
240
|
+
isAuditContextOpen &&
|
|
241
|
+
typeof contextBalance === 'number' && (
|
|
242
|
+
<div className="absolute top-full left-0 z-20 mt-1 flex min-w-max flex-wrap items-center gap-1.5 rounded-md border bg-popover p-1.5 shadow-md">
|
|
243
|
+
<ContextAmountBadge
|
|
244
|
+
tone="ledger"
|
|
245
|
+
icon={<BookOpen className="h-3 w-3" />}
|
|
246
|
+
label={contextLabel}
|
|
247
|
+
>
|
|
248
|
+
{formatCurrency(contextBalance, walletCurrency, undefined, {
|
|
249
|
+
signDisplay: 'auto',
|
|
250
|
+
})}
|
|
251
|
+
</ContextAmountBadge>
|
|
252
|
+
<ContextAmountBadge
|
|
253
|
+
tone="variance"
|
|
254
|
+
icon={<Scale className="h-3 w-3" />}
|
|
255
|
+
label={t('variance')}
|
|
256
|
+
>
|
|
257
|
+
{formatCurrency(
|
|
258
|
+
auditVariance ?? 0,
|
|
259
|
+
walletCurrency,
|
|
260
|
+
undefined,
|
|
261
|
+
{
|
|
262
|
+
signDisplay: 'always',
|
|
263
|
+
}
|
|
264
|
+
)}
|
|
265
|
+
</ContextAmountBadge>
|
|
266
|
+
</div>
|
|
110
267
|
)}
|
|
111
|
-
|
|
112
|
-
<TrendingDown className="h-3 w-3" />
|
|
113
|
-
{formattedBalance}
|
|
114
|
-
</Badge>
|
|
115
|
-
)}
|
|
116
|
-
{isNeutral && (
|
|
117
|
-
<Badge
|
|
118
|
-
variant="outline"
|
|
119
|
-
className="font-semibold text-muted-foreground"
|
|
120
|
-
>
|
|
121
|
-
{formattedBalance}
|
|
122
|
-
</Badge>
|
|
123
|
-
)}
|
|
268
|
+
</div>
|
|
124
269
|
</div>
|
|
125
270
|
{convertedText && (
|
|
126
271
|
<span className="text-muted-foreground text-xs">
|
|
127
272
|
{'\u2248'} {convertedText}
|
|
128
273
|
</span>
|
|
129
274
|
)}
|
|
275
|
+
{isAuditedMode && auditStatus === 'no_checkpoint' && (
|
|
276
|
+
<span className="text-muted-foreground text-xs">
|
|
277
|
+
{t('no_checkpoint_short')}
|
|
278
|
+
</span>
|
|
279
|
+
)}
|
|
130
280
|
</div>
|
|
131
281
|
);
|
|
132
282
|
}
|
|
@@ -139,6 +289,7 @@ export const walletColumns = ({
|
|
|
139
289
|
extraData?: WalletExtraData;
|
|
140
290
|
}): ColumnDef<Wallet>[] => {
|
|
141
291
|
const workspaceCurrency = extraData?.currency || 'USD';
|
|
292
|
+
const balanceMode = extraData?.balanceMode ?? 'ledger';
|
|
142
293
|
|
|
143
294
|
return [
|
|
144
295
|
// {
|
|
@@ -213,7 +364,9 @@ export const walletColumns = ({
|
|
|
213
364
|
cell: ({ row }) => <div>{row.getValue('description') || '-'}</div>,
|
|
214
365
|
},
|
|
215
366
|
{
|
|
216
|
-
|
|
367
|
+
id: 'balance',
|
|
368
|
+
accessorFn: (wallet) =>
|
|
369
|
+
resolveWalletBalanceForMode(wallet, balanceMode).displayBalance,
|
|
217
370
|
header: ({ column }) => (
|
|
218
371
|
<DataTableColumnHeader
|
|
219
372
|
t={t}
|
|
@@ -222,11 +375,11 @@ export const walletColumns = ({
|
|
|
222
375
|
/>
|
|
223
376
|
),
|
|
224
377
|
cell: ({ row }) => {
|
|
225
|
-
const balance = Number(row.getValue('balance')) || 0;
|
|
226
378
|
const walletCurrency = row.original.currency || workspaceCurrency;
|
|
227
379
|
return (
|
|
228
380
|
<WalletBalanceCell
|
|
229
|
-
|
|
381
|
+
balanceMode={balanceMode}
|
|
382
|
+
wallet={row.original}
|
|
230
383
|
walletCurrency={walletCurrency}
|
|
231
384
|
workspaceCurrency={workspaceCurrency}
|
|
232
385
|
exchangeRates={extraData?.exchangeRates}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
3
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import type { WalletFormValues } from './form';
|
|
4
6
|
import { WalletForm } from './form';
|
|
5
7
|
|
|
6
8
|
vi.mock('@tuturuuu/ui/hooks/use-workspace-currency', () => ({
|
|
@@ -23,27 +25,47 @@ vi.mock('./wallet-icon-image-picker', () => ({
|
|
|
23
25
|
WalletIconImagePicker: () => null,
|
|
24
26
|
}));
|
|
25
27
|
|
|
26
|
-
function renderWalletForm(
|
|
28
|
+
function renderWalletForm(
|
|
29
|
+
options: {
|
|
30
|
+
data?: ComponentProps<typeof WalletForm>['data'];
|
|
31
|
+
defaultType?: WalletFormValues['type'];
|
|
32
|
+
} = {}
|
|
33
|
+
) {
|
|
34
|
+
const data =
|
|
35
|
+
'data' in options
|
|
36
|
+
? options.data
|
|
37
|
+
: ({
|
|
38
|
+
balance: 1234,
|
|
39
|
+
currency: 'USD',
|
|
40
|
+
id: 'wallet-1',
|
|
41
|
+
name: 'Primary',
|
|
42
|
+
type: 'STANDARD',
|
|
43
|
+
} as never);
|
|
44
|
+
const { defaultType } = options;
|
|
45
|
+
|
|
27
46
|
const queryClient = new QueryClient();
|
|
28
47
|
|
|
29
48
|
return render(
|
|
30
49
|
<QueryClientProvider client={queryClient}>
|
|
31
|
-
<WalletForm
|
|
32
|
-
wsId="ws-1"
|
|
33
|
-
data={
|
|
34
|
-
{
|
|
35
|
-
balance: 1234,
|
|
36
|
-
currency: 'USD',
|
|
37
|
-
id: 'wallet-1',
|
|
38
|
-
name: 'Primary',
|
|
39
|
-
type: 'STANDARD',
|
|
40
|
-
} as never
|
|
41
|
-
}
|
|
42
|
-
/>
|
|
50
|
+
<WalletForm wsId="ws-1" data={data} defaultType={defaultType} />
|
|
43
51
|
</QueryClientProvider>
|
|
44
52
|
);
|
|
45
53
|
}
|
|
46
54
|
|
|
55
|
+
function typeIntoCurrencyInput(input: HTMLInputElement, value: string) {
|
|
56
|
+
fireEvent.focus(input);
|
|
57
|
+
|
|
58
|
+
for (const character of value) {
|
|
59
|
+
const nextValue = `${input.value}${character}`;
|
|
60
|
+
fireEvent.change(input, {
|
|
61
|
+
target: {
|
|
62
|
+
selectionStart: nextValue.length,
|
|
63
|
+
value: nextValue,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
describe('WalletForm', () => {
|
|
48
70
|
beforeEach(() => {
|
|
49
71
|
vi.clearAllMocks();
|
|
@@ -75,4 +97,47 @@ describe('WalletForm', () => {
|
|
|
75
97
|
).toHaveValue('1,234')
|
|
76
98
|
);
|
|
77
99
|
});
|
|
100
|
+
|
|
101
|
+
it('opens credit-card create flows with credit defaults', () => {
|
|
102
|
+
renderWalletForm({
|
|
103
|
+
data: undefined,
|
|
104
|
+
defaultType: 'CREDIT',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
screen.getByText('wallet-data-table.wallet_type_credit_description')
|
|
109
|
+
).toBeInTheDocument();
|
|
110
|
+
expect(screen.getByLabelText('wallet-data-table.credit_limit')).toHaveValue(
|
|
111
|
+
''
|
|
112
|
+
);
|
|
113
|
+
expect(
|
|
114
|
+
screen.getByLabelText('wallet-data-table.statement_date')
|
|
115
|
+
).toHaveValue(1);
|
|
116
|
+
expect(screen.getByLabelText('wallet-data-table.payment_date')).toHaveValue(
|
|
117
|
+
15
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('keeps accepting large credit limits after locale grouping is inserted', () => {
|
|
122
|
+
renderWalletForm({
|
|
123
|
+
data: {
|
|
124
|
+
balance: 0,
|
|
125
|
+
currency: 'VND',
|
|
126
|
+
id: 'wallet-1',
|
|
127
|
+
limit: undefined,
|
|
128
|
+
name: 'Credit Wallet',
|
|
129
|
+
payment_date: 25,
|
|
130
|
+
statement_date: 10,
|
|
131
|
+
type: 'CREDIT',
|
|
132
|
+
} as never,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const creditLimitInput = screen.getByLabelText(
|
|
136
|
+
'wallet-data-table.credit_limit'
|
|
137
|
+
) as HTMLInputElement;
|
|
138
|
+
|
|
139
|
+
typeIntoCurrencyInput(creditLimitInput, '39999');
|
|
140
|
+
|
|
141
|
+
expect(creditLimitInput).toHaveValue('39.999');
|
|
142
|
+
});
|
|
78
143
|
});
|