@cardinali-studio/adminkit 1.0.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/dist/api.d.ts +195 -0
- package/dist/api.js +118 -0
- package/dist/app/App.d.ts +3 -0
- package/dist/app/App.js +320 -0
- package/dist/app/app.css +2068 -0
- package/dist/app/index.d.ts +18 -0
- package/dist/app/index.js +33 -0
- package/dist/app/lib/bookings.d.ts +132 -0
- package/dist/app/lib/bookings.js +79 -0
- package/dist/app/lib/deliveries.d.ts +95 -0
- package/dist/app/lib/deliveries.js +104 -0
- package/dist/app/lib/delivery-drop.d.ts +1 -0
- package/dist/app/lib/delivery-drop.js +72 -0
- package/dist/app/lib/delivery-upload.d.ts +78 -0
- package/dist/app/lib/delivery-upload.js +585 -0
- package/dist/app/lib/groups.d.ts +9 -0
- package/dist/app/lib/groups.js +32 -0
- package/dist/app/lib/home.d.ts +11 -0
- package/dist/app/lib/home.js +67 -0
- package/dist/app/lib/overview.d.ts +82 -0
- package/dist/app/lib/overview.js +68 -0
- package/dist/app/lib/pages.d.ts +24 -0
- package/dist/app/lib/pages.js +156 -0
- package/dist/app/lib/publish.d.ts +18 -0
- package/dist/app/lib/publish.js +63 -0
- package/dist/app/lib/reorder.d.ts +3 -0
- package/dist/app/lib/reorder.js +31 -0
- package/dist/app/lib/requests.d.ts +10 -0
- package/dist/app/lib/requests.js +67 -0
- package/dist/app/lib/router.d.ts +1 -0
- package/dist/app/lib/router.js +14 -0
- package/dist/app/lib/settings.d.ts +29 -0
- package/dist/app/lib/settings.js +82 -0
- package/dist/app/lib/slug.d.ts +3 -0
- package/dist/app/lib/slug.js +23 -0
- package/dist/app/lib/text.d.ts +1 -0
- package/dist/app/lib/text.js +13 -0
- package/dist/app/lib/theme.d.ts +7 -0
- package/dist/app/lib/theme.js +43 -0
- package/dist/app/lib/upload-queue.d.ts +35 -0
- package/dist/app/lib/upload-queue.js +167 -0
- package/dist/app/routes/AboutEditor.d.ts +15 -0
- package/dist/app/routes/AboutEditor.js +122 -0
- package/dist/app/routes/Bookings.d.ts +21 -0
- package/dist/app/routes/Bookings.js +134 -0
- package/dist/app/routes/Deliveries.d.ts +37 -0
- package/dist/app/routes/Deliveries.js +563 -0
- package/dist/app/routes/GallerySectionsEditor.d.ts +12 -0
- package/dist/app/routes/GallerySectionsEditor.js +107 -0
- package/dist/app/routes/HomeEditor.d.ts +10 -0
- package/dist/app/routes/HomeEditor.js +188 -0
- package/dist/app/routes/Overview.d.ts +17 -0
- package/dist/app/routes/Overview.js +318 -0
- package/dist/app/routes/PageActions.d.ts +12 -0
- package/dist/app/routes/PageActions.js +93 -0
- package/dist/app/routes/PageEditor.d.ts +23 -0
- package/dist/app/routes/PageEditor.js +233 -0
- package/dist/app/routes/PageSeoEditor.d.ts +13 -0
- package/dist/app/routes/PageSeoEditor.js +102 -0
- package/dist/app/routes/PagesList.d.ts +10 -0
- package/dist/app/routes/PagesList.js +76 -0
- package/dist/app/routes/PortfolioWorksEditor.d.ts +12 -0
- package/dist/app/routes/PortfolioWorksEditor.js +139 -0
- package/dist/app/routes/Requests.d.ts +13 -0
- package/dist/app/routes/Requests.js +30 -0
- package/dist/app/routes/SiteSettingsEditor.d.ts +21 -0
- package/dist/app/routes/SiteSettingsEditor.js +66 -0
- package/dist/app/routes/UploadDock.d.ts +1 -0
- package/dist/app/routes/UploadDock.js +66 -0
- package/dist/app/routes/WorkshopEditionsEditor.d.ts +12 -0
- package/dist/app/routes/WorkshopEditionsEditor.js +101 -0
- package/dist/app/site.d.ts +37 -0
- package/dist/app/site.js +18 -0
- package/dist/app/ui/MediaSlot.d.ts +10 -0
- package/dist/app/ui/MediaSlot.js +20 -0
- package/dist/app/ui/OpenCard.d.ts +18 -0
- package/dist/app/ui/OpenCard.js +20 -0
- package/dist/app/ui/ParagraphEditor.d.ts +8 -0
- package/dist/app/ui/ParagraphEditor.js +37 -0
- package/dist/app/ui/dnd.d.ts +1 -0
- package/dist/app/ui/dnd.js +11 -0
- package/dist/app/ui/icons.d.ts +26 -0
- package/dist/app/ui/icons.js +45 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +26 -0
- package/dist/form/MediaBrowser.d.ts +13 -0
- package/dist/form/MediaBrowser.js +24 -0
- package/dist/form/MediaPicker.d.ts +10 -0
- package/dist/form/MediaPicker.js +20 -0
- package/dist/form/SchemaForm.d.ts +21 -0
- package/dist/form/SchemaForm.js +67 -0
- package/dist/form/Trash.d.ts +1 -0
- package/dist/form/Trash.js +42 -0
- package/dist/form/fields/index.d.ts +1 -0
- package/dist/form/fields/index.js +110 -0
- package/dist/form/fields/objectList.d.ts +1 -0
- package/dist/form/fields/objectList.js +99 -0
- package/dist/form/index.d.ts +8 -0
- package/dist/form/index.js +8 -0
- package/dist/form/media/Lightbox.d.ts +4 -0
- package/dist/form/media/Lightbox.js +70 -0
- package/dist/form/media/MediaFolders.d.ts +20 -0
- package/dist/form/media/MediaFolders.js +116 -0
- package/dist/form/media/MediaGrid.d.ts +8 -0
- package/dist/form/media/MediaGrid.js +139 -0
- package/dist/form/media/UploadDialog.d.ts +4 -0
- package/dist/form/media/UploadDialog.js +37 -0
- package/dist/form/media/useMediaLibrary.d.ts +55 -0
- package/dist/form/media/useMediaLibrary.js +495 -0
- package/dist/form/prose/ProseBar.d.ts +21 -0
- package/dist/form/prose/ProseBar.js +111 -0
- package/dist/form/prose/ProseField.d.ts +11 -0
- package/dist/form/prose/ProseField.js +152 -0
- package/dist/form/prose/pages.d.ts +6 -0
- package/dist/form/prose/pages.js +13 -0
- package/dist/form/registry.d.ts +20 -0
- package/dist/form/registry.js +15 -0
- package/dist/frontmatter.d.ts +20 -0
- package/dist/frontmatter.js +202 -0
- package/dist/functions/_middleware.js +101 -0
- package/dist/functions/_shared/analytics-ga4.js +248 -0
- package/dist/functions/_shared/analytics-umami.js +119 -0
- package/dist/functions/_shared/assets.js +90 -0
- package/dist/functions/_shared/b2.js +300 -0
- package/dist/functions/_shared/delivery-crypto.js +99 -0
- package/dist/functions/_shared/delivery-purge.js +222 -0
- package/dist/functions/_shared/edge-cache.js +37 -0
- package/dist/functions/_shared/exif.js +53 -0
- package/dist/functions/_shared/github.js +138 -0
- package/dist/functions/_shared/imagesize.js +101 -0
- package/dist/functions/_shared/media-content.js +200 -0
- package/dist/functions/_shared/pending-media.js +98 -0
- package/dist/functions/_shared/refs.js +155 -0
- package/dist/functions/api/analytics.js +106 -0
- package/dist/functions/api/assets/delete.js +241 -0
- package/dist/functions/api/assets/folder.js +43 -0
- package/dist/functions/api/assets/folders.js +32 -0
- package/dist/functions/api/assets/move.js +253 -0
- package/dist/functions/api/assets/preview.js +17 -0
- package/dist/functions/api/assets/trash/delete.js +13 -0
- package/dist/functions/api/assets/trash/empty.js +16 -0
- package/dist/functions/api/assets/trash/restore.js +43 -0
- package/dist/functions/api/assets/trash.js +28 -0
- package/dist/functions/api/assets/upload.js +54 -0
- package/dist/functions/api/assets/usage.js +41 -0
- package/dist/functions/api/assets.js +94 -0
- package/dist/functions/api/deliveries.js +629 -0
- package/dist/functions/api/delivery/hero.js +21 -0
- package/dist/functions/api/delivery/image.js +35 -0
- package/dist/functions/api/delivery/multipart.js +261 -0
- package/dist/functions/api/pages/get.js +26 -0
- package/dist/functions/api/pages.js +354 -0
- package/dist/functions/api/publish.js +134 -0
- package/dist/functions/api/requests.js +57 -0
- package/dist/functions/api/settings.js +236 -0
- package/dist/functions/api/singleton.js +105 -0
- package/dist/functions/api/storage.js +125 -0
- package/dist/functions/index.js +124 -0
- package/dist/functions/site.js +38 -0
- package/dist/gallery.d.ts +28 -0
- package/dist/gallery.js +18 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +47 -0
- package/dist/normalize.d.ts +50 -0
- package/dist/normalize.js +120 -0
- package/dist/preview.d.ts +3 -0
- package/dist/preview.js +82 -0
- package/dist/prose.d.ts +17 -0
- package/dist/prose.js +205 -0
- package/dist/registries.d.ts +43 -0
- package/dist/registries.js +29 -0
- package/dist/serialize.d.ts +26 -0
- package/dist/serialize.js +136 -0
- package/dist/testing.d.ts +26 -0
- package/dist/testing.js +66 -0
- package/dist/theme.css +636 -0
- package/dist/yaml.d.ts +8 -0
- package/dist/yaml.js +90 -0
- package/package.json +97 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
const days = ["Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"];
|
|
4
|
+
const statusLabel = {
|
|
5
|
+
pending: "Da approvare", confirming: "Sincronizzo", confirmed: "Confermato",
|
|
6
|
+
rejected: "Rifiutato", cancelled: "Annullato", completed: "Completato",
|
|
7
|
+
no_show: "Non presentato", expired: "Scaduto", anonymized: "Anonimizzato",
|
|
8
|
+
};
|
|
9
|
+
const local = (iso) => new Intl.DateTimeFormat("it-IT", {
|
|
10
|
+
dateStyle: "medium", timeStyle: "short",
|
|
11
|
+
}).format(new Date(iso));
|
|
12
|
+
const time = (minute) => `${String(Math.floor(minute / 60)).padStart(2, "0")}:${String(minute % 60).padStart(2, "0")}`;
|
|
13
|
+
const minute = (value) => {
|
|
14
|
+
const [hours, minutes] = value.split(":").map(Number);
|
|
15
|
+
return hours * 60 + minutes;
|
|
16
|
+
};
|
|
17
|
+
export function BookingsList({ rows, current, onOpen, onAgenda, onSettings, }) {
|
|
18
|
+
return _jsxs("div", { className: "bk-list", children: [_jsxs("button", { className: "col-item" + (!current ? " is-on" : ""), onClick: onAgenda, children: [_jsx("strong", { children: "Agenda" }), _jsx("span", { children: "Tutti gli appuntamenti" })] }), _jsx("div", { className: "bk-list-label", children: "Richieste e prossimi" }), rows.filter((row) => ["pending", "confirming", "confirmed"].includes(row.status)).map((row) => _jsxs("button", { className: "col-item bk-list-item" + (current === row.id ? " is-on" : ""), onClick: () => onOpen(row.id), children: [_jsx("span", { className: `bk-dot is-${row.status}` }), _jsxs("span", { children: [_jsx("strong", { children: row.visitorName || "Dati rimossi" }), _jsxs("small", { children: [local(row.startAt), " \u00B7 ", row.typeName] })] })] }, row.id)), _jsxs("button", { className: "col-item bk-settings-link" + (current === "settings" ? " is-on" : ""), onClick: onSettings, children: [_jsx("strong", { children: "Impostazioni" }), _jsx("span", { children: "Tipologie, orari e Google" })] })] });
|
|
19
|
+
}
|
|
20
|
+
export function BookingAgenda({ store, onOpen }) {
|
|
21
|
+
const [showManual, setShowManual] = useState(false);
|
|
22
|
+
const [showBlock, setShowBlock] = useState(false);
|
|
23
|
+
const [query, setQuery] = useState("");
|
|
24
|
+
const [status, setStatus] = useState("active");
|
|
25
|
+
const [range, setRange] = useState("month");
|
|
26
|
+
const upcoming = useMemo(() => {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
const until = now + (range === "week" ? 7 : 42) * 86_400_000;
|
|
29
|
+
const needle = query.trim().toLowerCase();
|
|
30
|
+
return (store.rows || []).filter((row) => {
|
|
31
|
+
const starts = Date.parse(row.startAt);
|
|
32
|
+
const inRange = range === "past"
|
|
33
|
+
? starts < now && starts >= now - 365 * 86_400_000
|
|
34
|
+
: starts >= now && starts < until;
|
|
35
|
+
const inStatus = status === "all" ? true : status === "active"
|
|
36
|
+
? ["pending", "confirming", "confirmed"].includes(row.status)
|
|
37
|
+
: row.status === status;
|
|
38
|
+
const matches = !needle || [row.visitorName, row.visitorEmail, row.referenceCode, row.typeName]
|
|
39
|
+
.some((value) => String(value || "").toLowerCase().includes(needle));
|
|
40
|
+
return inRange && inStatus && matches;
|
|
41
|
+
});
|
|
42
|
+
}, [query, range, status, store.rows]);
|
|
43
|
+
return _jsxs("div", { className: "bk-page", children: [_jsxs("div", { className: "bk-hero", children: [_jsxs("div", { children: [_jsx("p", { className: "bk-kicker", children: "Agenda" }), _jsxs("h2", { children: [store.pending, " da approvare", _jsx("br", {}), store.upcoming, " in arrivo"] })] }), _jsxs("div", { className: "bk-actions", children: [_jsx("a", { className: "ak-btn", href: "/admin/api/booking/export", children: "Esporta CSV" }), _jsx("button", { className: "ak-btn", onClick: () => setShowBlock((value) => !value), children: "Blocca orario" }), _jsx("button", { className: "ak-btn ak-btn-primary", onClick: () => setShowManual((value) => !value), children: "Nuovo appuntamento" })] })] }), showManual && _jsx(ManualBooking, { store: store, onCreated: (id) => { setShowManual(false); if (id)
|
|
44
|
+
onOpen(id); } }), showBlock && _jsx(ManualBlock, { store: store, onDone: () => setShowBlock(false) }), store.blocks.length > 0 && _jsxs("section", { className: "bk-section", children: [_jsx("h3", { children: "Blocchi manuali" }), _jsx("div", { className: "bk-blocks", children: store.blocks.map((block) => _jsxs("div", { children: [_jsxs("span", { children: [local(block.start_at), " \u2192 ", local(block.end_at), block.note ? ` · ${block.note}` : ""] }), _jsx("button", { onClick: () => void store.removeBlock(block.id), children: "Rimuovi" })] }, block.id)) })] }), _jsxs("section", { className: "bk-section", children: [_jsxs("div", { className: "bk-filter-head", children: [_jsx("h3", { children: "Prossimi appuntamenti" }), _jsxs("div", { className: "bk-filters", children: [_jsx("input", { type: "search", placeholder: "Cerca nome, email o codice", value: query, onChange: (e) => setQuery(e.target.value) }), _jsxs("select", { value: status, onChange: (e) => setStatus(e.target.value), children: [_jsx("option", { value: "active", children: "Attivi" }), _jsx("option", { value: "pending", children: "Da approvare" }), _jsx("option", { value: "confirmed", children: "Confermati" }), _jsx("option", { value: "all", children: "Tutti gli stati" })] }), _jsxs("select", { value: range, onChange: (e) => setRange(e.target.value), children: [_jsx("option", { value: "week", children: "7 giorni" }), _jsx("option", { value: "month", children: "6 settimane" }), _jsx("option", { value: "past", children: "Ultimi 12 mesi" })] })] })] }), upcoming.length ? _jsx("div", { className: "bk-table", children: upcoming.map((row) => _jsxs("button", { onClick: () => onOpen(row.id), children: [_jsx("time", { children: local(row.startAt) }), _jsx("strong", { children: row.visitorName }), _jsx("span", { children: row.typeName }), _jsx("em", { className: `bk-status is-${row.status}`, children: statusLabel[row.status] })] }, row.id)) }) : _jsx("p", { className: "ak-hint", children: "Nessun appuntamento in arrivo." })] })] });
|
|
45
|
+
}
|
|
46
|
+
function ManualBlock({ store, onDone }) {
|
|
47
|
+
const [value, setValue] = useState({ startAt: "", endAt: "", note: "" });
|
|
48
|
+
const submit = async (event) => {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
await store.addBlock({
|
|
51
|
+
startAt: new Date(value.startAt).toISOString(),
|
|
52
|
+
endAt: new Date(value.endAt).toISOString(),
|
|
53
|
+
note: value.note,
|
|
54
|
+
}).then(onDone).catch(() => undefined);
|
|
55
|
+
};
|
|
56
|
+
return _jsxs("form", { className: "bk-manual", onSubmit: submit, children: [_jsx("h3", { children: "Blocca una fascia" }), _jsxs("label", { children: ["Da", _jsx("input", { type: "datetime-local", required: true, value: value.startAt, onChange: (e) => setValue({ ...value, startAt: e.target.value }) })] }), _jsxs("label", { children: ["A", _jsx("input", { type: "datetime-local", required: true, value: value.endAt, onChange: (e) => setValue({ ...value, endAt: e.target.value }) })] }), _jsxs("label", { children: ["Motivo", _jsx("input", { value: value.note, onChange: (e) => setValue({ ...value, note: e.target.value }) })] }), _jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy, children: "Aggiungi blocco" })] });
|
|
57
|
+
}
|
|
58
|
+
function ManualBooking({ store, onCreated }) {
|
|
59
|
+
const [value, setValue] = useState({
|
|
60
|
+
appointmentType: store.types[0]?.slug || "", mode: "in_person",
|
|
61
|
+
startAt: "", name: "", email: "", phone: "", notes: "",
|
|
62
|
+
});
|
|
63
|
+
const submit = async (event) => {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
const id = await store.createManual({ ...value, startAt: new Date(value.startAt).toISOString() }).catch(() => null);
|
|
66
|
+
onCreated(id);
|
|
67
|
+
};
|
|
68
|
+
return _jsxs("form", { className: "bk-manual", onSubmit: submit, children: [_jsx("h3", { children: "Inserisci un appuntamento" }), _jsxs("label", { children: ["Tipologia", _jsx("select", { value: value.appointmentType, onChange: (e) => setValue({ ...value, appointmentType: e.target.value }), children: store.types.filter((type) => type.active).map((type) => _jsx("option", { value: type.slug, children: type.name }, type.id)) })] }), _jsxs("label", { children: ["Modalit\u00E0", _jsxs("select", { value: value.mode, onChange: (e) => setValue({ ...value, mode: e.target.value }), children: [_jsx("option", { value: "in_person", children: "In presenza" }), _jsx("option", { value: "virtual", children: "Virtuale" })] })] }), _jsxs("label", { children: ["Data e ora", _jsx("input", { type: "datetime-local", required: true, value: value.startAt, onChange: (e) => setValue({ ...value, startAt: e.target.value }) })] }), _jsxs("label", { children: ["Nome", _jsx("input", { required: true, value: value.name, onChange: (e) => setValue({ ...value, name: e.target.value }) })] }), _jsxs("label", { children: ["Email", _jsx("input", { type: "email", required: true, value: value.email, onChange: (e) => setValue({ ...value, email: e.target.value }) })] }), _jsxs("label", { children: ["Telefono", _jsx("input", { value: value.phone, onChange: (e) => setValue({ ...value, phone: e.target.value }) })] }), _jsxs("label", { className: "is-wide", children: ["Note", _jsx("textarea", { value: value.notes, onChange: (e) => setValue({ ...value, notes: e.target.value }) })] }), _jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy, children: "Salva come richiesta" })] });
|
|
69
|
+
}
|
|
70
|
+
export function BookingDetail({ row, store }) {
|
|
71
|
+
const [notes, setNotes] = useState(row.adminNotes || "");
|
|
72
|
+
const [newStart, setNewStart] = useState("");
|
|
73
|
+
useEffect(() => setNotes(row.adminNotes || ""), [row.id, row.adminNotes]);
|
|
74
|
+
const place = row.mode === "virtual" ? (row.meetUrl || "Google Meet dopo l’approvazione") : row.location;
|
|
75
|
+
return _jsxs("div", { className: "bk-page bk-detail", children: [_jsxs("div", { className: "bk-detail-head", children: [_jsxs("div", { children: [_jsx("p", { className: "bk-kicker", children: row.referenceCode }), _jsx("h2", { children: row.visitorName || "Dati rimossi" })] }), _jsx("span", { className: `bk-status is-${row.status}`, children: statusLabel[row.status] })] }), _jsxs("dl", { className: "bk-facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Quando" }), _jsxs("dd", { children: [local(row.startAt), " \u00B7 ", row.durationMinutes, " min"] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Tipologia" }), _jsx("dd", { children: row.typeName })] }), _jsxs("div", { children: [_jsx("dt", { children: "Modalit\u00E0" }), _jsxs("dd", { children: [row.mode === "virtual" ? "Videochiamata" : "In presenza", " \u00B7 ", place] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Contatto" }), _jsxs("dd", { children: [_jsx("a", { href: `mailto:${row.visitorEmail}`, children: row.visitorEmail }), row.visitorPhone ? ` · ${row.visitorPhone}` : ""] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Fuso visitatore" }), _jsx("dd", { children: row.visitorTimezone })] }), _jsxs("div", { children: [_jsx("dt", { children: "Sincronizzazione" }), _jsxs("dd", { children: [row.calendarSyncStatus, row.googleEventUrl ? _jsxs(_Fragment, { children: [" \u00B7 ", _jsx("a", { href: row.googleEventUrl, target: "_blank", children: "Apri evento" })] }) : null] })] })] }), row.visitorNotes && _jsxs("section", { className: "bk-note", children: [_jsx("h3", { children: "Note del visitatore" }), _jsx("p", { children: row.visitorNotes })] }), _jsxs("section", { className: "bk-note", children: [_jsx("h3", { children: "Note interne" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 4 }), _jsx("button", { className: "ak-btn", disabled: store.busy, onClick: () => void store.action(row.id, { action: "notes", notes }), children: "Salva note" })] }), ["pending", "confirmed"].includes(row.status) && _jsxs("section", { className: "bk-reschedule", children: [_jsx("h3", { children: "Sposta appuntamento" }), _jsx("input", { type: "datetime-local", value: newStart, onChange: (e) => setNewStart(e.target.value) }), _jsx("button", { className: "ak-btn", disabled: !newStart || store.busy, onClick: () => void store.action(row.id, { action: "reschedule", startAt: new Date(newStart).toISOString() }), children: "Aggiorna data" })] }), _jsxs("div", { className: "bk-actions bk-detail-actions", children: [row.status === "pending" && _jsxs(_Fragment, { children: [_jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy, onClick: () => void store.action(row.id, { action: "approve" }), children: "Approva e crea evento" }), _jsx("button", { className: "ak-btn is-danger", disabled: store.busy, onClick: () => void store.action(row.id, { action: "reject" }), children: "Rifiuta" })] }), row.status === "confirmed" && _jsxs(_Fragment, { children: [_jsx("button", { className: "ak-btn", disabled: store.busy, onClick: () => void store.action(row.id, { action: "complete" }), children: "Completato" }), _jsx("button", { className: "ak-btn", disabled: store.busy, onClick: () => void store.action(row.id, { action: "no_show" }), children: "Non presentato" }), _jsx("button", { className: "ak-btn is-danger", disabled: store.busy, onClick: () => void store.action(row.id, { action: "cancel" }), children: "Annulla" })] })] })] });
|
|
76
|
+
}
|
|
77
|
+
export function BookingSettingsHub({ store }) {
|
|
78
|
+
const [tab, setTab] = useState("general");
|
|
79
|
+
const tabs = [["general", "Generali"], ["types", "Tipologie"], ["availability", "Disponibilità"], ["google", "Google"], ["publish", "Attivazione"]];
|
|
80
|
+
return _jsxs("div", { className: "bk-page", children: [_jsx("div", { className: "bk-tabs", children: tabs.map(([id, label]) => _jsx("button", { className: tab === id ? "is-on" : "", onClick: () => setTab(id), children: label }, id)) }), tab === "general" && store.settings && _jsx(GeneralSettings, { value: store.settings, store: store }), tab === "types" && _jsx(TypesSettings, { types: store.types, store: store }), tab === "availability" && _jsx(AvailabilitySettings, { store: store }), tab === "google" && _jsx(GoogleSettings, { store: store }), tab === "publish" && _jsx(ActivationSettings, { store: store })] });
|
|
81
|
+
}
|
|
82
|
+
function GeneralSettings({ value, store }) {
|
|
83
|
+
const [draft, setDraft] = useState(value);
|
|
84
|
+
useEffect(() => setDraft(value), [value]);
|
|
85
|
+
const number = (key, value) => setDraft({ ...draft, [key]: Number(value) });
|
|
86
|
+
return _jsxs("form", { className: "bk-settings-form", onSubmit: (e) => { e.preventDefault(); void store.saveSettings(draft); }, children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Regole globali" }), _jsx("h2", { children: "Il perimetro dell\u2019agenda." })] }), _jsxs("label", { children: ["Fuso del proprietario", _jsx("input", { value: draft.timezone, onChange: (e) => setDraft({ ...draft, timezone: e.target.value }) })] }), _jsxs("label", { className: "is-wide", children: ["Indirizzo unico", _jsx("input", { value: draft.address, onChange: (e) => setDraft({ ...draft, address: e.target.value }) })] }), _jsxs("label", { children: ["Email proprietario", _jsx("input", { type: "email", value: draft.contactEmail, onChange: (e) => setDraft({ ...draft, contactEmail: e.target.value }) })] }), _jsxs("label", { children: ["Telefono proprietario", _jsx("input", { value: draft.contactPhone, onChange: (e) => setDraft({ ...draft, contactPhone: e.target.value }) })] }), _jsx(NumberField, { label: "Durata predefinita (min)", value: draft.defaultDurationMinutes, onChange: (v) => number("defaultDurationMinutes", v) }), _jsx(NumberField, { label: "Buffer prima (min)", value: draft.defaultBufferBeforeMinutes, onChange: (v) => number("defaultBufferBeforeMinutes", v) }), _jsx(NumberField, { label: "Buffer dopo (min)", value: draft.defaultBufferAfterMinutes, onChange: (v) => number("defaultBufferAfterMinutes", v) }), _jsx(NumberField, { label: "Passo slot (min)", value: draft.slotStepMinutes, onChange: (v) => number("slotStepMinutes", v) }), _jsx(NumberField, { label: "Preavviso minimo (min)", value: draft.minimumNoticeMinutes, onChange: (v) => number("minimumNoticeMinutes", v) }), _jsx(NumberField, { label: "Finestra massima (giorni)", value: draft.bookingHorizonDays, onChange: (v) => number("bookingHorizonDays", v) }), _jsx(NumberField, { label: "Limite al giorno (0 = nessuno)", value: draft.dailyLimit, onChange: (v) => number("dailyLimit", v) }), _jsx(NumberField, { label: "Limite alla settimana (0 = nessuno)", value: draft.weeklyLimit, onChange: (v) => number("weeklyLimit", v) }), _jsx(NumberField, { label: "Primo promemoria (min)", value: draft.reminder1Minutes, onChange: (v) => number("reminder1Minutes", v) }), _jsx(NumberField, { label: "Secondo promemoria (min)", value: draft.reminder2Minutes, onChange: (v) => number("reminder2Minutes", v) }), _jsx(NumberField, { label: "Retention (giorni)", value: draft.retentionDays, onChange: (v) => number("retentionDays", v) }), _jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy, children: "Salva impostazioni" })] });
|
|
87
|
+
}
|
|
88
|
+
function NumberField({ label, value, onChange }) {
|
|
89
|
+
return _jsxs("label", { children: [label, _jsx("input", { type: "number", min: "0", value: value, onChange: (e) => onChange(e.target.value) })] });
|
|
90
|
+
}
|
|
91
|
+
function emptyType(order) {
|
|
92
|
+
return { slug: "", name: "", description: "", active: true, durationMinutes: null, bufferBeforeMinutes: null, bufferAfterMinutes: null, modePolicy: "both", sortOrder: order, adminColor: "#e63329" };
|
|
93
|
+
}
|
|
94
|
+
function TypesSettings({ types, store }) {
|
|
95
|
+
const [draft, setDraft] = useState(types);
|
|
96
|
+
useEffect(() => setDraft(types), [types]);
|
|
97
|
+
return _jsxs("section", { className: "bk-settings-section", children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Offerta" }), _jsx("h2", { children: "Tipologie di appuntamento." })] }), _jsx("div", { className: "bk-types", children: draft.map((type, index) => _jsxs("div", { className: "bk-type", children: [_jsx("input", { className: "bk-type-name", placeholder: "Nome", value: type.name || "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, name: e.target.value } : v)) }), _jsx("input", { placeholder: "slug-pubblico", value: type.slug || "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, slug: e.target.value } : v)) }), _jsx("textarea", { placeholder: "Descrizione", value: type.description || "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, description: e.target.value } : v)) }), _jsxs("label", { children: ["Modalit\u00E0", _jsxs("select", { value: type.modePolicy, onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, modePolicy: e.target.value } : v)), children: [_jsx("option", { value: "both", children: "Scelta del visitatore" }), _jsx("option", { value: "in_person", children: "Solo in presenza" }), _jsx("option", { value: "virtual", children: "Solo virtuale" })] })] }), _jsxs("label", { children: ["Durata, vuoto = default", _jsx("input", { type: "number", value: type.durationMinutes ?? "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, durationMinutes: e.target.value ? Number(e.target.value) : null } : v)) })] }), _jsxs("label", { children: ["Buffer prima", _jsx("input", { type: "number", value: type.bufferBeforeMinutes ?? "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, bufferBeforeMinutes: e.target.value ? Number(e.target.value) : null } : v)) })] }), _jsxs("label", { children: ["Buffer dopo", _jsx("input", { type: "number", value: type.bufferAfterMinutes ?? "", onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, bufferAfterMinutes: e.target.value ? Number(e.target.value) : null } : v)) })] }), _jsxs("label", { className: "bk-check", children: [_jsx("input", { type: "checkbox", checked: type.active !== false, onChange: (e) => setDraft(draft.map((v, i) => i === index ? { ...v, active: e.target.checked } : v)) }), " Attiva"] }), _jsxs("div", { className: "bk-actions", children: [_jsx("button", { className: "ak-btn ak-btn-primary", onClick: () => void store.saveType(type, !type.id), children: "Salva" }), type.id && _jsx("button", { className: "ak-btn is-danger", onClick: () => void store.removeType(type.id), children: "Archivia" })] })] }, type.id || `new-${index}`)) }), _jsx("button", { className: "ak-btn", onClick: () => setDraft([...draft, emptyType(draft.length)]), children: "Aggiungi tipologia" })] });
|
|
98
|
+
}
|
|
99
|
+
function AvailabilitySettings({ store }) {
|
|
100
|
+
const [weekly, setWeekly] = useState(store.weekly);
|
|
101
|
+
const [overrides, setOverrides] = useState(store.overrides);
|
|
102
|
+
useEffect(() => { setWeekly(store.weekly); setOverrides(store.overrides); }, [store.weekly, store.overrides]);
|
|
103
|
+
const addWindow = (weekday) => setWeekly([...weekly, { weekday, startMinute: 9 * 60, endMinute: 13 * 60 }]);
|
|
104
|
+
return _jsxs("section", { className: "bk-settings-section", children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Disponibilit\u00E0 positiva" }), _jsx("h2", { children: "Quando si pu\u00F2 prenotare." })] }), _jsx("div", { className: "bk-week", children: days.map((label, day) => {
|
|
105
|
+
const weekday = day + 1;
|
|
106
|
+
return _jsxs("div", { children: [_jsx("strong", { children: label }), _jsxs("div", { children: [weekly.filter((window) => window.weekday === weekday).map((window, index) => {
|
|
107
|
+
const actual = weekly.indexOf(window);
|
|
108
|
+
return _jsxs("span", { children: [_jsx("input", { type: "time", value: time(window.startMinute), onChange: (e) => setWeekly(weekly.map((value, i) => i === actual ? { ...value, startMinute: minute(e.target.value) } : value)) }), _jsx("span", { children: "\u2014" }), _jsx("input", { type: "time", value: time(window.endMinute), onChange: (e) => setWeekly(weekly.map((value, i) => i === actual ? { ...value, endMinute: minute(e.target.value) } : value)) }), _jsx("button", { "aria-label": "Rimuovi fascia", onClick: () => setWeekly(weekly.filter((_, i) => i !== actual)), children: "\u00D7" })] }, `${window.startMinute}-${index}`);
|
|
109
|
+
}), _jsx("button", { onClick: () => addWindow(weekday), children: "+ fascia" })] })] }, weekday);
|
|
110
|
+
}) }), _jsx("h3", { children: "Eccezioni per data" }), _jsx("div", { className: "bk-overrides", children: overrides.map((override, index) => _jsxs("div", { children: [_jsx("input", { type: "date", value: override.date, onChange: (e) => setOverrides(overrides.map((v, i) => i === index ? { ...v, date: e.target.value } : v)) }), _jsxs("select", { value: override.kind, onChange: (e) => setOverrides(overrides.map((v, i) => i === index ? { ...v, kind: e.target.value, windows: e.target.value === "closed" ? [] : (v.windows.length ? v.windows : [{ startMinute: 9 * 60, endMinute: 13 * 60 }]) } : v)), children: [_jsx("option", { value: "closed", children: "Non disponibile" }), _jsx("option", { value: "custom", children: "Orari speciali" })] }), override.kind === "custom" && _jsxs(_Fragment, { children: [_jsx("input", { type: "time", value: time(override.windows[0]?.startMinute ?? 540), onChange: (e) => setOverrides(overrides.map((v, i) => i === index ? { ...v, windows: [{ startMinute: minute(e.target.value), endMinute: v.windows[0]?.endMinute ?? 780 }] } : v)) }), _jsx("input", { type: "time", value: time(override.windows[0]?.endMinute ?? 780), onChange: (e) => setOverrides(overrides.map((v, i) => i === index ? { ...v, windows: [{ startMinute: v.windows[0]?.startMinute ?? 540, endMinute: minute(e.target.value) }] } : v)) })] }), _jsx("input", { placeholder: "Nota", value: override.note || "", onChange: (e) => setOverrides(overrides.map((v, i) => i === index ? { ...v, note: e.target.value } : v)) }), _jsx("button", { onClick: () => setOverrides(overrides.filter((_, i) => i !== index)), children: "Rimuovi" })] }, override.id || index)) }), _jsxs("div", { className: "bk-actions", children: [_jsx("button", { className: "ak-btn", onClick: () => setOverrides([...overrides, { date: "", kind: "closed", windows: [] }]), children: "Aggiungi eccezione" }), _jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy, onClick: () => void store.saveAvailability(weekly, overrides), children: "Salva disponibilit\u00E0" })] })] });
|
|
111
|
+
}
|
|
112
|
+
function GoogleSettings({ store }) {
|
|
113
|
+
const [busy, setBusy] = useState(store.google.calendars.filter((calendar) => calendar.busy).map((calendar) => calendar.id));
|
|
114
|
+
const [target, setTarget] = useState(store.google.calendars.find((calendar) => calendar.booking)?.id || "");
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
setBusy(store.google.calendars.filter((calendar) => calendar.busy).map((calendar) => calendar.id));
|
|
117
|
+
setTarget(store.google.calendars.find((calendar) => calendar.booking)?.id || "");
|
|
118
|
+
}, [store.google]);
|
|
119
|
+
if (!store.google.connected)
|
|
120
|
+
return _jsxs("section", { className: "bk-settings-section", children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Sincronizzazione" }), _jsx("h2", { children: "Collega Google Calendar." })] }), _jsx("p", { children: "Leggeremo gli impegni dai calendari scelti e creeremo gli eventi, con Google Meet quando necessario, solo dopo l\u2019approvazione." }), _jsx("a", { className: "ak-btn ak-btn-primary", href: "/admin/api/booking/google/connect", children: "Collega account Google" })] });
|
|
121
|
+
return _jsxs("section", { className: "bk-settings-section", children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Account collegato" }), _jsx("h2", { children: store.google.connection?.account_email })] }), _jsxs("p", { children: ["Stato token: ", store.google.connection?.token_status] }), _jsx("h3", { children: "Calendari che rendono occupato" }), _jsx("div", { className: "bk-google-list", children: store.google.calendars.map((calendar) => _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: busy.includes(calendar.id), onChange: (e) => setBusy(e.target.checked ? [...busy, calendar.id] : busy.filter((id) => id !== calendar.id)) }), _jsxs("span", { children: [calendar.summary, calendar.primary ? " · principale" : ""] })] }, calendar.id)) }), _jsxs("label", { className: "bk-target", children: ["Calendario in cui creare gli appuntamenti", _jsxs("select", { value: target, onChange: (e) => setTarget(e.target.value), children: [_jsx("option", { value: "", children: "Scegli\u2026" }), store.google.calendars.filter((calendar) => calendar.accessRole === "owner").map((calendar) => _jsx("option", { value: calendar.id, children: calendar.summary }, calendar.id))] })] }), _jsxs("div", { className: "bk-actions", children: [_jsx("button", { className: "ak-btn ak-btn-primary", disabled: !target || !busy.length || store.busy, onClick: () => void store.saveGoogle(busy, target), children: "Salva calendari" }), _jsx("button", { className: "ak-btn", onClick: () => void store.refreshGoogle(), children: "Aggiorna elenco" }), _jsx("button", { className: "ak-btn is-danger", onClick: () => void store.disconnectGoogle(), children: "Scollega" })] })] });
|
|
122
|
+
}
|
|
123
|
+
function ActivationSettings({ store }) {
|
|
124
|
+
const labels = {
|
|
125
|
+
contact: "Email proprietario", address: "Indirizzo per incontri in presenza",
|
|
126
|
+
turnstile: "Turnstile configurato", rateLimit: "Sale anti-abuso",
|
|
127
|
+
email: "Invio email configurato", googleOAuth: "Credenziali OAuth Google",
|
|
128
|
+
type: "Almeno una tipologia",
|
|
129
|
+
availability: "Disponibilità ricorrente", google: "Account Google",
|
|
130
|
+
targetCalendar: "Calendario di destinazione", meet: "Google Meet disponibile",
|
|
131
|
+
};
|
|
132
|
+
const state = store.activation;
|
|
133
|
+
return _jsxs("section", { className: "bk-settings-section", children: [_jsxs("header", { children: [_jsx("p", { className: "bk-kicker", children: "Pubblicazione" }), _jsx("h2", { children: state?.enabled ? "Prenotazioni attive." : "Prenotazioni non pubbliche." })] }), _jsxs("p", { children: ["Quando la funzione \u00E8 spenta, ", _jsx("code", { children: "/prenota/" }), ", le API pubbliche e le voci di menu rispondono come se non esistessero."] }), _jsx("div", { className: "bk-checklist", children: Object.entries(state?.checks || {}).map(([id, ready]) => _jsxs("div", { className: ready ? "is-ready" : "", children: [_jsx("span", { children: ready ? "✓" : "—" }), labels[id] || id] }, id)) }), _jsx("button", { className: "ak-btn ak-btn-primary", disabled: store.busy || (!state?.enabled && !state?.ready), onClick: () => void store.setEnabled(!state?.enabled), children: state?.enabled ? "Disattiva prenotazioni" : "Attiva e pubblica" })] });
|
|
134
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Delivery, DeliverySettings as DeliverySettingsModel, DeliverySummary } from "../lib/deliveries";
|
|
2
|
+
type DeliveryAction = <T>(payload: Record<string, unknown>, options?: {
|
|
3
|
+
reload?: boolean;
|
|
4
|
+
}) => Promise<T>;
|
|
5
|
+
export declare function NewDeliveryButton({ onCreate, disabled }: {
|
|
6
|
+
onCreate: () => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export declare function DeliveriesList({ rows, current, disabled, onOpen }: {
|
|
10
|
+
rows: DeliverySummary[];
|
|
11
|
+
current?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onOpen: (id: string) => void;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
export declare function DeliveriesTotal({ rows }: {
|
|
16
|
+
rows: DeliverySummary[];
|
|
17
|
+
}): import("react").JSX.Element | null;
|
|
18
|
+
interface WorkspaceProps {
|
|
19
|
+
delivery: Delivery | null;
|
|
20
|
+
busy: boolean;
|
|
21
|
+
onAction: DeliveryAction;
|
|
22
|
+
onRefresh: () => Promise<void>;
|
|
23
|
+
onReload: () => Promise<void>;
|
|
24
|
+
onRead: (id: string) => Promise<Delivery | null>;
|
|
25
|
+
onDeleted: () => void;
|
|
26
|
+
onOpen: (id: string) => void;
|
|
27
|
+
onSelected: (id: string) => void;
|
|
28
|
+
}
|
|
29
|
+
export declare function DeliveryWorkspace({ delivery: fromServer, busy, onAction, onRefresh, onReload, onRead, onDeleted, onOpen, onSelected, }: WorkspaceProps): import("react").JSX.Element;
|
|
30
|
+
export declare function DeliverySettings({ settings, onAction, pickMedia }: {
|
|
31
|
+
settings: DeliverySettingsModel;
|
|
32
|
+
onAction: DeliveryAction;
|
|
33
|
+
pickMedia: (options: {
|
|
34
|
+
multiple: boolean;
|
|
35
|
+
}) => Promise<string[] | null>;
|
|
36
|
+
}): import("react").JSX.Element;
|
|
37
|
+
export {};
|