includio-cms 0.36.9 → 0.37.1
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/API.md +178 -3
- package/CHANGELOG.md +77 -0
- package/DOCS.md +28 -3
- package/ROADMAP.md +32 -0
- package/dist/admin/client/booking/booking-dashboard.svelte +40 -0
- package/dist/admin/client/booking/booking-dashboard.svelte.d.ts +3 -0
- package/dist/admin/client/booking/customer-panel.svelte +102 -0
- package/dist/admin/client/booking/customer-panel.svelte.d.ts +11 -0
- package/dist/admin/client/booking/customer-view.d.ts +52 -0
- package/dist/admin/client/booking/customer-view.js +134 -0
- package/dist/admin/client/booking/participant-data-panel.svelte +330 -0
- package/dist/admin/client/booking/participant-data-panel.svelte.d.ts +22 -0
- package/dist/admin/client/booking/reservation-detail-page.svelte +637 -0
- package/dist/admin/client/booking/reservation-detail-page.svelte.d.ts +3 -0
- package/dist/admin/client/booking/reservations-page.svelte +221 -0
- package/dist/admin/client/booking/reservations-page.svelte.d.ts +3 -0
- package/dist/admin/client/collection/collection-entries.svelte +1 -1
- package/dist/admin/client/collection/entry-link.svelte +6 -4
- package/dist/admin/client/collection/entry-link.svelte.d.ts +3 -1
- package/dist/admin/client/collection/status-badge.svelte +39 -3
- package/dist/admin/client/collection/status-badge.svelte.d.ts +5 -3
- package/dist/admin/client/entry/entry.svelte +26 -9
- package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +73 -10
- package/dist/admin/client/entry/hybrid/hybrid-preview.svelte.d.ts +6 -5
- package/dist/admin/client/entry/preview-views-context.svelte.d.ts +22 -0
- package/dist/admin/client/entry/preview-views-context.svelte.js +58 -0
- package/dist/admin/client/entry/preview-views.d.ts +27 -0
- package/dist/admin/client/entry/preview-views.js +33 -0
- package/dist/admin/client/index.d.ts +3 -0
- package/dist/admin/client/index.js +3 -0
- package/dist/admin/client/modules/dashboard-registry.d.ts +7 -0
- package/dist/admin/client/modules/dashboard-registry.js +11 -0
- package/dist/admin/client/modules/module-dashboard-page.svelte +26 -0
- package/dist/admin/client/modules/module-dashboard-page.svelte.d.ts +6 -0
- package/dist/admin/client/modules/stat-card.svelte +22 -0
- package/dist/admin/client/modules/stat-card.svelte.d.ts +10 -0
- package/dist/admin/client/shop/shop-dashboard.svelte +29 -0
- package/dist/admin/client/shop/shop-dashboard.svelte.d.ts +3 -0
- package/dist/admin/components/fields/relation-field.svelte +11 -2
- package/dist/admin/components/icons/icon-map.js +8 -0
- package/dist/admin/components/layout/app-sidebar.svelte +45 -14
- package/dist/admin/components/layout/context-switcher.svelte +90 -0
- package/dist/admin/components/layout/context-switcher.svelte.d.ts +3 -0
- package/dist/admin/components/layout/lang.d.ts +4 -0
- package/dist/admin/components/layout/lang.js +8 -0
- package/dist/admin/components/layout/layout-renderer.svelte +2 -1
- package/dist/admin/components/layout/layout-tabs.svelte +14 -0
- package/dist/admin/components/layout/layout-tabs.svelte.d.ts +2 -0
- package/dist/admin/components/layout/nav-collections.svelte +16 -9
- package/dist/admin/components/layout/nav-collections.svelte.d.ts +5 -1
- package/dist/admin/components/layout/nav-global.svelte +45 -0
- package/dist/admin/components/layout/nav-global.svelte.d.ts +3 -0
- package/dist/admin/components/layout/nav-main.svelte +8 -32
- package/dist/admin/components/layout/nav-main.svelte.d.ts +6 -1
- package/dist/admin/components/layout/nav-module.svelte +57 -0
- package/dist/admin/components/layout/nav-module.svelte.d.ts +7 -0
- package/dist/admin/components/layout/nav-section.svelte +1 -1
- package/dist/admin/components/layout/nav-section.svelte.d.ts +1 -1
- package/dist/admin/components/layout/nav-singletons.svelte +14 -7
- package/dist/admin/components/layout/nav-singletons.svelte.d.ts +5 -1
- package/dist/admin/remote/booking.remote.d.ts +247 -0
- package/dist/admin/remote/booking.remote.js +286 -0
- package/dist/admin/remote/index.d.ts +2 -0
- package/dist/admin/remote/index.js +2 -0
- package/dist/admin/remote/modules.remote.d.ts +10 -0
- package/dist/admin/remote/modules.remote.js +14 -0
- package/dist/admin/state/active-context-resolve.d.ts +37 -0
- package/dist/admin/state/active-context-resolve.js +66 -0
- package/dist/admin/state/active-context.svelte.d.ts +8 -0
- package/dist/admin/state/active-context.svelte.js +27 -0
- package/dist/booking/adjustments.d.ts +33 -0
- package/dist/booking/adjustments.js +26 -0
- package/dist/booking/capacity.d.ts +12 -0
- package/dist/booking/capacity.js +10 -0
- package/dist/booking/client/index.d.ts +42 -0
- package/dist/booking/client/index.js +48 -0
- package/dist/booking/client/use-booking.svelte.d.ts +24 -0
- package/dist/booking/client/use-booking.svelte.js +56 -0
- package/dist/booking/config.d.ts +176 -0
- package/dist/booking/config.js +37 -0
- package/dist/booking/draft.d.ts +96 -0
- package/dist/booking/draft.js +67 -0
- package/dist/booking/expiry-logic.d.ts +30 -0
- package/dist/booking/expiry-logic.js +33 -0
- package/dist/booking/index.d.ts +18 -0
- package/dist/booking/index.js +52 -0
- package/dist/booking/ledger.d.ts +46 -0
- package/dist/booking/ledger.js +60 -0
- package/dist/booking/number.d.ts +2 -0
- package/dist/booking/number.js +11 -0
- package/dist/booking/participant-fields/access.d.ts +10 -0
- package/dist/booking/participant-fields/access.js +14 -0
- package/dist/booking/participant-fields/age.d.ts +12 -0
- package/dist/booking/participant-fields/age.js +28 -0
- package/dist/booking/participant-fields/completeness.d.ts +8 -0
- package/dist/booking/participant-fields/completeness.js +13 -0
- package/dist/booking/participant-fields/group.d.ts +6 -0
- package/dist/booking/participant-fields/group.js +15 -0
- package/dist/booking/participant-fields/index.d.ts +6 -0
- package/dist/booking/participant-fields/index.js +6 -0
- package/dist/booking/participant-fields/types.d.ts +50 -0
- package/dist/booking/participant-fields/types.js +5 -0
- package/dist/booking/participant-fields/validate.d.ts +7 -0
- package/dist/booking/participant-fields/validate.js +44 -0
- package/dist/booking/participant-pricing.d.ts +93 -0
- package/dist/booking/participant-pricing.js +91 -0
- package/dist/booking/participants-view.d.ts +32 -0
- package/dist/booking/participants-view.js +22 -0
- package/dist/booking/payment-context.d.ts +19 -0
- package/dist/booking/payment-context.js +29 -0
- package/dist/booking/pricing-inputs.d.ts +28 -0
- package/dist/booking/pricing-inputs.js +35 -0
- package/dist/booking/pricing.d.ts +86 -0
- package/dist/booking/pricing.js +125 -0
- package/dist/booking/recompute.d.ts +22 -0
- package/dist/booking/recompute.js +31 -0
- package/dist/booking/reminders-schedule.d.ts +11 -0
- package/dist/booking/reminders-schedule.js +28 -0
- package/dist/booking/server/adjustments.d.ts +27 -0
- package/dist/booking/server/adjustments.js +139 -0
- package/dist/booking/server/bookings.d.ts +51 -0
- package/dist/booking/server/bookings.js +336 -0
- package/dist/booking/server/catalog.d.ts +9 -0
- package/dist/booking/server/catalog.js +20 -0
- package/dist/booking/server/db.d.ts +7 -0
- package/dist/booking/server/db.js +18 -0
- package/dist/booking/server/email.d.ts +23 -0
- package/dist/booking/server/email.js +59 -0
- package/dist/booking/server/emailTemplateRegistry.d.ts +29 -0
- package/dist/booking/server/emailTemplateRegistry.js +125 -0
- package/dist/booking/server/http/create-handler.d.ts +5 -0
- package/dist/booking/server/http/create-handler.js +70 -0
- package/dist/booking/server/http/index.d.ts +4 -0
- package/dist/booking/server/http/index.js +4 -0
- package/dist/booking/server/http/maintenance-handler.d.ts +9 -0
- package/dist/booking/server/http/maintenance-handler.js +36 -0
- package/dist/booking/server/http/portal-handler.d.ts +13 -0
- package/dist/booking/server/http/portal-handler.js +103 -0
- package/dist/booking/server/http/webhook-handler.d.ts +9 -0
- package/dist/booking/server/http/webhook-handler.js +39 -0
- package/dist/booking/server/index.d.ts +12 -0
- package/dist/booking/server/index.js +14 -0
- package/dist/booking/server/maintenance.d.ts +28 -0
- package/dist/booking/server/maintenance.js +134 -0
- package/dist/booking/server/participants.d.ts +26 -0
- package/dist/booking/server/participants.js +92 -0
- package/dist/booking/server/payments.d.ts +34 -0
- package/dist/booking/server/payments.js +207 -0
- package/dist/booking/server/portal-session.d.ts +16 -0
- package/dist/booking/server/portal-session.js +42 -0
- package/dist/booking/server/portal.d.ts +206 -0
- package/dist/booking/server/portal.js +236 -0
- package/dist/booking/server/price-source.d.ts +73 -0
- package/dist/booking/server/price-source.js +94 -0
- package/dist/booking/server/refunds.d.ts +7 -0
- package/dist/booking/server/refunds.js +58 -0
- package/dist/booking/server/reminders.d.ts +9 -0
- package/dist/booking/server/reminders.js +24 -0
- package/dist/booking/server/validation.d.ts +17 -0
- package/dist/booking/server/validation.js +55 -0
- package/dist/booking/state-machine.d.ts +41 -0
- package/dist/booking/state-machine.js +73 -0
- package/dist/booking/templates/_partials/footer.en.html +4 -0
- package/dist/booking/templates/_partials/footer.pl.html +4 -0
- package/dist/booking/templates/_partials/header.en.html +4 -0
- package/dist/booking/templates/_partials/header.pl.html +4 -0
- package/dist/booking/templates/balance-due.en.html +10 -0
- package/dist/booking/templates/balance-due.pl.html +10 -0
- package/dist/booking/templates/balance-overdue-cancelled.en.html +5 -0
- package/dist/booking/templates/balance-overdue-cancelled.pl.html +5 -0
- package/dist/booking/templates/booking-received.en.html +9 -0
- package/dist/booking/templates/booking-received.pl.html +9 -0
- package/dist/booking/templates/deposit-paid.en.html +6 -0
- package/dist/booking/templates/deposit-paid.pl.html +6 -0
- package/dist/booking/templates/payment-link.en.html +5 -0
- package/dist/booking/templates/payment-link.pl.html +5 -0
- package/dist/booking/templates/payment-received.en.html +10 -0
- package/dist/booking/templates/payment-received.pl.html +10 -0
- package/dist/booking/totals.d.ts +38 -0
- package/dist/booking/totals.js +61 -0
- package/dist/booking/webhook-logic.d.ts +28 -0
- package/dist/booking/webhook-logic.js +50 -0
- package/dist/cli/create-user.d.ts +7 -1
- package/dist/cli/create-user.js +23 -15
- package/dist/cli/index.js +55 -7
- package/dist/cli/scaffold/admin.d.ts +6 -0
- package/dist/cli/scaffold/admin.js +5 -3
- package/dist/cli/scaffold/booking.d.ts +12 -0
- package/dist/cli/scaffold/booking.js +103 -0
- package/dist/core/cms.d.ts +24 -9
- package/dist/core/cms.js +72 -34
- package/dist/core/config/validatePreviews.d.ts +12 -0
- package/dist/core/config/validatePreviews.js +90 -0
- package/dist/core/fields/slugPath.d.ts +9 -1
- package/dist/core/fields/slugPath.js +16 -2
- package/dist/core/server/fields/resolveRelationFields.d.ts +11 -2
- package/dist/core/server/fields/resolveRelationFields.js +12 -3
- package/dist/core/server/generator/generator.js +7 -2
- package/dist/db-postgres/schema/booking/bookingAdjustments.d.ts +186 -0
- package/dist/db-postgres/schema/booking/bookingAdjustments.js +22 -0
- package/dist/db-postgres/schema/booking/bookingItemPersons.d.ts +117 -0
- package/dist/db-postgres/schema/booking/bookingItemPersons.js +22 -0
- package/dist/db-postgres/schema/booking/bookingItems.d.ts +181 -0
- package/dist/db-postgres/schema/booking/bookingItems.js +22 -0
- package/dist/db-postgres/schema/booking/bookingPayments.d.ts +204 -0
- package/dist/db-postgres/schema/booking/bookingPayments.js +25 -0
- package/dist/db-postgres/schema/booking/bookingPersons.d.ts +99 -0
- package/dist/db-postgres/schema/booking/bookingPersons.js +17 -0
- package/dist/db-postgres/schema/booking/bookingReminders.d.ts +131 -0
- package/dist/db-postgres/schema/booking/bookingReminders.js +18 -0
- package/dist/db-postgres/schema/booking/bookings.d.ts +324 -0
- package/dist/db-postgres/schema/booking/bookings.js +52 -0
- package/dist/db-postgres/schema/booking/index.d.ts +7 -0
- package/dist/db-postgres/schema/booking/index.js +7 -0
- package/dist/db-postgres/schema/index.d.ts +1 -0
- package/dist/db-postgres/schema/index.js +1 -0
- package/dist/db-postgres/schema-booking.d.ts +1 -0
- package/dist/db-postgres/schema-booking.js +1 -0
- package/dist/server/security/rate-limit-handler.d.ts +19 -0
- package/dist/server/security/rate-limit-handler.js +34 -0
- package/dist/shop/adapters/payu/client.js +3 -1
- package/dist/shop/adapters/payu/index.js +37 -17
- package/dist/shop/adapters/payu/payload.d.ts +6 -0
- package/dist/shop/adapters/payu/payload.js +1 -1
- package/dist/shop/adapters/stripe/index.js +36 -21
- package/dist/shop/index.d.ts +11 -2
- package/dist/shop/index.js +43 -1
- package/dist/shop/rate-limit.d.ts +1 -9
- package/dist/shop/rate-limit.js +3 -28
- package/dist/shop/types.d.ts +19 -0
- package/dist/sveltekit/config.d.ts +4 -0
- package/dist/sveltekit/config.js +6 -0
- package/dist/types/cms.d.ts +15 -1
- package/dist/types/cms.schema.d.ts +96 -2
- package/dist/types/cms.schema.js +84 -1
- package/dist/types/config.d.ts +33 -0
- package/dist/types/layout.d.ts +6 -0
- package/dist/types/modules.d.ts +133 -0
- package/dist/types/modules.js +27 -0
- package/dist/updates/0.37.0/index.d.ts +2 -0
- package/dist/updates/0.37.0/index.js +52 -0
- package/dist/updates/0.37.1/index.d.ts +2 -0
- package/dist/updates/0.37.1/index.js +24 -0
- package/dist/updates/index.js +5 -1
- package/package.json +19 -1
- package/dist/admin/components/layout/nav-shop.svelte +0 -33
- package/dist/admin/components/layout/nav-shop.svelte.d.ts +0 -3
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getRemotes } from '../../../sveltekit/index.js';
|
|
3
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
4
|
+
import { formatCentsPrice, formatDateTime } from '../../utils/formatters.js';
|
|
5
|
+
import PageHeader from '../../components/layout/page-header.svelte';
|
|
6
|
+
import DataTable from '../collection/data-table.svelte';
|
|
7
|
+
import TableToolbar from '../collection/table-toolbar.svelte';
|
|
8
|
+
import TablePagination from '../collection/table-pagination.svelte';
|
|
9
|
+
import StateDisplay from '../collection/state-display.svelte';
|
|
10
|
+
import StatusBadge from '../collection/status-badge.svelte';
|
|
11
|
+
import EntryLink from '../collection/entry-link.svelte';
|
|
12
|
+
import { renderComponent } from '../../../components/ui/data-table/render-helpers.js';
|
|
13
|
+
import { customerSummary } from './customer-view.js';
|
|
14
|
+
import type { ColumnDef, PaginationState } from '@tanstack/table-core';
|
|
15
|
+
import type { BookingStatus } from '../../../booking/state-machine.js';
|
|
16
|
+
|
|
17
|
+
type BookingRow = {
|
|
18
|
+
id: string;
|
|
19
|
+
number: string;
|
|
20
|
+
status: BookingStatus;
|
|
21
|
+
currency: string;
|
|
22
|
+
customer: Record<string, unknown>;
|
|
23
|
+
totals: { gross: number; balance: number };
|
|
24
|
+
createdAt: Date | string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const remotes = getRemotes();
|
|
28
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
29
|
+
|
|
30
|
+
let statusFilter = $state<string | null>(null);
|
|
31
|
+
let searchQuery = $state('');
|
|
32
|
+
let appliedSearch = $state('');
|
|
33
|
+
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 50 });
|
|
34
|
+
let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
35
|
+
|
|
36
|
+
const bookingsQuery = $derived(
|
|
37
|
+
remotes.listBookings({
|
|
38
|
+
status: (statusFilter as BookingStatus | null) ?? undefined,
|
|
39
|
+
search: appliedSearch || undefined,
|
|
40
|
+
limit: pagination.pageSize,
|
|
41
|
+
offset: pagination.pageIndex * pagination.pageSize
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const items = $derived<BookingRow[]>((bookingsQuery.current?.items as BookingRow[]) ?? []);
|
|
46
|
+
const total = $derived(bookingsQuery.current?.total ?? 0);
|
|
47
|
+
const isLoading = $derived(!bookingsQuery.ready);
|
|
48
|
+
const isError = $derived(Boolean(bookingsQuery.error));
|
|
49
|
+
const pageCount = $derived(Math.max(1, Math.ceil(total / pagination.pageSize)));
|
|
50
|
+
|
|
51
|
+
const t = $derived(
|
|
52
|
+
interfaceLanguage.current === 'pl'
|
|
53
|
+
? {
|
|
54
|
+
title: 'Rezerwacje',
|
|
55
|
+
search: 'Szukaj',
|
|
56
|
+
searchPlaceholder: 'Szukaj po numerze, imieniu lub e-mailu…',
|
|
57
|
+
filterStatus: 'Status',
|
|
58
|
+
number: 'Numer',
|
|
59
|
+
created: 'Utworzono',
|
|
60
|
+
customer: 'Klient',
|
|
61
|
+
total: 'Wartość',
|
|
62
|
+
balance: 'Saldo',
|
|
63
|
+
status: 'Status',
|
|
64
|
+
emptyTitle: 'Brak rezerwacji',
|
|
65
|
+
emptyDescription: 'Gdy ktoś zarezerwuje, pojawi się tutaj.',
|
|
66
|
+
noResults: 'Brak wyników.',
|
|
67
|
+
statusDraft: 'Czeka na zaliczkę',
|
|
68
|
+
statusReserved: 'Zarezerwowane',
|
|
69
|
+
statusPartiallyPaid: 'Częściowo opłacone',
|
|
70
|
+
statusPaid: 'Opłacone',
|
|
71
|
+
statusCompleted: 'Zakończone',
|
|
72
|
+
statusCancelled: 'Anulowane'
|
|
73
|
+
}
|
|
74
|
+
: {
|
|
75
|
+
title: 'Reservations',
|
|
76
|
+
search: 'Search',
|
|
77
|
+
searchPlaceholder: 'Search by number, name or email…',
|
|
78
|
+
filterStatus: 'Status',
|
|
79
|
+
number: 'Number',
|
|
80
|
+
created: 'Created',
|
|
81
|
+
customer: 'Customer',
|
|
82
|
+
total: 'Total',
|
|
83
|
+
balance: 'Balance',
|
|
84
|
+
status: 'Status',
|
|
85
|
+
emptyTitle: 'No reservations yet',
|
|
86
|
+
emptyDescription: 'Reservations will appear here as soon as someone books.',
|
|
87
|
+
noResults: 'No results.',
|
|
88
|
+
statusDraft: 'Awaiting deposit',
|
|
89
|
+
statusReserved: 'Reserved',
|
|
90
|
+
statusPartiallyPaid: 'Partially paid',
|
|
91
|
+
statusPaid: 'Paid',
|
|
92
|
+
statusCompleted: 'Completed',
|
|
93
|
+
statusCancelled: 'Cancelled'
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const dataFilters = $derived([
|
|
98
|
+
{
|
|
99
|
+
slug: 'status',
|
|
100
|
+
label: t.filterStatus,
|
|
101
|
+
options: [
|
|
102
|
+
{ value: 'draft', label: t.statusDraft },
|
|
103
|
+
{ value: 'reserved', label: t.statusReserved },
|
|
104
|
+
{ value: 'partially_paid', label: t.statusPartiallyPaid },
|
|
105
|
+
{ value: 'paid', label: t.statusPaid },
|
|
106
|
+
{ value: 'completed', label: t.statusCompleted },
|
|
107
|
+
{ value: 'cancelled', label: t.statusCancelled }
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
const activeDataFilters = $derived({ status: statusFilter });
|
|
112
|
+
|
|
113
|
+
function scheduleSearchUpdate(value: string) {
|
|
114
|
+
if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
|
|
115
|
+
searchDebounceTimer = setTimeout(() => {
|
|
116
|
+
appliedSearch = value.trim();
|
|
117
|
+
pagination = { ...pagination, pageIndex: 0 };
|
|
118
|
+
}, 350);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const columns = $derived.by<ColumnDef<BookingRow>[]>(() => [
|
|
122
|
+
{
|
|
123
|
+
accessorKey: 'number',
|
|
124
|
+
header: t.number,
|
|
125
|
+
cell: (info) =>
|
|
126
|
+
renderComponent(EntryLink, {
|
|
127
|
+
name: info.row.original.number,
|
|
128
|
+
url: `/admin/booking/reservations/${info.row.original.id}`
|
|
129
|
+
})
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
accessorKey: 'createdAt',
|
|
133
|
+
header: t.created,
|
|
134
|
+
cell: (info) => formatDateTime(info.row.original.createdAt, interfaceLanguage.current)
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'customer',
|
|
138
|
+
header: t.customer,
|
|
139
|
+
cell: (info) => {
|
|
140
|
+
const row = info.row.original;
|
|
141
|
+
const s = customerSummary(row.customer ?? {});
|
|
142
|
+
return renderComponent(EntryLink, {
|
|
143
|
+
name: s.name ?? s.email ?? '—',
|
|
144
|
+
url: `/admin/booking/reservations/${row.id}`,
|
|
145
|
+
slug: s.name && s.email ? s.email : undefined
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'gross',
|
|
151
|
+
header: t.total,
|
|
152
|
+
cell: (info) => formatCentsPrice(info.row.original.totals.gross, info.row.original.currency)
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'balance',
|
|
156
|
+
header: t.balance,
|
|
157
|
+
cell: (info) => formatCentsPrice(info.row.original.totals.balance, info.row.original.currency)
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
accessorKey: 'status',
|
|
161
|
+
header: t.status,
|
|
162
|
+
cell: (info) =>
|
|
163
|
+
renderComponent(StatusBadge, { variant: 'booking', status: info.row.original.status })
|
|
164
|
+
}
|
|
165
|
+
]);
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
<div class="p-5 pb-24 md:p-7">
|
|
169
|
+
<PageHeader title={t.title} count={!isLoading && !isError ? total : undefined} />
|
|
170
|
+
|
|
171
|
+
{#if !isLoading && !isError && total === 0 && !statusFilter && !appliedSearch}
|
|
172
|
+
<StateDisplay kind="empty" title={t.emptyTitle} description={t.emptyDescription} />
|
|
173
|
+
{:else}
|
|
174
|
+
<TableToolbar
|
|
175
|
+
{searchQuery}
|
|
176
|
+
searchPlaceholder={t.searchPlaceholder}
|
|
177
|
+
searchLabel={t.search}
|
|
178
|
+
onSearchChange={(q) => {
|
|
179
|
+
searchQuery = q;
|
|
180
|
+
scheduleSearchUpdate(q);
|
|
181
|
+
}}
|
|
182
|
+
hideStatusFilter
|
|
183
|
+
hideViewToggle
|
|
184
|
+
{dataFilters}
|
|
185
|
+
{activeDataFilters}
|
|
186
|
+
onDataFilterChange={(_slug, value) => {
|
|
187
|
+
statusFilter = value;
|
|
188
|
+
pagination = { ...pagination, pageIndex: 0 };
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
<div class="bg-card overflow-hidden rounded-xl border shadow-sm">
|
|
193
|
+
{#if isError}
|
|
194
|
+
<StateDisplay kind="error" />
|
|
195
|
+
{:else if isLoading && items.length === 0}
|
|
196
|
+
<StateDisplay kind="loading" />
|
|
197
|
+
{:else}
|
|
198
|
+
<DataTable
|
|
199
|
+
data={items}
|
|
200
|
+
{columns}
|
|
201
|
+
enablePagination
|
|
202
|
+
manualPagination
|
|
203
|
+
{pageCount}
|
|
204
|
+
rowCount={total}
|
|
205
|
+
{pagination}
|
|
206
|
+
onPaginationChange={(p) => (pagination = p)}
|
|
207
|
+
emptyTitle={t.noResults}
|
|
208
|
+
/>
|
|
209
|
+
|
|
210
|
+
<TablePagination
|
|
211
|
+
pageIndex={pagination.pageIndex}
|
|
212
|
+
pageSize={pagination.pageSize}
|
|
213
|
+
{pageCount}
|
|
214
|
+
totalItems={total}
|
|
215
|
+
onPageChange={(p) => (pagination = { ...pagination, pageIndex: p })}
|
|
216
|
+
onPageSizeChange={(s) => (pagination = { pageIndex: 0, pageSize: s })}
|
|
217
|
+
/>
|
|
218
|
+
{/if}
|
|
219
|
+
</div>
|
|
220
|
+
{/if}
|
|
221
|
+
</div>
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
type Props = {
|
|
3
3
|
name: string;
|
|
4
|
+
/** Admin edit link for this entry. */
|
|
4
5
|
url: string;
|
|
5
|
-
|
|
6
|
+
/** Public path of the entry — already absolute (`resolveEntryUrl`), never prefix it. */
|
|
7
|
+
path?: string;
|
|
6
8
|
};
|
|
7
9
|
|
|
8
|
-
let { name, url,
|
|
10
|
+
let { name, url, path }: Props = $props();
|
|
9
11
|
</script>
|
|
10
12
|
|
|
11
13
|
<div>
|
|
12
14
|
<a href={url} class="font-medium text-foreground hover:text-primary hover:underline">
|
|
13
15
|
{name}
|
|
14
16
|
</a>
|
|
15
|
-
{#if
|
|
16
|
-
<div class="text-[11px] text-muted-foreground font-mono mt-0.5 truncate max-w-[200px]"
|
|
17
|
+
{#if path}
|
|
18
|
+
<div class="text-[11px] text-muted-foreground font-mono mt-0.5 truncate max-w-[200px]">{path}</div>
|
|
17
19
|
{/if}
|
|
18
20
|
</div>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
type Props = {
|
|
2
2
|
name: string;
|
|
3
|
+
/** Admin edit link for this entry. */
|
|
3
4
|
url: string;
|
|
4
|
-
|
|
5
|
+
/** Public path of the entry — already absolute (`resolveEntryUrl`), never prefix it. */
|
|
6
|
+
path?: string;
|
|
5
7
|
};
|
|
6
8
|
declare const EntryLink: import("svelte").Component<Props, {}, "">;
|
|
7
9
|
type EntryLink = ReturnType<typeof EntryLink>;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { EntryStatus } from '../../../types/entries.js';
|
|
3
3
|
import type { OrderStatus } from '../../../shop/types.js';
|
|
4
|
+
import type { BookingStatus } from '../../../booking/state-machine.js';
|
|
4
5
|
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
5
6
|
import type { InterfaceLanguage } from '../../../types/languages.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* StatusBadge accepts
|
|
9
|
+
* StatusBadge accepts four usage modes (variants):
|
|
9
10
|
* - entry (default): pass `status` as `EntryStatus`
|
|
10
11
|
* - order: pass `variant="order"` and `status` as `OrderStatus`
|
|
12
|
+
* - booking: pass `variant="booking"` and `status` as `BookingStatus`
|
|
11
13
|
* - boolean: pass `variant="boolean"` and `active: boolean`
|
|
12
14
|
*
|
|
13
15
|
* Type stays permissive (FlatProps) so consumers can pass any combination;
|
|
14
16
|
* runtime narrows by `variant` and falls back to muted style on unknown.
|
|
15
17
|
*/
|
|
16
18
|
type Props = {
|
|
17
|
-
variant?: 'entry' | 'order' | 'boolean';
|
|
18
|
-
status?: EntryStatus | OrderStatus;
|
|
19
|
+
variant?: 'entry' | 'order' | 'boolean' | 'booking';
|
|
20
|
+
status?: EntryStatus | OrderStatus | BookingStatus;
|
|
19
21
|
active?: boolean;
|
|
20
22
|
activeLabel?: string;
|
|
21
23
|
inactiveLabel?: string;
|
|
@@ -92,6 +94,35 @@
|
|
|
92
94
|
}
|
|
93
95
|
};
|
|
94
96
|
|
|
97
|
+
const bookingStyles: Record<BookingStatus, StatusStyle> = {
|
|
98
|
+
draft: { dot: '◐', classes: 'bg-warning-bg text-warning' },
|
|
99
|
+
reserved: { dot: '●', classes: 'bg-info-bg text-info' },
|
|
100
|
+
partially_paid: { dot: '◐', classes: 'bg-warning-bg text-warning' },
|
|
101
|
+
paid: { dot: '●', classes: 'bg-success-bg text-success' },
|
|
102
|
+
completed: { dot: '●', classes: 'bg-success-bg text-success' },
|
|
103
|
+
cancelled: { dot: '○', classes: 'bg-muted text-muted-foreground' }
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const bookingLabels: Record<InterfaceLanguage, Record<BookingStatus, string>> = {
|
|
107
|
+
en: {
|
|
108
|
+
draft: 'Awaiting deposit',
|
|
109
|
+
reserved: 'Reserved',
|
|
110
|
+
partially_paid: 'Partially paid',
|
|
111
|
+
paid: 'Paid',
|
|
112
|
+
completed: 'Completed',
|
|
113
|
+
cancelled: 'Cancelled'
|
|
114
|
+
},
|
|
115
|
+
pl: {
|
|
116
|
+
// „Szkic" nic nie mówi osobie prowadzącej rezerwacje — ona myśli „czeka na zaliczkę".
|
|
117
|
+
draft: 'Czeka na zaliczkę',
|
|
118
|
+
reserved: 'Zarezerwowane',
|
|
119
|
+
partially_paid: 'Częściowo opłacone',
|
|
120
|
+
paid: 'Opłacone',
|
|
121
|
+
completed: 'Zakończone',
|
|
122
|
+
cancelled: 'Anulowane'
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
95
126
|
const booleanDefaults: Record<InterfaceLanguage, { active: string; inactive: string }> = {
|
|
96
127
|
en: { active: 'Active', inactive: 'Inactive' },
|
|
97
128
|
pl: { active: 'Aktywny', inactive: 'Nieaktywny' }
|
|
@@ -106,6 +137,11 @@
|
|
|
106
137
|
if (!s || !(s in orderStyles)) return { style: fallbackStyle, label: String(s ?? '') };
|
|
107
138
|
return { style: orderStyles[s], label: orderLabels[lang][s] };
|
|
108
139
|
}
|
|
140
|
+
if (variant === 'booking') {
|
|
141
|
+
const s = status as BookingStatus | undefined;
|
|
142
|
+
if (!s || !(s in bookingStyles)) return { style: fallbackStyle, label: String(s ?? '') };
|
|
143
|
+
return { style: bookingStyles[s], label: bookingLabels[lang][s] };
|
|
144
|
+
}
|
|
109
145
|
if (variant === 'boolean') {
|
|
110
146
|
const isActive = active === true;
|
|
111
147
|
const fallback = booleanDefaults[lang];
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import type { EntryStatus } from '../../../types/entries.js';
|
|
2
2
|
import type { OrderStatus } from '../../../shop/types.js';
|
|
3
|
+
import type { BookingStatus } from '../../../booking/state-machine.js';
|
|
3
4
|
/**
|
|
4
|
-
* StatusBadge accepts
|
|
5
|
+
* StatusBadge accepts four usage modes (variants):
|
|
5
6
|
* - entry (default): pass `status` as `EntryStatus`
|
|
6
7
|
* - order: pass `variant="order"` and `status` as `OrderStatus`
|
|
8
|
+
* - booking: pass `variant="booking"` and `status` as `BookingStatus`
|
|
7
9
|
* - boolean: pass `variant="boolean"` and `active: boolean`
|
|
8
10
|
*
|
|
9
11
|
* Type stays permissive (FlatProps) so consumers can pass any combination;
|
|
10
12
|
* runtime narrows by `variant` and falls back to muted style on unknown.
|
|
11
13
|
*/
|
|
12
14
|
type Props = {
|
|
13
|
-
variant?: 'entry' | 'order' | 'boolean';
|
|
14
|
-
status?: EntryStatus | OrderStatus;
|
|
15
|
+
variant?: 'entry' | 'order' | 'boolean' | 'booking';
|
|
16
|
+
status?: EntryStatus | OrderStatus | BookingStatus;
|
|
15
17
|
active?: boolean;
|
|
16
18
|
activeLabel?: string;
|
|
17
19
|
inactiveLabel?: string;
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
import { getCollectionEntryDisplayLabel } from '../../utils/entryLabel.js';
|
|
34
34
|
import { getFieldsFromConfig, hasLayout } from '../../../core/fields/layoutUtils.js';
|
|
35
35
|
import { createHybridContext } from './hybrid/hybrid-context.svelte.js';
|
|
36
|
+
import { createPreviewViewsContext } from './preview-views-context.svelte.js';
|
|
37
|
+
import { normalizePreviews, buildPreviewUrl } from './preview-views.js';
|
|
36
38
|
import { onMount, setContext, tick } from 'svelte';
|
|
37
39
|
import { get } from 'svelte/store';
|
|
38
40
|
const contentLanguage = getContentLanguage();
|
|
@@ -111,6 +113,11 @@
|
|
|
111
113
|
let { collection } = entry;
|
|
112
114
|
const isArchived = $derived(!!entry.archivedAt);
|
|
113
115
|
|
|
116
|
+
// Preview views: `previewUrl` is normalized to a single view, so everything
|
|
117
|
+
// below only ever deals with an array. Empty array = collection has no preview.
|
|
118
|
+
const previewViews = createPreviewViewsContext(normalizePreviews(collection));
|
|
119
|
+
const hasPreview = previewViews.views.length > 0;
|
|
120
|
+
|
|
114
121
|
setContext('cms-path-template', collection.pathTemplate || null);
|
|
115
122
|
setContext('cms-entry-published', entry.publishedVersions[contentLanguage.current] != null);
|
|
116
123
|
setContext('cms-entry-id', entry.id);
|
|
@@ -431,9 +438,17 @@
|
|
|
431
438
|
}
|
|
432
439
|
}
|
|
433
440
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
441
|
+
// Src of the active view — switching a view swaps the iframe src, which reloads
|
|
442
|
+
// the preview page; it then asks for the current (unsaved) form data via
|
|
443
|
+
// `preview-ready`, so nothing in the editor is lost.
|
|
444
|
+
const previewSrc = $derived(
|
|
445
|
+
previewViews.activeView
|
|
446
|
+
? buildPreviewUrl(previewViews.activeView, String(editingEntry.id))
|
|
447
|
+
: ''
|
|
448
|
+
);
|
|
449
|
+
const previewOrigin = $derived(
|
|
450
|
+
previewSrc ? getOriginFromUrl(previewSrc) : window.location.origin
|
|
451
|
+
);
|
|
437
452
|
|
|
438
453
|
const updatePreview = useDebounce(
|
|
439
454
|
async (window: Window, form: SuperForm<Record<string, unknown>>) => {
|
|
@@ -466,16 +481,16 @@
|
|
|
466
481
|
|
|
467
482
|
// Subscribe to form changes for preview updates
|
|
468
483
|
onMount(() => {
|
|
469
|
-
if (!
|
|
484
|
+
if (!hasPreview) return;
|
|
470
485
|
const unsub = form.form.subscribe(() => {
|
|
471
486
|
onUpdate();
|
|
472
487
|
});
|
|
473
488
|
return () => unsub();
|
|
474
489
|
});
|
|
475
490
|
|
|
476
|
-
// Enable hybrid mode when collection has
|
|
491
|
+
// Enable hybrid mode when the collection has at least one preview view
|
|
477
492
|
$effect(() => {
|
|
478
|
-
hybridContext.enabled =
|
|
493
|
+
hybridContext.enabled = hasPreview;
|
|
479
494
|
});
|
|
480
495
|
|
|
481
496
|
// Send current form data + hybrid mode state to iframe
|
|
@@ -605,7 +620,7 @@
|
|
|
605
620
|
});
|
|
606
621
|
|
|
607
622
|
const t = $derived(lang[interfaceLanguage.current]);
|
|
608
|
-
const isHybrid = $derived(hybridContext.mode === 'hybrid' &&
|
|
623
|
+
const isHybrid = $derived(hybridContext.mode === 'hybrid' && hasPreview);
|
|
609
624
|
|
|
610
625
|
const errorPaths = $derived(new Set(validationErrors.map((e) => e.path)));
|
|
611
626
|
|
|
@@ -752,8 +767,10 @@
|
|
|
752
767
|
<div class="bg-accent h-full animate-pulse rounded-md"></div>
|
|
753
768
|
{:then { default: HybridPreview }}
|
|
754
769
|
<HybridPreview
|
|
755
|
-
{
|
|
756
|
-
{
|
|
770
|
+
{previewSrc}
|
|
771
|
+
views={previewViews.views}
|
|
772
|
+
activeName={previewViews.activeName}
|
|
773
|
+
onSelectView={(name) => previewViews.select(name)}
|
|
757
774
|
bind:previewIframe
|
|
758
775
|
bind:sizePreset
|
|
759
776
|
{size}
|
|
@@ -1,26 +1,59 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as ToggleGroup from '../../../../components/ui/toggle-group/index.js';
|
|
3
|
+
import * as Select from '../../../../components/ui/select/index.js';
|
|
3
4
|
import Button from '../../../../components/ui/button/button.svelte';
|
|
4
5
|
import DeviceTablet from '@tabler/icons-svelte/icons/device-tablet';
|
|
5
6
|
import DeviceMobile from '@tabler/icons-svelte/icons/device-mobile';
|
|
6
7
|
import DeviceDesktop from '@tabler/icons-svelte/icons/device-desktop';
|
|
7
8
|
import ArrowsMaximize from '@tabler/icons-svelte/icons/arrows-maximize';
|
|
8
9
|
import Link from '@tabler/icons-svelte/icons/link';
|
|
9
|
-
import type {
|
|
10
|
+
import type { PreviewView } from '../../../../types/config.js';
|
|
11
|
+
import { getLocalizedLabel } from '../../../utils/collectionLabel.js';
|
|
12
|
+
import { useInterfaceLanguage } from '../../../state/interface-language.svelte.js';
|
|
10
13
|
import type { ElementSize } from 'runed';
|
|
11
14
|
|
|
12
15
|
type SizePreset = 'responsive' | 'desktop' | 'tablet' | 'mobile';
|
|
13
16
|
|
|
14
17
|
type Props = {
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
/** Fully built preview URL of the active view. */
|
|
19
|
+
previewSrc: string;
|
|
20
|
+
views: PreviewView[];
|
|
21
|
+
activeName: string;
|
|
22
|
+
onSelectView: (name: string) => void;
|
|
17
23
|
previewIframe?: HTMLIFrameElement;
|
|
18
24
|
sizePreset: SizePreset;
|
|
19
25
|
size: ElementSize;
|
|
20
26
|
el?: HTMLElement;
|
|
21
27
|
};
|
|
22
28
|
|
|
23
|
-
let {
|
|
29
|
+
let {
|
|
30
|
+
previewSrc,
|
|
31
|
+
views,
|
|
32
|
+
activeName,
|
|
33
|
+
onSelectView,
|
|
34
|
+
previewIframe = $bindable(),
|
|
35
|
+
sizePreset = $bindable('responsive'),
|
|
36
|
+
size,
|
|
37
|
+
el = $bindable()
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
41
|
+
|
|
42
|
+
const lang = {
|
|
43
|
+
pl: { view: 'Widok', openInNewTab: 'Otwórz podgląd w nowej karcie' },
|
|
44
|
+
en: { view: 'View', openInNewTab: 'Open preview in a new tab' }
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
const t = $derived(lang[interfaceLanguage.current as 'pl' | 'en'] ?? lang.en);
|
|
48
|
+
|
|
49
|
+
function viewLabel(view: PreviewView): string {
|
|
50
|
+
return getLocalizedLabel(view.label, interfaceLanguage.current) || view.name;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const activeLabel = $derived.by(() => {
|
|
54
|
+
const view = views.find((v) => v.name === activeName) ?? views[0];
|
|
55
|
+
return view ? viewLabel(view) : '';
|
|
56
|
+
});
|
|
24
57
|
|
|
25
58
|
const sizePresets = {
|
|
26
59
|
responsive: null,
|
|
@@ -61,8 +94,36 @@
|
|
|
61
94
|
</script>
|
|
62
95
|
|
|
63
96
|
<div class="flex h-full flex-col overflow-hidden">
|
|
64
|
-
|
|
65
|
-
|
|
97
|
+
<!--
|
|
98
|
+
Trzy strefy, nie jeden ciąg: kontekst (jaki widok) przy krawędzi wiodącej,
|
|
99
|
+
prezentacja (rozmiar urządzenia) na środku płótna, akcja poboczna (otwórz
|
|
100
|
+
w nowej karcie) przy końcowej. Grid `1fr auto 1fr` trzyma przełączniki
|
|
101
|
+
urządzeń dokładnie na środku niezależnie od długości nazwy widoku.
|
|
102
|
+
-->
|
|
103
|
+
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-2 border-b px-2 py-1.5">
|
|
104
|
+
<div class="justify-self-start">
|
|
105
|
+
{#if views.length > 1}
|
|
106
|
+
<Select.Root type="single" value={activeName} onValueChange={onSelectView}>
|
|
107
|
+
<Select.Trigger
|
|
108
|
+
class="hover:bg-accent hover:text-accent-foreground h-8 w-auto gap-1.5 border-0 bg-transparent px-2 text-sm font-medium shadow-none"
|
|
109
|
+
aria-label={t.view}
|
|
110
|
+
>
|
|
111
|
+
{activeLabel}
|
|
112
|
+
</Select.Trigger>
|
|
113
|
+
<Select.Content align="start">
|
|
114
|
+
{#each views as view (view.name)}
|
|
115
|
+
<Select.Item value={view.name} label={viewLabel(view)} />
|
|
116
|
+
{/each}
|
|
117
|
+
</Select.Content>
|
|
118
|
+
</Select.Root>
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
121
|
+
<ToggleGroup.Root
|
|
122
|
+
type="single"
|
|
123
|
+
bind:value={sizePreset}
|
|
124
|
+
role="radiogroup"
|
|
125
|
+
class="justify-self-center"
|
|
126
|
+
>
|
|
66
127
|
<ToggleGroup.Item value="responsive" aria-label="Responsive view">
|
|
67
128
|
<ArrowsMaximize class="size-4" />
|
|
68
129
|
</ToggleGroup.Item>
|
|
@@ -79,8 +140,10 @@
|
|
|
79
140
|
<Button
|
|
80
141
|
variant="ghost"
|
|
81
142
|
size="icon"
|
|
82
|
-
href=
|
|
143
|
+
href={previewSrc}
|
|
83
144
|
target="_blank"
|
|
145
|
+
aria-label={t.openInNewTab}
|
|
146
|
+
class="justify-self-end"
|
|
84
147
|
>
|
|
85
148
|
<Link class="size-4" />
|
|
86
149
|
</Button>
|
|
@@ -90,7 +153,7 @@
|
|
|
90
153
|
<iframe
|
|
91
154
|
bind:this={previewIframe}
|
|
92
155
|
class="h-full w-full border-0"
|
|
93
|
-
src=
|
|
156
|
+
src={previewSrc}
|
|
94
157
|
title="preview"
|
|
95
158
|
></iframe>
|
|
96
159
|
{:else if sizePreset === 'desktop'}
|
|
@@ -101,7 +164,7 @@
|
|
|
101
164
|
class="shrink-0 border-0"
|
|
102
165
|
width={sizePresets.desktop[0]}
|
|
103
166
|
height={sizePresets.desktop[1]}
|
|
104
|
-
src=
|
|
167
|
+
src={previewSrc}
|
|
105
168
|
title="preview"
|
|
106
169
|
></iframe>
|
|
107
170
|
</div>
|
|
@@ -142,7 +205,7 @@
|
|
|
142
205
|
"
|
|
143
206
|
width={iw}
|
|
144
207
|
height={ih}
|
|
145
|
-
src=
|
|
208
|
+
src={previewSrc}
|
|
146
209
|
title="preview"
|
|
147
210
|
></iframe>
|
|
148
211
|
</div>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PreviewView } from '../../../../types/config.js';
|
|
2
2
|
import type { ElementSize } from 'runed';
|
|
3
3
|
type SizePreset = 'responsive' | 'desktop' | 'tablet' | 'mobile';
|
|
4
4
|
type Props = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/** Fully built preview URL of the active view. */
|
|
6
|
+
previewSrc: string;
|
|
7
|
+
views: PreviewView[];
|
|
8
|
+
activeName: string;
|
|
9
|
+
onSelectView: (name: string) => void;
|
|
9
10
|
previewIframe?: HTMLIFrameElement;
|
|
10
11
|
sizePreset: SizePreset;
|
|
11
12
|
size: ElementSize;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PreviewView } from '../../../types/config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Holds which preview view is showing, plus the per-tab memory of manual picks:
|
|
4
|
+
* a view the user selected by hand on a tab wins over that tab's `preview`
|
|
5
|
+
* binding when they come back to it. Lives as long as the open entry — nothing
|
|
6
|
+
* is persisted to the URL or the database.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createPreviewViewsContext(views: PreviewView[]): {
|
|
9
|
+
readonly views: PreviewView[];
|
|
10
|
+
readonly activeName: string;
|
|
11
|
+
readonly activeView: PreviewView | undefined;
|
|
12
|
+
/** Manual pick — remember it for the tab the user is on. */
|
|
13
|
+
select(name: string): void;
|
|
14
|
+
/** A tab became active (on mount too, so a `?tab=` deep-link lands right). */
|
|
15
|
+
onTabActivate(tabValue: string, tabPreview?: string): void;
|
|
16
|
+
};
|
|
17
|
+
export type PreviewViewsContext = ReturnType<typeof createPreviewViewsContext>;
|
|
18
|
+
/**
|
|
19
|
+
* Optional — layout components also render outside the entry editor (Storybook,
|
|
20
|
+
* forms), where no preview context exists.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPreviewViewsContext(): PreviewViewsContext | undefined;
|