@tuturuuu/ui 0.7.0 → 0.9.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 +88 -0
- package/biome.json +1 -1
- package/package.json +75 -73
- package/src/components/ui/accordion.tsx +1 -1
- package/src/components/ui/breadcrumb.tsx +1 -1
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
- package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
- package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
- package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
- package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
- package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/carousel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
- package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
- package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/color-picker.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/components/ui/context-menu.tsx +5 -1
- package/src/components/ui/currency-input.test.tsx +43 -0
- package/src/components/ui/currency-input.tsx +1 -1
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +19 -0
- package/src/components/ui/custom/combobox.test.tsx +195 -0
- package/src/components/ui/custom/combobox.tsx +273 -156
- package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
- package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
- package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
- package/src/components/ui/custom/settings-dialog-shell.tsx +2 -1
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-access/workspace-access-default-role-card.tsx +60 -35
- package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
- package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
- package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
- package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
- package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
- package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
- package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
- package/src/components/ui/custom/workspace-select.tsx +8 -3
- package/src/components/ui/dialog.test.tsx +52 -0
- package/src/components/ui/dialog.tsx +6 -2
- package/src/components/ui/dropdown-menu.tsx +5 -1
- package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
- package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
- package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
- package/src/components/ui/finance/debts/debts-page.tsx +15 -2
- package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
- package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
- package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
- package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/utils.ts +3 -1
- package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
- package/src/components/ui/finance/transactions/form-types.ts +3 -0
- package/src/components/ui/finance/transactions/form.tsx +2 -0
- package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
- package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
- package/src/components/ui/finance/transactions/transaction-card.tsx +21 -9
- package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
- package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
- package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
- package/src/components/ui/finance/wallets/form.test.tsx +51 -3
- package/src/components/ui/finance/wallets/form.tsx +15 -4
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
- package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
- package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
- package/src/components/ui/input-otp.tsx +1 -1
- package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
- package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
- package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
- package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
- package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
- package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
- package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
- package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
- package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
- package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
- package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
- package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
- package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
- package/src/components/ui/money-input.test.tsx +64 -0
- package/src/components/ui/money-input.tsx +63 -0
- package/src/components/ui/navigation-menu.tsx +1 -1
- package/src/components/ui/pagination.tsx +1 -1
- package/src/components/ui/radio-group.tsx +1 -1
- package/src/components/ui/select.tsx +5 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/sidebar.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +61 -0
- package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
- package/src/components/ui/storefront/cart-summary.tsx +104 -80
- package/src/components/ui/storefront/checkout-overlay.tsx +26 -0
- package/src/components/ui/storefront/hero-panel.tsx +2 -8
- package/src/components/ui/storefront/image-panel.tsx +6 -0
- package/src/components/ui/storefront/index.ts +11 -0
- package/src/components/ui/storefront/listing-card.tsx +84 -22
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +289 -0
- package/src/components/ui/storefront/product-dialog.tsx +72 -0
- package/src/components/ui/storefront/storefront-surface.test.tsx +221 -3
- package/src/components/ui/storefront/storefront-surface.tsx +288 -153
- package/src/components/ui/storefront/types.ts +27 -1
- package/src/components/ui/storefront/utils.ts +117 -27
- package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
- package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -0
- package/src/components/ui/text-editor/background-color-extension.ts +62 -0
- package/src/components/ui/text-editor/color-controls.tsx +284 -0
- package/src/components/ui/text-editor/content-migration.ts +41 -18
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +9 -3
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/image-extension.ts +40 -18
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/text-editor/video-extension.ts +11 -2
- package/src/components/ui/toast.tsx +1 -1
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
- package/src/components/ui/tu-do/boards/__tests__/workspace-projects-client-page.test.tsx +70 -1
- package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
- package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +127 -0
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +113 -46
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +51 -9
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +63 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +127 -38
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +410 -4
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +106 -14
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +186 -0
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +59 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
- package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
- package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-display.ts +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
- package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
- package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
- package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
- package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
- package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +237 -3
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
- package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +465 -937
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
- package/src/components/ui/tu-do/shared/board-views.tsx +596 -82
- package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
- package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
- package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
- package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
- package/src/components/ui/tu-do/shared/task-dialog-presentation.test.ts +53 -0
- package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +44 -15
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- package/src/declarations.d.ts +1 -0
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
- package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
- package/src/hooks/__tests__/useBoardRealtime.test.tsx +2 -2
- package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -0
- package/src/hooks/use-calendar-sync.tsx +247 -243
- package/src/hooks/use-calendar.tsx +323 -138
- package/src/hooks/use-task-actions.ts +24 -0
- package/src/hooks/use-user-workspace-config.ts +75 -0
- package/src/hooks/use-workspace-currency.ts +8 -3
- package/src/hooks/useBoardRealtime.ts +6 -3
- package/src/hooks/useBoardRealtime.types.ts +11 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
- package/src/hooks/useCursorTracking.ts +91 -27
- package/src/hooks/useTaskUserRealtime.ts +5 -3
|
@@ -85,12 +85,6 @@ export function TransactionCard({
|
|
|
85
85
|
useFinanceConfidentialVisibility();
|
|
86
86
|
const isTransfer = !!transaction.transfer;
|
|
87
87
|
|
|
88
|
-
// Check if transaction is confidential
|
|
89
|
-
const isConfidential =
|
|
90
|
-
transaction.is_amount_confidential ||
|
|
91
|
-
transaction.is_description_confidential ||
|
|
92
|
-
transaction.is_category_confidential;
|
|
93
|
-
|
|
94
88
|
// Get custom icon if available
|
|
95
89
|
const CategoryIcon = useMemo(() => {
|
|
96
90
|
if (transaction.category_icon) {
|
|
@@ -127,10 +121,17 @@ export function TransactionCard({
|
|
|
127
121
|
return wallets.find((w) => w.id === transaction.transfer?.linked_wallet_id);
|
|
128
122
|
}, [transaction.transfer?.linked_wallet_id, wallets]);
|
|
129
123
|
|
|
124
|
+
const linkedAmount = transaction.transfer?.linked_amount_redacted
|
|
125
|
+
? undefined
|
|
126
|
+
: transaction.transfer?.linked_amount;
|
|
127
|
+
const linkedAmountIsConfidential = Boolean(
|
|
128
|
+
transaction.transfer?.linked_is_amount_confidential
|
|
129
|
+
);
|
|
130
130
|
const transferDisplay = transaction.transfer
|
|
131
131
|
? transaction.transfer.is_origin
|
|
132
132
|
? {
|
|
133
133
|
amount: transaction.amount,
|
|
134
|
+
amountIsConfidential: Boolean(transaction.is_amount_confidential),
|
|
134
135
|
amountCurrency: transaction.wallet_currency,
|
|
135
136
|
destinationIcon: linkedWallet?.icon,
|
|
136
137
|
destinationImageSrc: linkedWallet?.image_src,
|
|
@@ -140,11 +141,15 @@ export function TransactionCard({
|
|
|
140
141
|
originImageSrc: wallet?.image_src,
|
|
141
142
|
originWalletId: transaction.wallet_id,
|
|
142
143
|
originWalletName: transaction.wallet,
|
|
143
|
-
secondaryAmount:
|
|
144
|
+
secondaryAmount: linkedAmount,
|
|
144
145
|
secondaryCurrency: transaction.transfer.linked_wallet_currency,
|
|
145
146
|
}
|
|
146
147
|
: {
|
|
147
|
-
amount:
|
|
148
|
+
amount: linkedAmount ?? transaction.amount,
|
|
149
|
+
amountIsConfidential:
|
|
150
|
+
linkedAmount != null
|
|
151
|
+
? linkedAmountIsConfidential
|
|
152
|
+
: Boolean(transaction.is_amount_confidential),
|
|
148
153
|
amountCurrency:
|
|
149
154
|
transaction.transfer.linked_wallet_currency ||
|
|
150
155
|
transaction.wallet_currency,
|
|
@@ -161,9 +166,16 @@ export function TransactionCard({
|
|
|
161
166
|
}
|
|
162
167
|
: null;
|
|
163
168
|
const displayAmount = transferDisplay?.amount ?? transaction.amount;
|
|
169
|
+
const displayAmountIsConfidential =
|
|
170
|
+
transferDisplay?.amountIsConfidential ??
|
|
171
|
+
Boolean(transaction.is_amount_confidential);
|
|
164
172
|
const effectiveCurrency =
|
|
165
173
|
transferDisplay?.amountCurrency || transaction.wallet_currency || currency;
|
|
166
174
|
const isExpense = (displayAmount || 0) < 0;
|
|
175
|
+
const isConfidential =
|
|
176
|
+
displayAmountIsConfidential ||
|
|
177
|
+
transaction.is_description_confidential ||
|
|
178
|
+
transaction.is_category_confidential;
|
|
167
179
|
|
|
168
180
|
// Currency conversion for foreign-currency transactions
|
|
169
181
|
const isForeignCurrency =
|
|
@@ -434,7 +446,7 @@ export function TransactionCard({
|
|
|
434
446
|
<div className="flex flex-col items-end">
|
|
435
447
|
<ConfidentialAmount
|
|
436
448
|
amount={displayAmount ?? null}
|
|
437
|
-
isConfidential={
|
|
449
|
+
isConfidential={displayAmountIsConfidential}
|
|
438
450
|
currency={effectiveCurrency}
|
|
439
451
|
className={cn(
|
|
440
452
|
'font-bold text-sm tabular-nums transition-all duration-200 sm:text-xl',
|
|
@@ -481,10 +481,7 @@ export function TransactionEditDialog({
|
|
|
481
481
|
return (
|
|
482
482
|
<>
|
|
483
483
|
<Dialog open={isOpen} onOpenChange={handleClose} modal={true}>
|
|
484
|
-
<DialogContent
|
|
485
|
-
showCloseButton={false}
|
|
486
|
-
className="data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 inset-0! top-0! left-0! flex h-screen max-h-screen w-screen max-w-none! translate-x-0! translate-y-0! gap-0 rounded-none! border-0 p-0"
|
|
487
|
-
>
|
|
484
|
+
<DialogContent showCloseButton={false} presentation="fullscreen">
|
|
488
485
|
{/* Main content area */}
|
|
489
486
|
<div className="flex min-w-0 flex-1 flex-col bg-background">
|
|
490
487
|
{/* Header with gradient */}
|
|
@@ -15,6 +15,7 @@ interface TransactionsCreateSummaryProps {
|
|
|
15
15
|
createDescription: string;
|
|
16
16
|
createTitle: string;
|
|
17
17
|
defaultOpen?: boolean;
|
|
18
|
+
defaultCurrency?: string;
|
|
18
19
|
description: string;
|
|
19
20
|
initialMode?: 'transaction' | 'transfer';
|
|
20
21
|
permissionRequestUser?: FinancePermissionRequestUser | null;
|
|
@@ -32,6 +33,7 @@ export function TransactionsCreateSummary({
|
|
|
32
33
|
createDescription,
|
|
33
34
|
createTitle,
|
|
34
35
|
defaultOpen = false,
|
|
36
|
+
defaultCurrency,
|
|
35
37
|
description,
|
|
36
38
|
initialMode = 'transaction',
|
|
37
39
|
permissionRequestUser,
|
|
@@ -59,6 +61,7 @@ export function TransactionsCreateSummary({
|
|
|
59
61
|
canCreateConfidentialTransactions
|
|
60
62
|
}
|
|
61
63
|
initialMode={initialMode}
|
|
64
|
+
defaultCurrency={defaultCurrency}
|
|
62
65
|
timezone={timezone}
|
|
63
66
|
permissionRequestUser={permissionRequestUser}
|
|
64
67
|
/>
|
|
@@ -7,6 +7,7 @@ import ExportDialogContent from '@tuturuuu/ui/finance/transactions/export-dialog
|
|
|
7
7
|
import { TransactionsCreateSummary } from '@tuturuuu/ui/finance/transactions/transactions-create-summary';
|
|
8
8
|
import { TransactionsInfinitePage } from '@tuturuuu/ui/finance/transactions/transactions-infinite-page';
|
|
9
9
|
import { Separator } from '@tuturuuu/ui/separator';
|
|
10
|
+
import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
|
|
10
11
|
import {
|
|
11
12
|
getPermissions,
|
|
12
13
|
getWorkspace,
|
|
@@ -51,6 +52,7 @@ export default async function TransactionsPage({
|
|
|
51
52
|
]);
|
|
52
53
|
if (!resolvedWorkspace || !resolvedPermissions) return notFound();
|
|
53
54
|
const { containsPermission } = resolvedPermissions;
|
|
55
|
+
const workspaceCurrency = resolveSupportedCurrency(resolvedCurrency);
|
|
54
56
|
|
|
55
57
|
const canViewTransactions = containsPermission('view_transactions');
|
|
56
58
|
const canExportFinanceData = containsPermission('export_finance_data');
|
|
@@ -110,13 +112,14 @@ export default async function TransactionsPage({
|
|
|
110
112
|
canChangeFinanceWallets={canChangeFinanceWallets}
|
|
111
113
|
canSetFinanceWalletsOnCreate={canSetFinanceWalletsOnCreate}
|
|
112
114
|
canCreateConfidentialTransactions={canCreateConfidentialTransactions}
|
|
115
|
+
defaultCurrency={workspaceCurrency}
|
|
113
116
|
timezone={resolvedWorkspace.timezone}
|
|
114
117
|
permissionRequestUser={permissionRequestUser}
|
|
115
118
|
/>
|
|
116
119
|
<Separator className="my-4" />
|
|
117
120
|
<TransactionsInfinitePage
|
|
118
121
|
wsId={wsId}
|
|
119
|
-
currency={
|
|
122
|
+
currency={workspaceCurrency}
|
|
120
123
|
timezone={resolvedWorkspace.timezone}
|
|
121
124
|
canExport={canExportFinanceData}
|
|
122
125
|
exportContent={
|
|
@@ -5,10 +5,27 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
5
5
|
import type { WalletFormValues } from './form';
|
|
6
6
|
import { WalletForm } from './form';
|
|
7
7
|
|
|
8
|
+
const mocks = vi.hoisted(() => ({
|
|
9
|
+
createWallet: vi.fn(),
|
|
10
|
+
updateWallet: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
8
13
|
vi.mock('@tuturuuu/ui/hooks/use-workspace-currency', () => ({
|
|
9
|
-
useWorkspaceCurrency: (
|
|
14
|
+
useWorkspaceCurrency: (_wsId: string, fallbackCurrency = 'USD') => ({
|
|
15
|
+
currency: fallbackCurrency,
|
|
16
|
+
}),
|
|
10
17
|
}));
|
|
11
18
|
|
|
19
|
+
vi.mock('@tuturuuu/internal-api/finance', async (importOriginal) => {
|
|
20
|
+
const actual =
|
|
21
|
+
await importOriginal<typeof import('@tuturuuu/internal-api/finance')>();
|
|
22
|
+
return {
|
|
23
|
+
...actual,
|
|
24
|
+
createWallet: mocks.createWallet,
|
|
25
|
+
updateWallet: mocks.updateWallet,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
12
29
|
vi.mock('next/navigation', () => ({
|
|
13
30
|
useRouter: () => ({ refresh: vi.fn() }),
|
|
14
31
|
}));
|
|
@@ -29,6 +46,7 @@ function renderWalletForm(
|
|
|
29
46
|
options: {
|
|
30
47
|
data?: ComponentProps<typeof WalletForm>['data'];
|
|
31
48
|
defaultType?: WalletFormValues['type'];
|
|
49
|
+
defaultCurrency?: string;
|
|
32
50
|
} = {}
|
|
33
51
|
) {
|
|
34
52
|
const data =
|
|
@@ -41,13 +59,18 @@ function renderWalletForm(
|
|
|
41
59
|
name: 'Primary',
|
|
42
60
|
type: 'STANDARD',
|
|
43
61
|
} as never);
|
|
44
|
-
const { defaultType } = options;
|
|
62
|
+
const { defaultCurrency, defaultType } = options;
|
|
45
63
|
|
|
46
64
|
const queryClient = new QueryClient();
|
|
47
65
|
|
|
48
66
|
return render(
|
|
49
67
|
<QueryClientProvider client={queryClient}>
|
|
50
|
-
<WalletForm
|
|
68
|
+
<WalletForm
|
|
69
|
+
wsId="ws-1"
|
|
70
|
+
data={data}
|
|
71
|
+
defaultType={defaultType}
|
|
72
|
+
defaultCurrency={defaultCurrency}
|
|
73
|
+
/>
|
|
51
74
|
</QueryClientProvider>
|
|
52
75
|
);
|
|
53
76
|
}
|
|
@@ -69,6 +92,8 @@ function typeIntoCurrencyInput(input: HTMLInputElement, value: string) {
|
|
|
69
92
|
describe('WalletForm', () => {
|
|
70
93
|
beforeEach(() => {
|
|
71
94
|
vi.clearAllMocks();
|
|
95
|
+
mocks.createWallet.mockResolvedValue({ message: 'success' });
|
|
96
|
+
mocks.updateWallet.mockResolvedValue({ message: 'success' });
|
|
72
97
|
// biome-ignore lint/suspicious/noDocumentCookie: test resets the finance visibility cookie.
|
|
73
98
|
document.cookie =
|
|
74
99
|
'finance-confidential-mode=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/';
|
|
@@ -118,6 +143,29 @@ describe('WalletForm', () => {
|
|
|
118
143
|
);
|
|
119
144
|
});
|
|
120
145
|
|
|
146
|
+
it('initializes new wallet currency from the supplied workspace default', async () => {
|
|
147
|
+
renderWalletForm({
|
|
148
|
+
data: undefined,
|
|
149
|
+
defaultCurrency: 'SGD',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
fireEvent.change(screen.getByLabelText('wallet-data-table.wallet_name'), {
|
|
153
|
+
target: { value: 'Singapore Cash' },
|
|
154
|
+
});
|
|
155
|
+
fireEvent.click(screen.getByRole('button', { name: 'ws-wallets.create' }));
|
|
156
|
+
|
|
157
|
+
await waitFor(() =>
|
|
158
|
+
expect(mocks.createWallet).toHaveBeenCalledWith(
|
|
159
|
+
'ws-1',
|
|
160
|
+
expect.objectContaining({
|
|
161
|
+
currency: 'SGD',
|
|
162
|
+
name: 'Singapore Cash',
|
|
163
|
+
type: 'STANDARD',
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
121
169
|
it('keeps accepting large credit limits after locale grouping is inserted', () => {
|
|
122
170
|
renderWalletForm({
|
|
123
171
|
data: {
|
|
@@ -20,6 +20,7 @@ import { Input } from '@tuturuuu/ui/input';
|
|
|
20
20
|
import { zodResolver } from '@tuturuuu/ui/resolvers';
|
|
21
21
|
import {
|
|
22
22
|
getCurrencyLocale,
|
|
23
|
+
resolveSupportedCurrency,
|
|
23
24
|
SUPPORTED_CURRENCIES,
|
|
24
25
|
} from '@tuturuuu/utils/currencies';
|
|
25
26
|
import { useRouter } from 'next/navigation';
|
|
@@ -97,6 +98,7 @@ interface Props {
|
|
|
97
98
|
wsId: string;
|
|
98
99
|
data?: Wallet;
|
|
99
100
|
defaultType?: WalletFormValues['type'];
|
|
101
|
+
defaultCurrency?: string;
|
|
100
102
|
onFinish?: (data: WalletFormValues) => void;
|
|
101
103
|
isPersonalWorkspace?: boolean;
|
|
102
104
|
}
|
|
@@ -105,11 +107,20 @@ export function WalletForm({
|
|
|
105
107
|
wsId,
|
|
106
108
|
data,
|
|
107
109
|
defaultType = 'STANDARD',
|
|
110
|
+
defaultCurrency,
|
|
108
111
|
onFinish,
|
|
109
112
|
isPersonalWorkspace,
|
|
110
113
|
}: Props) {
|
|
111
114
|
const t = useTranslations();
|
|
112
|
-
const
|
|
115
|
+
const fallbackCurrency = resolveSupportedCurrency(defaultCurrency);
|
|
116
|
+
const { currency: workspaceCurrency } = useWorkspaceCurrency(
|
|
117
|
+
wsId,
|
|
118
|
+
fallbackCurrency
|
|
119
|
+
);
|
|
120
|
+
const resolvedWorkspaceCurrency = resolveSupportedCurrency(
|
|
121
|
+
workspaceCurrency,
|
|
122
|
+
fallbackCurrency
|
|
123
|
+
);
|
|
113
124
|
const { isConfidential: areNumbersHidden } =
|
|
114
125
|
useFinanceConfidentialVisibility();
|
|
115
126
|
const queryClient = useQueryClient();
|
|
@@ -127,7 +138,7 @@ export function WalletForm({
|
|
|
127
138
|
description: data?.description || '',
|
|
128
139
|
balance: data?.balance || 0,
|
|
129
140
|
type: data?.type || defaultType,
|
|
130
|
-
currency: data?.currency ||
|
|
141
|
+
currency: data?.currency || resolvedWorkspaceCurrency,
|
|
131
142
|
icon: data?.icon || null,
|
|
132
143
|
image_src: data?.image_src || null,
|
|
133
144
|
limit: data?.limit ?? undefined,
|
|
@@ -193,7 +204,7 @@ export function WalletForm({
|
|
|
193
204
|
? ''
|
|
194
205
|
: new Intl.NumberFormat(
|
|
195
206
|
getCurrencyLocale(
|
|
196
|
-
walletCurrency ||
|
|
207
|
+
walletCurrency || resolvedWorkspaceCurrency
|
|
197
208
|
),
|
|
198
209
|
{
|
|
199
210
|
maximumFractionDigits: 2,
|
|
@@ -296,7 +307,7 @@ export function WalletForm({
|
|
|
296
307
|
<WalletCreditFields
|
|
297
308
|
form={form}
|
|
298
309
|
loading={loading}
|
|
299
|
-
currency={walletCurrency ||
|
|
310
|
+
currency={walletCurrency || resolvedWorkspaceCurrency}
|
|
300
311
|
/>
|
|
301
312
|
)}
|
|
302
313
|
|
|
@@ -35,6 +35,7 @@ interface WalletDetailsActionsProps {
|
|
|
35
35
|
canSetFinanceWalletsOnCreate?: boolean;
|
|
36
36
|
canDeleteWallets: boolean;
|
|
37
37
|
isPersonalWorkspace: boolean;
|
|
38
|
+
defaultCurrency?: string;
|
|
38
39
|
timezone?: string | null;
|
|
39
40
|
permissionRequestUser?: FinancePermissionRequestUser | null;
|
|
40
41
|
}
|
|
@@ -51,6 +52,7 @@ export function WalletDetailsActions({
|
|
|
51
52
|
canSetFinanceWalletsOnCreate,
|
|
52
53
|
canDeleteWallets,
|
|
53
54
|
isPersonalWorkspace,
|
|
55
|
+
defaultCurrency,
|
|
54
56
|
timezone,
|
|
55
57
|
permissionRequestUser,
|
|
56
58
|
}: WalletDetailsActionsProps) {
|
|
@@ -110,6 +112,7 @@ export function WalletDetailsActions({
|
|
|
110
112
|
<WalletForm
|
|
111
113
|
wsId={wsId}
|
|
112
114
|
data={wallet}
|
|
115
|
+
defaultCurrency={defaultCurrency}
|
|
113
116
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
114
117
|
/>
|
|
115
118
|
}
|
|
@@ -210,6 +213,7 @@ export function WalletDetailsActions({
|
|
|
210
213
|
canCreateConfidentialTransactions={
|
|
211
214
|
canCreateConfidentialTransactions
|
|
212
215
|
}
|
|
216
|
+
defaultCurrency={defaultCurrency}
|
|
213
217
|
timezone={timezone}
|
|
214
218
|
preferInitialWalletSelection={transactionAction !== 'payment'}
|
|
215
219
|
refreshPageOnFinish
|
|
@@ -11,6 +11,7 @@ import type { FinancePermissionRequestUser } from '@tuturuuu/ui/finance/shared/f
|
|
|
11
11
|
import { InfiniteTransactionsList } from '@tuturuuu/ui/finance/transactions/infinite-transactions-list';
|
|
12
12
|
import { Separator } from '@tuturuuu/ui/separator';
|
|
13
13
|
import { Skeleton } from '@tuturuuu/ui/skeleton';
|
|
14
|
+
import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
|
|
14
15
|
import type { ExchangeRate } from '@tuturuuu/utils/exchange-rates';
|
|
15
16
|
import { getCurrencyLocale } from '@tuturuuu/utils/format';
|
|
16
17
|
import {
|
|
@@ -117,8 +118,8 @@ export default async function WalletDetailsPage({
|
|
|
117
118
|
notFound();
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
const
|
|
121
|
-
const
|
|
121
|
+
const workspaceCurrency = resolveSupportedCurrency(resolvedDefaultCurrency);
|
|
122
|
+
const currency = wallet.currency || workspaceCurrency;
|
|
122
123
|
const initialAction = getInitialWalletAction(searchParams.action);
|
|
123
124
|
|
|
124
125
|
// Fetch exchange rates for conversion display
|
|
@@ -153,6 +154,7 @@ export default async function WalletDetailsPage({
|
|
|
153
154
|
canSetFinanceWalletsOnCreate={canSetFinanceWalletsOnCreate}
|
|
154
155
|
canDeleteWallets={canDeleteWallets}
|
|
155
156
|
isPersonalWorkspace={!!resolvedWorkspace.personal}
|
|
157
|
+
defaultCurrency={workspaceCurrency}
|
|
156
158
|
timezone={resolvedWorkspace.timezone}
|
|
157
159
|
permissionRequestUser={permissionRequestUser}
|
|
158
160
|
/>
|
|
@@ -6,6 +6,7 @@ import { WalletTotalCheckDialog } from '@tuturuuu/ui/finance/wallets/checkpoints
|
|
|
6
6
|
import { WalletForm } from '@tuturuuu/ui/finance/wallets/form';
|
|
7
7
|
import { WalletsDataTable } from '@tuturuuu/ui/finance/wallets/wallets-data-table';
|
|
8
8
|
import { Separator } from '@tuturuuu/ui/separator';
|
|
9
|
+
import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
|
|
9
10
|
import {
|
|
10
11
|
getPermissions,
|
|
11
12
|
getWorkspace,
|
|
@@ -51,6 +52,7 @@ export default async function WalletsPage({
|
|
|
51
52
|
]);
|
|
52
53
|
if (!resolvedPermissions || !resolvedWorkspace) notFound();
|
|
53
54
|
const { containsPermission } = resolvedPermissions;
|
|
55
|
+
const workspaceCurrency = resolveSupportedCurrency(resolvedCurrency);
|
|
54
56
|
|
|
55
57
|
const canCreateWallets = containsPermission('create_wallets');
|
|
56
58
|
const canUpdateWallets = containsPermission('update_wallets');
|
|
@@ -76,6 +78,7 @@ export default async function WalletsPage({
|
|
|
76
78
|
<WalletForm
|
|
77
79
|
wsId={wsId}
|
|
78
80
|
defaultType={isCreditCardCreate ? 'CREDIT' : 'STANDARD'}
|
|
81
|
+
defaultCurrency={workspaceCurrency}
|
|
79
82
|
/>
|
|
80
83
|
) : undefined
|
|
81
84
|
}
|
|
@@ -95,7 +98,7 @@ export default async function WalletsPage({
|
|
|
95
98
|
/>
|
|
96
99
|
<WalletTotalCheckDialog
|
|
97
100
|
wsId={wsId}
|
|
98
|
-
currency={
|
|
101
|
+
currency={workspaceCurrency}
|
|
99
102
|
canUpdateWallets={canUpdateWallets}
|
|
100
103
|
defaultOpen={searchParams.tool === 'all-wallet-check'}
|
|
101
104
|
/>
|
|
@@ -105,7 +108,7 @@ export default async function WalletsPage({
|
|
|
105
108
|
wsId={wsId}
|
|
106
109
|
canUpdateWallets={canUpdateWallets}
|
|
107
110
|
canDeleteWallets={canDeleteWallets}
|
|
108
|
-
currency={
|
|
111
|
+
currency={workspaceCurrency}
|
|
109
112
|
financePrefix={financePrefix}
|
|
110
113
|
isPersonalWorkspace={!!resolvedWorkspace?.personal}
|
|
111
114
|
query={searchParams.q}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { MinusIcon } from '@tuturuuu/icons';
|
|
3
|
+
import { MinusIcon } from '@tuturuuu/icons/lucide-static';
|
|
4
4
|
import { cn } from '@tuturuuu/utils/format';
|
|
5
5
|
import { OTPInput, OTPInputContext, type SlotProps } from 'input-otp';
|
|
6
6
|
import * as React from 'react';
|
|
@@ -9,9 +9,9 @@ import isBetween from 'dayjs/plugin/isBetween';
|
|
|
9
9
|
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
|
10
10
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
|
11
11
|
import timezone from 'dayjs/plugin/timezone';
|
|
12
|
-
import Image from 'next/image';
|
|
13
12
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
14
13
|
import { MIN_COLUMN_WIDTH } from './config';
|
|
14
|
+
import { CalendarEventProviderIcon } from './event-provider-display';
|
|
15
15
|
import { getLocationType, LocationTimeline } from './location-timeline';
|
|
16
16
|
import { useCalendarSettings } from './settings/settings-context';
|
|
17
17
|
|
|
@@ -68,18 +68,10 @@ interface DragState {
|
|
|
68
68
|
// 1. Extract EventContent component for shared rendering
|
|
69
69
|
const EventContent = ({ event }: { event: CalendarEvent }) => (
|
|
70
70
|
<>
|
|
71
|
-
|
|
72
|
-
event
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
alt="Google Calendar"
|
|
76
|
-
className="mr-1 inline-block h-[1.25em] w-[1.25em] align-middle opacity-80 dark:opacity-90"
|
|
77
|
-
title="Synced from Google Calendar"
|
|
78
|
-
data-testid="google-calendar-logo"
|
|
79
|
-
width={18}
|
|
80
|
-
height={18}
|
|
81
|
-
/>
|
|
82
|
-
)}
|
|
71
|
+
<CalendarEventProviderIcon
|
|
72
|
+
event={event}
|
|
73
|
+
className="mr-1 h-[1.25em] w-[1.25em] opacity-80 dark:opacity-90"
|
|
74
|
+
/>
|
|
83
75
|
<span className="truncate">{event.title}</span>
|
|
84
76
|
</>
|
|
85
77
|
);
|
|
@@ -342,27 +334,6 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
342
334
|
eventStart.isBefore(lastVisibleDate.add(1, 'day'), 'day') &&
|
|
343
335
|
eventEnd.isAfter(firstVisibleDate, 'day');
|
|
344
336
|
|
|
345
|
-
// Debug logging for multi-day events
|
|
346
|
-
const eventDurationDays = eventEnd.diff(eventStart, 'day');
|
|
347
|
-
if (eventDurationDays > 0) {
|
|
348
|
-
console.log('Multi-day event processing:', {
|
|
349
|
-
title: event.title,
|
|
350
|
-
eventStart: eventStart.format('YYYY-MM-DD'),
|
|
351
|
-
eventEnd: eventEnd.format('YYYY-MM-DD'),
|
|
352
|
-
durationDays: eventDurationDays,
|
|
353
|
-
isActuallyMultiDay: eventDurationDays > 1,
|
|
354
|
-
firstVisibleDate: firstVisibleDate.format('YYYY-MM-DD'),
|
|
355
|
-
lastVisibleDate: lastVisibleDate.format('YYYY-MM-DD'),
|
|
356
|
-
eventOverlaps,
|
|
357
|
-
wouldShowCutOffStart:
|
|
358
|
-
eventDurationDays > 1 &&
|
|
359
|
-
eventStart.isBefore(firstVisibleDate, 'day'),
|
|
360
|
-
wouldShowCutOffEnd:
|
|
361
|
-
eventDurationDays > 1 &&
|
|
362
|
-
eventEnd.isAfter(lastVisibleDate.add(1, 'day'), 'day'),
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
|
|
366
337
|
if (!eventOverlaps) {
|
|
367
338
|
return; // Skip this event if it doesn't overlap with visible dates
|
|
368
339
|
}
|
|
@@ -680,7 +651,13 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
680
651
|
};
|
|
681
652
|
|
|
682
653
|
// Calculate dynamic height based on visible events
|
|
683
|
-
const
|
|
654
|
+
const locationTopOffset =
|
|
655
|
+
locationSpans.length > 0 ? LOCATION_TIMELINE_HEIGHT_REM : 0;
|
|
656
|
+
const regularRowsHeight =
|
|
657
|
+
eventLayout.maxVisibleEventsPerDay > 0
|
|
658
|
+
? eventLayout.maxVisibleEventsPerDay * 1.75
|
|
659
|
+
: 0;
|
|
660
|
+
const barHeight = Math.max(1.9, locationTopOffset + regularRowsHeight);
|
|
684
661
|
|
|
685
662
|
// Enhanced mouse and touch handlers
|
|
686
663
|
const handleEventMouseDown = (e: React.MouseEvent, eventSpan: EventSpan) => {
|
|
@@ -839,7 +816,7 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
839
816
|
onClick={() => toggleDateExpansion(dateKey)}
|
|
840
817
|
style={{
|
|
841
818
|
position: 'absolute',
|
|
842
|
-
top: `${MAX_EVENTS_DISPLAY * 1.7}rem`,
|
|
819
|
+
top: `${locationTopOffset + MAX_EVENTS_DISPLAY * 1.7}rem`,
|
|
843
820
|
left: `${(dateIndex * 100) / visibleDates.length}%`,
|
|
844
821
|
width: `${100 / visibleDates.length}%`,
|
|
845
822
|
zIndex: 10,
|
|
@@ -858,7 +835,7 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
858
835
|
onClick={() => toggleDateExpansion(dateKey)}
|
|
859
836
|
style={{
|
|
860
837
|
position: 'absolute',
|
|
861
|
-
top: `${dateEvents.length * 1.7}rem`,
|
|
838
|
+
top: `${locationTopOffset + dateEvents.length * 1.7}rem`,
|
|
862
839
|
left: `${(dateIndex * 100) / visibleDates.length}%`,
|
|
863
840
|
width: `${100 / visibleDates.length}%`,
|
|
864
841
|
zIndex: 10,
|
|
@@ -941,8 +918,18 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
941
918
|
dragState.isDragging && dragState.draggedEvent?.id === event.id;
|
|
942
919
|
|
|
943
920
|
// Calculate top offset based on location strip presence
|
|
944
|
-
const topOffset =
|
|
945
|
-
|
|
921
|
+
const topOffset = locationTopOffset;
|
|
922
|
+
const optimisticStatus = (
|
|
923
|
+
event as CalendarEvent & {
|
|
924
|
+
_optimisticStatus?:
|
|
925
|
+
| 'creating'
|
|
926
|
+
| 'updating'
|
|
927
|
+
| 'deleting'
|
|
928
|
+
| 'error';
|
|
929
|
+
}
|
|
930
|
+
)._optimisticStatus;
|
|
931
|
+
const isPendingMutation =
|
|
932
|
+
optimisticStatus === 'updating' || optimisticStatus === 'deleting';
|
|
946
933
|
|
|
947
934
|
return (
|
|
948
935
|
<div
|
|
@@ -959,6 +946,8 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
|
|
|
959
946
|
bg,
|
|
960
947
|
border,
|
|
961
948
|
text,
|
|
949
|
+
isPendingMutation &&
|
|
950
|
+
'opacity-60 outline outline-dashed outline-1 outline-primary/50',
|
|
962
951
|
// Special styling for cut-off events
|
|
963
952
|
(isCutOffStart || isCutOffEnd) && 'border-dashed'
|
|
964
953
|
)}
|
|
@@ -880,6 +880,8 @@ export const CalendarCell = ({ date, hour }: CalendarCellProps) => {
|
|
|
880
880
|
height: `${HOUR_HEIGHT}px`,
|
|
881
881
|
}}
|
|
882
882
|
onContextMenu={(e) => {
|
|
883
|
+
const target = e.target as HTMLElement | null;
|
|
884
|
+
if (target?.closest('[data-slot="context-menu-trigger"]')) return;
|
|
883
885
|
e.preventDefault();
|
|
884
886
|
}}
|
|
885
887
|
onMouseEnter={() => setIsHovering(true)}
|
|
@@ -9,6 +9,7 @@ import { cn } from '@tuturuuu/utils/format';
|
|
|
9
9
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
10
10
|
import { AgendaView } from './agenda-view';
|
|
11
11
|
import { CalendarHeader } from './calendar-header';
|
|
12
|
+
import { CalendarLoadingSkeleton } from './calendar-loading-skeleton';
|
|
12
13
|
import { CalendarViewWithTrail } from './calendar-view-with-trail';
|
|
13
14
|
import { EventModal } from './event-modal';
|
|
14
15
|
import { EventPreviewPopover } from './event-preview-popover';
|
|
@@ -102,6 +103,7 @@ export const CalendarContent = ({
|
|
|
102
103
|
externalState,
|
|
103
104
|
extras,
|
|
104
105
|
overlay,
|
|
106
|
+
disableBuiltInEventUi,
|
|
105
107
|
}: {
|
|
106
108
|
t: any;
|
|
107
109
|
locale: string;
|
|
@@ -118,10 +120,11 @@ export const CalendarContent = ({
|
|
|
118
120
|
};
|
|
119
121
|
extras?: React.ReactNode;
|
|
120
122
|
overlay?: React.ReactNode;
|
|
123
|
+
disableBuiltInEventUi?: boolean;
|
|
121
124
|
}) => {
|
|
122
125
|
const { transition } = useViewTransition();
|
|
123
126
|
const { settings } = useCalendarSettings();
|
|
124
|
-
const { dates, setDates } = useCalendarSync();
|
|
127
|
+
const { dates, isLoading, setDates } = useCalendarSync();
|
|
125
128
|
|
|
126
129
|
// Use ref to always have the latest settings without causing dependency cascades
|
|
127
130
|
const settingsRef = useRef(settings);
|
|
@@ -191,7 +194,6 @@ export const CalendarContent = ({
|
|
|
191
194
|
handleSetView('4-days');
|
|
192
195
|
setDates(dates);
|
|
193
196
|
});
|
|
194
|
-
console.log('enable4DayView', dates);
|
|
195
197
|
}, [date, transition, handleSetView, setDates]);
|
|
196
198
|
|
|
197
199
|
const enableWeekView = useCallback(() => {
|
|
@@ -578,7 +580,7 @@ export const CalendarContent = ({
|
|
|
578
580
|
return (
|
|
579
581
|
<div
|
|
580
582
|
className={cn(
|
|
581
|
-
'grid h-full w-full',
|
|
583
|
+
'grid h-full min-h-0 w-full',
|
|
582
584
|
view === 'month' || view === 'year'
|
|
583
585
|
? 'grid-rows-[auto_1fr]'
|
|
584
586
|
: 'grid-rows-[auto_auto_1fr]'
|
|
@@ -621,7 +623,7 @@ export const CalendarContent = ({
|
|
|
621
623
|
|
|
622
624
|
<div
|
|
623
625
|
className={cn(
|
|
624
|
-
'scrollbar-none relative flex-1 overflow-auto rounded-lg focus:outline-none',
|
|
626
|
+
'scrollbar-none relative min-h-0 flex-1 overflow-auto rounded-lg focus:outline-none',
|
|
625
627
|
view === 'agenda' ||
|
|
626
628
|
view === 'month' ||
|
|
627
629
|
view === 'year' ||
|
|
@@ -632,7 +634,9 @@ export const CalendarContent = ({
|
|
|
632
634
|
e.currentTarget.focus();
|
|
633
635
|
}}
|
|
634
636
|
>
|
|
635
|
-
{
|
|
637
|
+
{isLoading ? (
|
|
638
|
+
<CalendarLoadingSkeleton dates={dates} view={view} />
|
|
639
|
+
) : view === 'month' && dates?.[0] ? (
|
|
636
640
|
<MonthCalendar
|
|
637
641
|
date={dates[0]}
|
|
638
642
|
workspace={workspace}
|
|
@@ -665,7 +669,7 @@ export const CalendarContent = ({
|
|
|
665
669
|
)}
|
|
666
670
|
</div>
|
|
667
671
|
|
|
668
|
-
{disabled
|
|
672
|
+
{disabled || disableBuiltInEventUi
|
|
669
673
|
? null
|
|
670
674
|
: workspace && (
|
|
671
675
|
<>
|