@tuturuuu/ui 0.8.0 → 0.10.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 +69 -0
- package/biome.json +1 -1
- package/package.json +74 -71
- 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/custom/__tests__/settings-dialog-search.test.ts +78 -0
- package/src/components/ui/custom/__tests__/settings-dialog-shell-compile-graph.test.ts +76 -0
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +46 -1
- 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/nav-link.test.tsx +165 -0
- package/src/components/ui/custom/nav-link.tsx +69 -11
- package/src/components/ui/custom/navigation.tsx +1 -0
- package/src/components/ui/custom/settings/task-settings.tsx +104 -0
- package/src/components/ui/custom/settings-dialog-search-loader.d.ts +5 -0
- package/src/components/ui/custom/settings-dialog-search-loader.js +3 -0
- package/src/components/ui/custom/settings-dialog-search.ts +75 -0
- package/src/components/ui/custom/settings-dialog-shell.tsx +65 -28
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
- package/src/components/ui/custom/workspace-select.tsx +25 -19
- 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 +1 -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-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/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 +93 -154
- package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
- package/src/components/ui/storefront/listing-card.tsx +1 -1
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +1 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
- package/src/components/ui/storefront/storefront-surface.tsx +101 -166
- package/src/components/ui/storefront/types.ts +4 -0
- package/src/components/ui/storefront/utils.ts +6 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -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/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +8 -2
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/toast.tsx +1 -1
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +286 -0
- package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -0
- 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 +15 -226
- package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +351 -0
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +121 -39
- package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +7 -0
- 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/bulk/bulk-operation-types.ts +3 -3
- 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/data/use-bulk-resources.ts +59 -5
- 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/drag-preview.tsx +20 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -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.ts +81 -30
- 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 +642 -5
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +224 -15
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +535 -53
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +101 -33
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +235 -113
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +50 -5
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +12 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +10 -1
- 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-open-options.test.ts +20 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +10 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +271 -36
- 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 +22 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +35 -3
- package/src/components/ui/tu-do/boards/form.tsx +1 -1
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- 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/hooks/__tests__/useTaskLabelManagement.test.tsx +48 -0
- package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +144 -0
- package/src/components/ui/tu-do/hooks/useTaskDialog.ts +7 -0
- package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +115 -106
- package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +115 -122
- 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/progress/task-progress-import-panel.tsx +60 -0
- package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +156 -0
- package/src/components/ui/tu-do/progress/task-progress-page.tsx +348 -0
- package/src/components/ui/tu-do/progress/task-progress-panels.tsx +301 -0
- package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +26 -0
- package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +81 -10
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +141 -1
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +377 -36
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +374 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +419 -5
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +38 -0
- package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
- package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +3 -0
- package/src/components/ui/tu-do/shared/assignee-select.tsx +77 -26
- package/src/components/ui/tu-do/shared/board-client.tsx +15 -10
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +471 -975
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +244 -220
- package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
- package/src/components/ui/tu-do/shared/board-views.tsx +577 -85
- package/src/components/ui/tu-do/shared/list-view.tsx +246 -2
- 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-cache-patches.ts +394 -0
- package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +21 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +5 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +25 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +7 -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/hooks/use-task-data.ts +79 -10
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +76 -77
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +63 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +78 -69
- package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +28 -8
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +14 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +8 -0
- 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-dialog-actions.tsx +8 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +150 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +61 -35
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +44 -2
- 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 +11 -1
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +11 -0
- package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +2 -0
- 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__/useBoardPresence.test.tsx +191 -0
- package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
- 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/useBoardPresence.ts +364 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +45 -90
- package/src/lib/workspace-actions.ts +2 -6
|
@@ -1,216 +1,155 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ArrowRight
|
|
3
|
+
import { ArrowRight } from '@tuturuuu/icons';
|
|
4
4
|
import type { InventoryStorefront } from '@tuturuuu/internal-api/inventory';
|
|
5
5
|
import { cn } from '@tuturuuu/utils/format';
|
|
6
6
|
import type { FormEvent } from 'react';
|
|
7
7
|
import { Badge } from '../badge';
|
|
8
|
-
import { Button } from '../button';
|
|
9
8
|
import { AccentButton } from './accent-button';
|
|
10
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
CartActions,
|
|
11
|
+
CartContents,
|
|
12
|
+
CheckoutContactFields,
|
|
13
|
+
CheckoutSection,
|
|
14
|
+
} from './cart-summary-parts';
|
|
11
15
|
import type {
|
|
12
16
|
StorefrontBuyerDefaults,
|
|
13
17
|
StorefrontCartEntry,
|
|
14
18
|
StorefrontSurfaceLabels,
|
|
15
19
|
} from './types';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
getStorefrontVariantLabel,
|
|
20
|
-
storefrontCartLineKey,
|
|
21
|
-
storefrontSurfaceClasses,
|
|
22
|
-
} from './utils';
|
|
20
|
+
import { formatStorefrontPrice, storefrontSurfaceClasses } from './utils';
|
|
21
|
+
|
|
22
|
+
type StorefrontCartSummaryVariant = 'checkout' | 'panel' | 'popover';
|
|
23
23
|
|
|
24
24
|
export function StorefrontCartSummary({
|
|
25
25
|
buyerDefaults,
|
|
26
26
|
cartEntries,
|
|
27
27
|
checkoutHref,
|
|
28
|
+
className,
|
|
28
29
|
currency,
|
|
29
30
|
isCheckout,
|
|
30
31
|
isPreview,
|
|
31
32
|
isSubmitting,
|
|
32
33
|
labels,
|
|
34
|
+
onCheckoutOpen,
|
|
33
35
|
onCheckoutSubmit,
|
|
34
36
|
onInstantCheckout,
|
|
35
37
|
radius,
|
|
36
38
|
storefront,
|
|
37
39
|
total,
|
|
40
|
+
variant,
|
|
38
41
|
}: {
|
|
39
42
|
buyerDefaults?: StorefrontBuyerDefaults;
|
|
40
43
|
cartEntries: StorefrontCartEntry[];
|
|
41
44
|
checkoutHref?: string;
|
|
45
|
+
className?: string;
|
|
42
46
|
currency: string;
|
|
43
|
-
isCheckout
|
|
47
|
+
isCheckout?: boolean;
|
|
44
48
|
isPreview: boolean;
|
|
45
49
|
isSubmitting: boolean;
|
|
46
50
|
labels: StorefrontSurfaceLabels;
|
|
51
|
+
onCheckoutOpen?: () => void;
|
|
47
52
|
onCheckoutSubmit?: (formData: FormData) => void;
|
|
48
53
|
onInstantCheckout?: () => void;
|
|
49
54
|
radius: string;
|
|
50
55
|
storefront: InventoryStorefront;
|
|
51
56
|
total: number;
|
|
57
|
+
variant?: StorefrontCartSummaryVariant;
|
|
52
58
|
}) {
|
|
59
|
+
const presentation = variant ?? (isCheckout ? 'checkout' : 'panel');
|
|
53
60
|
const hasCart = cartEntries.length > 0;
|
|
54
61
|
const isCheckoutDisabled = storefront.checkoutMode === 'disabled';
|
|
55
62
|
const submitDisabled =
|
|
56
63
|
!hasCart || isSubmitting || isCheckoutDisabled || !onCheckoutSubmit;
|
|
57
|
-
const canOpenCheckout = hasCart && Boolean(checkoutHref);
|
|
58
|
-
const buyerEmail = buyerDefaults?.email?.trim() || undefined;
|
|
59
|
-
const buyerName = buyerDefaults?.name?.trim() || undefined;
|
|
60
|
-
const inputClassName =
|
|
61
|
-
'h-11 rounded-md border border-input bg-background px-3 text-sm outline-none transition focus-visible:ring-2 focus-visible:ring-ring/40';
|
|
62
|
-
const labelClassName = 'grid gap-1.5 text-sm';
|
|
64
|
+
const canOpenCheckout = hasCart && Boolean(checkoutHref || onCheckoutOpen);
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
radius
|
|
71
|
-
)}
|
|
72
|
-
>
|
|
73
|
-
<div className="flex items-center justify-between gap-3">
|
|
74
|
-
<p className="font-semibold">{labels.cart}</p>
|
|
75
|
-
<Badge className="border-border bg-background" variant="outline">
|
|
76
|
-
{cartEntries.length}
|
|
77
|
-
</Badge>
|
|
78
|
-
</div>
|
|
79
|
-
<p className="mt-2 text-muted-foreground text-sm leading-6">
|
|
80
|
-
{labels.reservedCopy}
|
|
81
|
-
</p>
|
|
82
|
-
<div className="mt-4 -mr-1 grid max-h-72 gap-2.5 overflow-y-auto pr-1">
|
|
83
|
-
{cartEntries.map(({ line, listing, variant }) => {
|
|
84
|
-
const unitPrice = getStorefrontLinePrice(listing, variant);
|
|
85
|
-
const variantLabel = variant
|
|
86
|
-
? getStorefrontVariantLabel(variant)
|
|
87
|
-
: null;
|
|
88
|
-
return (
|
|
89
|
-
<div
|
|
90
|
-
className="flex items-center gap-3 text-sm"
|
|
91
|
-
key={storefrontCartLineKey(line.listingId, line.variantId)}
|
|
92
|
-
>
|
|
93
|
-
<StorefrontImagePanel
|
|
94
|
-
className={cn('size-10 shrink-0 rounded-md', radius)}
|
|
95
|
-
imageUrl={variant?.imageUrl ?? listing.imageUrl}
|
|
96
|
-
label={listing.title}
|
|
97
|
-
/>
|
|
98
|
-
<div className="min-w-0 flex-1">
|
|
99
|
-
<p className="truncate font-medium">{listing.title}</p>
|
|
100
|
-
{variantLabel ? (
|
|
101
|
-
<p className="truncate text-muted-foreground text-xs">
|
|
102
|
-
{variantLabel}
|
|
103
|
-
</p>
|
|
104
|
-
) : null}
|
|
105
|
-
<p className="truncate text-muted-foreground text-xs tabular-nums">
|
|
106
|
-
{line.quantity} × {formatStorefrontPrice(unitPrice, currency)}
|
|
107
|
-
</p>
|
|
108
|
-
</div>
|
|
109
|
-
<span className="shrink-0 whitespace-nowrap font-medium tabular-nums">
|
|
110
|
-
{formatStorefrontPrice(unitPrice * line.quantity, currency)}
|
|
111
|
-
</span>
|
|
112
|
-
</div>
|
|
113
|
-
);
|
|
114
|
-
})}
|
|
115
|
-
</div>
|
|
116
|
-
<div className="mt-4 flex items-center justify-between gap-2 border-border border-t pt-4">
|
|
117
|
-
<span className="text-muted-foreground text-sm">{labels.total}</span>
|
|
118
|
-
<span className="shrink-0 whitespace-nowrap font-semibold tabular-nums">
|
|
119
|
-
{formatStorefrontPrice(total, currency)}
|
|
120
|
-
</span>
|
|
121
|
-
</div>
|
|
122
|
-
{hasCart && !isCheckoutDisabled ? (
|
|
123
|
-
<p className="mt-3 flex items-center gap-2 rounded-md border border-border border-dashed bg-muted/30 px-3 py-2 text-muted-foreground text-xs leading-5">
|
|
124
|
-
<Tag className="h-3.5 w-3.5 shrink-0" />
|
|
125
|
-
{labels.couponNote}
|
|
126
|
-
</p>
|
|
127
|
-
) : null}
|
|
128
|
-
{!hasCart ? (
|
|
129
|
-
<p className="mt-4 flex items-center gap-2 rounded-md border border-border bg-muted/40 px-3 py-2 text-muted-foreground text-sm">
|
|
130
|
-
<TriangleAlert className="h-4 w-4" />
|
|
131
|
-
{labels.emptyCart}
|
|
132
|
-
</p>
|
|
133
|
-
) : null}
|
|
134
|
-
{isCheckout ? (
|
|
66
|
+
if (presentation === 'checkout') {
|
|
67
|
+
return (
|
|
68
|
+
<section
|
|
69
|
+
aria-label={labels.checkout}
|
|
70
|
+
className={cn('grid gap-5', className)}
|
|
71
|
+
>
|
|
135
72
|
<form
|
|
136
|
-
className="
|
|
73
|
+
className="grid gap-5"
|
|
137
74
|
onSubmit={(event: FormEvent<HTMLFormElement>) => {
|
|
138
75
|
event.preventDefault();
|
|
139
76
|
onCheckoutSubmit?.(new FormData(event.currentTarget));
|
|
140
77
|
}}
|
|
141
78
|
>
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<span className="font-medium text-xs">{labels.form.email}</span>
|
|
155
|
-
<input
|
|
156
|
-
autoComplete="email"
|
|
157
|
-
className={inputClassName}
|
|
158
|
-
defaultValue={buyerEmail}
|
|
159
|
-
name="customerEmail"
|
|
160
|
-
placeholder={labels.form.email}
|
|
161
|
-
required
|
|
162
|
-
type="email"
|
|
79
|
+
<CheckoutSection
|
|
80
|
+
defaultOpen={cartEntries.length > 1}
|
|
81
|
+
meta={formatStorefrontPrice(total, currency)}
|
|
82
|
+
title={labels.orderSummary}
|
|
83
|
+
>
|
|
84
|
+
<CartContents
|
|
85
|
+
cartEntries={cartEntries}
|
|
86
|
+
currency={currency}
|
|
87
|
+
hasCart={hasCart}
|
|
88
|
+
isCheckoutDisabled={isCheckoutDisabled}
|
|
89
|
+
labels={labels}
|
|
90
|
+
total={total}
|
|
163
91
|
/>
|
|
164
|
-
</
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
name="customerPhone"
|
|
171
|
-
placeholder={labels.form.phone}
|
|
172
|
-
type="tel"
|
|
92
|
+
</CheckoutSection>
|
|
93
|
+
|
|
94
|
+
<CheckoutSection defaultOpen title={labels.contactDetails}>
|
|
95
|
+
<CheckoutContactFields
|
|
96
|
+
buyerDefaults={buyerDefaults}
|
|
97
|
+
labels={labels}
|
|
173
98
|
/>
|
|
174
|
-
</
|
|
175
|
-
|
|
176
|
-
className="min-h-24 rounded-md border border-input bg-background px-3 py-2.5 text-sm outline-none transition focus-visible:ring-2 focus-visible:ring-ring/40"
|
|
177
|
-
name="note"
|
|
178
|
-
placeholder={labels.form.note}
|
|
179
|
-
/>
|
|
99
|
+
</CheckoutSection>
|
|
100
|
+
|
|
180
101
|
<AccentButton disabled={submitDisabled} radius={radius}>
|
|
181
102
|
{isSubmitting ? labels.reserving : labels.reserve}
|
|
182
103
|
<ArrowRight className="size-4 shrink-0" />
|
|
183
104
|
</AccentButton>
|
|
184
105
|
</form>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
{isSubmitting ? labels.reserving : labels.instantCheckout}
|
|
199
|
-
</AccentButton>
|
|
200
|
-
) : null}
|
|
201
|
-
<Button asChild className={cn('w-full', radius)} variant="outline">
|
|
202
|
-
<a href={checkoutHref}>
|
|
203
|
-
{labels.checkout}
|
|
204
|
-
<ArrowRight className="size-4 shrink-0" />
|
|
205
|
-
</a>
|
|
206
|
-
</Button>
|
|
207
|
-
</div>
|
|
208
|
-
) : (
|
|
209
|
-
<Button className={cn('mt-4 w-full', radius)} disabled type="button">
|
|
210
|
-
{labels.checkout}
|
|
211
|
-
<ArrowRight className="size-4 shrink-0" />
|
|
212
|
-
</Button>
|
|
106
|
+
</section>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<section
|
|
112
|
+
aria-label={labels.cart}
|
|
113
|
+
className={cn(
|
|
114
|
+
'h-fit',
|
|
115
|
+
presentation === 'panel'
|
|
116
|
+
? cn('p-4', storefrontSurfaceClasses[storefront.surfaceStyle], radius)
|
|
117
|
+
: 'grid gap-4',
|
|
118
|
+
className
|
|
213
119
|
)}
|
|
214
|
-
|
|
120
|
+
>
|
|
121
|
+
<div>
|
|
122
|
+
<div className="flex items-center justify-between gap-3">
|
|
123
|
+
<p className="font-semibold">{labels.cart}</p>
|
|
124
|
+
<Badge className="border-border bg-background" variant="outline">
|
|
125
|
+
{cartEntries.length}
|
|
126
|
+
</Badge>
|
|
127
|
+
</div>
|
|
128
|
+
<p className="mt-2 text-muted-foreground text-sm leading-6">
|
|
129
|
+
{labels.reservedCopy}
|
|
130
|
+
</p>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<CartContents
|
|
134
|
+
cartEntries={cartEntries}
|
|
135
|
+
currency={currency}
|
|
136
|
+
hasCart={hasCart}
|
|
137
|
+
isCheckoutDisabled={isCheckoutDisabled}
|
|
138
|
+
labels={labels}
|
|
139
|
+
total={total}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<CartActions
|
|
143
|
+
canOpenCheckout={canOpenCheckout}
|
|
144
|
+
checkoutHref={checkoutHref}
|
|
145
|
+
isCheckoutDisabled={isCheckoutDisabled}
|
|
146
|
+
isPreview={isPreview}
|
|
147
|
+
isSubmitting={isSubmitting}
|
|
148
|
+
labels={labels}
|
|
149
|
+
onCheckoutOpen={onCheckoutOpen}
|
|
150
|
+
onInstantCheckout={onInstantCheckout}
|
|
151
|
+
radius={radius}
|
|
152
|
+
/>
|
|
153
|
+
</section>
|
|
215
154
|
);
|
|
216
155
|
}
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
import { Loader2 } from '@tuturuuu/icons';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Full-screen blocking overlay shown while
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* sees progress instead of a frozen button.
|
|
6
|
+
* Full-screen blocking overlay shown while the checkout session or embedded
|
|
7
|
+
* payment intent is opening, so the buyer sees progress instead of a frozen
|
|
8
|
+
* button.
|
|
10
9
|
*/
|
|
11
10
|
export function StorefrontCheckoutOverlay({ label }: { label: string }) {
|
|
12
11
|
return (
|
|
@@ -17,7 +16,7 @@ export function StorefrontCheckoutOverlay({ label }: { label: string }) {
|
|
|
17
16
|
role="status"
|
|
18
17
|
>
|
|
19
18
|
<div className="flex flex-col items-center gap-4 px-6 text-center">
|
|
20
|
-
<span className="grid size-14 place-items-center rounded-full bg-[var(--storefront-accent,var(--
|
|
19
|
+
<span className="grid size-14 place-items-center rounded-full bg-[var(--storefront-accent-soft,var(--muted))] text-[var(--storefront-accent-text,var(--primary))]">
|
|
21
20
|
<Loader2 className="size-7 animate-spin" />
|
|
22
21
|
</span>
|
|
23
22
|
<p className="font-medium text-sm">{label}</p>
|
|
@@ -92,7 +92,7 @@ export function StorefrontListingCard({
|
|
|
92
92
|
<div className="min-w-0">
|
|
93
93
|
<div className="flex flex-wrap items-center gap-2">
|
|
94
94
|
<button
|
|
95
|
-
className="min-w-0 truncate text-left font-semibold transition hover:text-[var(--storefront-accent,var(--primary))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-default disabled:hover:text-foreground"
|
|
95
|
+
className="min-w-0 truncate text-left font-semibold transition hover:text-[var(--storefront-accent-text,var(--primary))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-default disabled:hover:text-foreground"
|
|
96
96
|
disabled={!openDetail}
|
|
97
97
|
onClick={openDetail}
|
|
98
98
|
type="button"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { InventoryStorefrontSection } from '@tuturuuu/internal-api/inventory';
|
|
2
|
+
import { cn } from '@tuturuuu/utils/format';
|
|
3
|
+
import { StorefrontImagePanel } from './image-panel';
|
|
4
|
+
import { getSafeStorefrontHttpUrl } from './utils';
|
|
5
|
+
|
|
6
|
+
export function StorefrontMerchSections({
|
|
7
|
+
radius,
|
|
8
|
+
sections,
|
|
9
|
+
}: {
|
|
10
|
+
radius: string;
|
|
11
|
+
sections: InventoryStorefrontSection[];
|
|
12
|
+
}) {
|
|
13
|
+
const visibleSections = sections
|
|
14
|
+
.filter((section) => section.status === 'published')
|
|
15
|
+
.filter((section) => section.sectionType !== 'cover')
|
|
16
|
+
.filter((section) => {
|
|
17
|
+
const sectionHref = getSafeStorefrontHttpUrl(section.href);
|
|
18
|
+
return Boolean(
|
|
19
|
+
section.title?.trim() ||
|
|
20
|
+
section.description?.trim() ||
|
|
21
|
+
section.imageUrl?.trim() ||
|
|
22
|
+
sectionHref
|
|
23
|
+
);
|
|
24
|
+
})
|
|
25
|
+
.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
26
|
+
|
|
27
|
+
if (visibleSections.length === 0) return null;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="mt-4 grid gap-3">
|
|
31
|
+
{visibleSections.map((section) => {
|
|
32
|
+
const sectionHref = getSafeStorefrontHttpUrl(section.href);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<section
|
|
36
|
+
className={cn(
|
|
37
|
+
'grid overflow-hidden border border-border bg-card md:grid-cols-[minmax(0,1fr)_280px]',
|
|
38
|
+
radius
|
|
39
|
+
)}
|
|
40
|
+
key={section.id}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex min-w-0 flex-col justify-center gap-2 p-4">
|
|
43
|
+
{section.title ? (
|
|
44
|
+
<h2 className="font-semibold text-lg">{section.title}</h2>
|
|
45
|
+
) : null}
|
|
46
|
+
{section.description ? (
|
|
47
|
+
<p className="text-muted-foreground text-sm leading-6">
|
|
48
|
+
{section.description}
|
|
49
|
+
</p>
|
|
50
|
+
) : null}
|
|
51
|
+
{sectionHref ? (
|
|
52
|
+
<a
|
|
53
|
+
className="mt-1 w-fit font-medium text-sm underline-offset-4 hover:underline"
|
|
54
|
+
href={sectionHref}
|
|
55
|
+
>
|
|
56
|
+
{sectionHref.replace(/^https?:\/\//u, '')}
|
|
57
|
+
</a>
|
|
58
|
+
) : null}
|
|
59
|
+
</div>
|
|
60
|
+
<StorefrontImagePanel
|
|
61
|
+
className="min-h-36 md:min-h-full"
|
|
62
|
+
imageUrl={section.imageUrl}
|
|
63
|
+
label={section.title ?? 'Storefront section'}
|
|
64
|
+
/>
|
|
65
|
+
</section>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -182,7 +182,7 @@ export function StorefrontProductDetail({
|
|
|
182
182
|
'inline-flex h-10 items-center justify-center border px-3 font-medium text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',
|
|
183
183
|
radius,
|
|
184
184
|
isActive
|
|
185
|
-
? 'border-[var(--storefront-accent,var(--
|
|
185
|
+
? 'border-[var(--storefront-accent-border,var(--border))] bg-[var(--storefront-accent-soft,var(--muted))] text-[var(--storefront-accent-text,var(--primary))]'
|
|
186
186
|
: 'border-border bg-card hover:bg-muted/45'
|
|
187
187
|
)}
|
|
188
188
|
key={value.id}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
2
|
import type {
|
|
3
3
|
InventoryStorefront,
|
|
4
4
|
InventoryStorefrontListing,
|
|
5
5
|
} from '@tuturuuu/internal-api/inventory';
|
|
6
6
|
import { describe, expect, it } from 'vitest';
|
|
7
7
|
import { StorefrontSurface } from './storefront-surface';
|
|
8
|
-
import { sanitizeStorefrontAccentColor } from './utils';
|
|
8
|
+
import { formatStorefrontPrice, sanitizeStorefrontAccentColor } from './utils';
|
|
9
9
|
|
|
10
10
|
const storefront: InventoryStorefront = {
|
|
11
11
|
accentColor: '#abc',
|
|
@@ -56,6 +56,15 @@ const listing: InventoryStorefrontListing = {
|
|
|
56
56
|
wsId: storefront.wsId,
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
const secondListing: InventoryStorefrontListing = {
|
|
60
|
+
...listing,
|
|
61
|
+
id: 'listing-2',
|
|
62
|
+
price: 2500,
|
|
63
|
+
productId: 'product-2',
|
|
64
|
+
title: 'Team Workshop With Long Name',
|
|
65
|
+
unitId: 'unit-2',
|
|
66
|
+
};
|
|
67
|
+
|
|
59
68
|
describe('StorefrontSurface', () => {
|
|
60
69
|
it('sanitizes hex accent colors only', () => {
|
|
61
70
|
expect(sanitizeStorefrontAccentColor('#abc')).toBe('#aabbcc');
|
|
@@ -80,10 +89,11 @@ describe('StorefrontSurface', () => {
|
|
|
80
89
|
expect(screen.getAllByText('Preview Store')).toHaveLength(2);
|
|
81
90
|
expect(screen.getByText('No buyer listings')).toBeInTheDocument();
|
|
82
91
|
expect(screen.getByText('Create a listing next.')).toBeInTheDocument();
|
|
92
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cart: 0' }));
|
|
83
93
|
expect(screen.getByText('Preview checkout disabled')).toBeDisabled();
|
|
84
94
|
});
|
|
85
95
|
|
|
86
|
-
it('links storefront chrome back to the store and
|
|
96
|
+
it('links storefront chrome back to the store and opens cart from the header popover', () => {
|
|
87
97
|
render(
|
|
88
98
|
<StorefrontSurface
|
|
89
99
|
cartHref="/preview-store/cart"
|
|
@@ -100,10 +110,68 @@ describe('StorefrontSurface', () => {
|
|
|
100
110
|
'/preview-store'
|
|
101
111
|
);
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
expect(
|
|
113
|
+
expect(screen.queryByText('$2.00')).not.toBeInTheDocument();
|
|
114
|
+
|
|
115
|
+
const cartButton = screen.getByRole('button', { name: 'Cart: 2' });
|
|
116
|
+
expect(cartButton).toHaveClass('h-11', 'min-w-14', 'shrink-0');
|
|
117
|
+
expect(cartButton.querySelector('svg')).toHaveClass('size-5', 'shrink-0');
|
|
118
|
+
|
|
119
|
+
fireEvent.click(cartButton);
|
|
120
|
+
|
|
121
|
+
expect(screen.getByRole('region', { name: 'Cart' })).toBeInTheDocument();
|
|
122
|
+
expect(screen.getAllByText('$2.00')).toHaveLength(2);
|
|
123
|
+
expect(screen.getAllByText('1M')).toHaveLength(1);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('keeps the compatibility cart page as a full cart review', () => {
|
|
127
|
+
render(
|
|
128
|
+
<StorefrontSurface
|
|
129
|
+
cartHref="/preview-store/cart"
|
|
130
|
+
cartLines={[{ listingId: listing.id, quantity: 2 }]}
|
|
131
|
+
checkoutHref="/preview-store/checkout"
|
|
132
|
+
listings={[listing]}
|
|
133
|
+
mode="cart"
|
|
134
|
+
onCheckoutOpen={() => undefined}
|
|
135
|
+
storefront={storefront}
|
|
136
|
+
storefrontHref="/preview-store"
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(screen.getByRole('region', { name: 'Cart' })).toBeInTheDocument();
|
|
141
|
+
expect(screen.getByText('1:1 Mentoring')).toBeInTheDocument();
|
|
142
|
+
expect(screen.getAllByText('$2.00')).toHaveLength(2);
|
|
143
|
+
expect(screen.getByRole('button', { name: /Checkout/ })).toBeEnabled();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('keeps long item names and large totals inside the cart row bounds', () => {
|
|
147
|
+
const largeListing: InventoryStorefrontListing = {
|
|
148
|
+
...listing,
|
|
149
|
+
price: 123_456_789,
|
|
150
|
+
title:
|
|
151
|
+
'A very long product title with specifications, measurements, and buyer-facing detail',
|
|
152
|
+
};
|
|
153
|
+
const expectedTotal = formatStorefrontPrice(largeListing.price * 5, 'USD');
|
|
154
|
+
|
|
155
|
+
render(
|
|
156
|
+
<StorefrontSurface
|
|
157
|
+
cartLines={[{ listingId: largeListing.id, quantity: 5 }]}
|
|
158
|
+
listings={[largeListing]}
|
|
159
|
+
mode="cart"
|
|
160
|
+
storefront={storefront}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const amount = screen.getByTitle(expectedTotal);
|
|
165
|
+
expect(amount).toHaveClass(
|
|
166
|
+
'max-w-[9rem]',
|
|
167
|
+
'overflow-hidden',
|
|
168
|
+
'text-ellipsis',
|
|
169
|
+
'text-right'
|
|
170
|
+
);
|
|
171
|
+
expect(screen.getByText(largeListing.title)).toHaveClass(
|
|
172
|
+
'line-clamp-2',
|
|
173
|
+
'break-words'
|
|
174
|
+
);
|
|
107
175
|
});
|
|
108
176
|
|
|
109
177
|
it('prefills checkout buyer details while keeping editable form fields', () => {
|
|
@@ -113,14 +181,23 @@ describe('StorefrontSurface', () => {
|
|
|
113
181
|
email: 'buyer@example.com',
|
|
114
182
|
name: 'Sokora Buyer',
|
|
115
183
|
}}
|
|
116
|
-
cartLines={[
|
|
117
|
-
|
|
184
|
+
cartLines={[
|
|
185
|
+
{ listingId: listing.id, quantity: 1 },
|
|
186
|
+
{ listingId: secondListing.id, quantity: 1 },
|
|
187
|
+
]}
|
|
188
|
+
listings={[listing, secondListing]}
|
|
118
189
|
mode="checkout"
|
|
119
190
|
onCheckoutSubmit={() => undefined}
|
|
120
191
|
storefront={storefront}
|
|
121
192
|
/>
|
|
122
193
|
);
|
|
123
194
|
|
|
195
|
+
expect(
|
|
196
|
+
screen.getByRole('button', { name: /Order summary/ })
|
|
197
|
+
).toBeInTheDocument();
|
|
198
|
+
expect(
|
|
199
|
+
screen.getByRole('button', { name: 'Contact details' })
|
|
200
|
+
).toBeInTheDocument();
|
|
124
201
|
expect(screen.getByLabelText('Name')).toHaveValue('Sokora Buyer');
|
|
125
202
|
expect(screen.getByLabelText('Email')).toHaveValue('buyer@example.com');
|
|
126
203
|
expect(screen.getByLabelText('Name')).toBeEnabled();
|
|
@@ -155,6 +232,7 @@ describe('StorefrontSurface', () => {
|
|
|
155
232
|
);
|
|
156
233
|
|
|
157
234
|
expect(screen.queryByText('Checkout disabled')).not.toBeInTheDocument();
|
|
235
|
+
fireEvent.click(screen.getByRole('button', { name: 'Cart: 0' }));
|
|
158
236
|
expect(screen.getByText('Checkout unavailable')).toBeDisabled();
|
|
159
237
|
});
|
|
160
238
|
|
|
@@ -166,6 +244,22 @@ describe('StorefrontSurface', () => {
|
|
|
166
244
|
storefront={{
|
|
167
245
|
...storefront,
|
|
168
246
|
sections: [
|
|
247
|
+
{
|
|
248
|
+
createdAt: null,
|
|
249
|
+
description: null,
|
|
250
|
+
href: null,
|
|
251
|
+
id: 'section-empty',
|
|
252
|
+
imageUrl: null,
|
|
253
|
+
items: [],
|
|
254
|
+
metadata: {},
|
|
255
|
+
sectionType: 'promo',
|
|
256
|
+
sortOrder: 0,
|
|
257
|
+
status: 'published',
|
|
258
|
+
storefrontId: storefront.id,
|
|
259
|
+
title: null,
|
|
260
|
+
updatedAt: null,
|
|
261
|
+
wsId: storefront.wsId,
|
|
262
|
+
},
|
|
169
263
|
{
|
|
170
264
|
createdAt: null,
|
|
171
265
|
description: null,
|
|
@@ -175,7 +269,7 @@ describe('StorefrontSurface', () => {
|
|
|
175
269
|
items: [],
|
|
176
270
|
metadata: {},
|
|
177
271
|
sectionType: 'promo',
|
|
178
|
-
sortOrder:
|
|
272
|
+
sortOrder: 1,
|
|
179
273
|
status: 'published',
|
|
180
274
|
storefrontId: storefront.id,
|
|
181
275
|
title: 'Unsafe section',
|
|
@@ -191,7 +285,7 @@ describe('StorefrontSurface', () => {
|
|
|
191
285
|
items: [],
|
|
192
286
|
metadata: {},
|
|
193
287
|
sectionType: 'promo',
|
|
194
|
-
sortOrder:
|
|
288
|
+
sortOrder: 2,
|
|
195
289
|
status: 'published',
|
|
196
290
|
storefrontId: storefront.id,
|
|
197
291
|
title: 'Safe section',
|
|
@@ -204,6 +298,7 @@ describe('StorefrontSurface', () => {
|
|
|
204
298
|
);
|
|
205
299
|
|
|
206
300
|
expect(screen.getByText('Safe section')).toBeInTheDocument();
|
|
301
|
+
expect(screen.queryByText('Storefront section')).not.toBeInTheDocument();
|
|
207
302
|
expect(
|
|
208
303
|
screen.getByRole('link', { name: 'example.com/promo' })
|
|
209
304
|
).toHaveAttribute('href', 'https://example.com/promo');
|