@sevenfold/setto-client 0.8.5 → 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/dist/chrome.d.ts +36 -0
- package/dist/index.d.ts +3 -1
- package/dist/modules/domain/DomainView.d.ts +3 -0
- package/dist/modules/domain/index.d.ts +11 -0
- package/dist/setto-client.js +544 -255
- package/dist/setto-client.js.map +1 -1
- package/package.json +1 -1
package/dist/setto-client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
2
3
|
import { useState, useRef, useEffect, useSyncExternalStore, useLayoutEffect, createContext, useContext, useCallback, useMemo, useId, useInsertionEffect, forwardRef, Fragment as Fragment$1 } from "react";
|
|
3
4
|
import { useTranslation } from "react-i18next";
|
|
5
|
+
import * as ReactDOM from "react-dom";
|
|
4
6
|
import { createPortal } from "react-dom";
|
|
5
7
|
function __rest(s, e) {
|
|
6
8
|
var t = {};
|
|
@@ -5039,26 +5041,26 @@ var PostgrestClient = class PostgrestClient2 {
|
|
|
5039
5041
|
this.schemaName = schema;
|
|
5040
5042
|
this.urlLengthLimit = urlLengthLimit;
|
|
5041
5043
|
const originalFetch = fetch$1 !== null && fetch$1 !== void 0 ? fetch$1 : globalThis.fetch;
|
|
5042
|
-
if (timeout !== void 0 && timeout > 0) this.fetch = (
|
|
5044
|
+
if (timeout !== void 0 && timeout > 0) this.fetch = (input2, init) => {
|
|
5043
5045
|
const controller = new AbortController();
|
|
5044
5046
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
5045
5047
|
const existingSignal = init === null || init === void 0 ? void 0 : init.signal;
|
|
5046
5048
|
if (existingSignal) {
|
|
5047
5049
|
if (existingSignal.aborted) {
|
|
5048
5050
|
clearTimeout(timeoutId);
|
|
5049
|
-
return originalFetch(
|
|
5051
|
+
return originalFetch(input2, init);
|
|
5050
5052
|
}
|
|
5051
5053
|
const abortHandler = () => {
|
|
5052
5054
|
clearTimeout(timeoutId);
|
|
5053
5055
|
controller.abort();
|
|
5054
5056
|
};
|
|
5055
5057
|
existingSignal.addEventListener("abort", abortHandler, { once: true });
|
|
5056
|
-
return originalFetch(
|
|
5058
|
+
return originalFetch(input2, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => {
|
|
5057
5059
|
clearTimeout(timeoutId);
|
|
5058
5060
|
existingSignal.removeEventListener("abort", abortHandler);
|
|
5059
5061
|
});
|
|
5060
5062
|
}
|
|
5061
|
-
return originalFetch(
|
|
5063
|
+
return originalFetch(input2, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => clearTimeout(timeoutId));
|
|
5062
5064
|
};
|
|
5063
5065
|
else this.fetch = originalFetch;
|
|
5064
5066
|
this.retry = retry;
|
|
@@ -20182,25 +20184,25 @@ const fetchWithAuth = (supabaseKey, supabaseUrl, getAccessToken, customFetch, tr
|
|
|
20182
20184
|
const traceEnabled = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.enabled) === true;
|
|
20183
20185
|
const respectSampling = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.respectSamplingDecision) !== false;
|
|
20184
20186
|
const traceTargets = traceEnabled ? getDefaultPropagationTargets(supabaseUrl) : null;
|
|
20185
|
-
return async (
|
|
20187
|
+
return async (input2, init) => {
|
|
20186
20188
|
var _await$getAccessToken;
|
|
20187
20189
|
const accessToken = (_await$getAccessToken = await getAccessToken()) !== null && _await$getAccessToken !== void 0 ? _await$getAccessToken : supabaseKey;
|
|
20188
20190
|
let headers = new HeadersConstructor(init === null || init === void 0 ? void 0 : init.headers);
|
|
20189
20191
|
if (!headers.has("apikey")) headers.set("apikey", supabaseKey);
|
|
20190
20192
|
if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${accessToken}`);
|
|
20191
20193
|
if (traceTargets) {
|
|
20192
|
-
const traceHeaders = await getTraceHeaders(
|
|
20194
|
+
const traceHeaders = await getTraceHeaders(input2, traceTargets, respectSampling);
|
|
20193
20195
|
if (traceHeaders) {
|
|
20194
20196
|
if (traceHeaders.traceparent && !headers.has("traceparent")) headers.set("traceparent", traceHeaders.traceparent);
|
|
20195
20197
|
if (traceHeaders.tracestate && !headers.has("tracestate")) headers.set("tracestate", traceHeaders.tracestate);
|
|
20196
20198
|
if (traceHeaders.baggage && !headers.has("baggage")) headers.set("baggage", traceHeaders.baggage);
|
|
20197
20199
|
}
|
|
20198
20200
|
}
|
|
20199
|
-
return fetch$1(
|
|
20201
|
+
return fetch$1(input2, _objectSpread2(_objectSpread2({}, init), {}, { headers }));
|
|
20200
20202
|
};
|
|
20201
20203
|
};
|
|
20202
|
-
async function getTraceHeaders(
|
|
20203
|
-
if (!shouldPropagateToTarget(typeof
|
|
20204
|
+
async function getTraceHeaders(input2, targets, respectSampling) {
|
|
20205
|
+
if (!shouldPropagateToTarget(typeof input2 === "string" ? input2 : input2 instanceof URL ? input2 : input2.url, targets)) return null;
|
|
20204
20206
|
const traceContext = await extractTraceContext();
|
|
20205
20207
|
if (!traceContext || !traceContext.traceparent) return null;
|
|
20206
20208
|
if (respectSampling) {
|
|
@@ -21310,14 +21312,14 @@ class ThemeStore {
|
|
|
21310
21312
|
for (const fn of this.listeners) fn(this.cachedSnapshot);
|
|
21311
21313
|
}
|
|
21312
21314
|
}
|
|
21313
|
-
const EMPTY = {
|
|
21315
|
+
const EMPTY$1 = {
|
|
21314
21316
|
sites: null,
|
|
21315
21317
|
currentSite: null,
|
|
21316
21318
|
loading: true,
|
|
21317
21319
|
error: null
|
|
21318
21320
|
};
|
|
21319
21321
|
function useSiteAccess(supabase, session, siteId) {
|
|
21320
|
-
const [state, setState] = useState(EMPTY);
|
|
21322
|
+
const [state, setState] = useState(EMPTY$1);
|
|
21321
21323
|
const cachedUserIdRef = useRef(null);
|
|
21322
21324
|
useEffect(() => {
|
|
21323
21325
|
if (!session) {
|
|
@@ -21358,6 +21360,71 @@ function useSiteAccess(supabase, session, siteId) {
|
|
|
21358
21360
|
}, [supabase, session, siteId]);
|
|
21359
21361
|
return state;
|
|
21360
21362
|
}
|
|
21363
|
+
const CHROME_CONTRACT = 1;
|
|
21364
|
+
function chromeUrl(apiUrl) {
|
|
21365
|
+
return `${apiUrl.replace(/\/$/, "")}/chrome/v${CHROME_CONTRACT}.js`;
|
|
21366
|
+
}
|
|
21367
|
+
function SettoChrome() {
|
|
21368
|
+
const setto = useSetto();
|
|
21369
|
+
const listeners = useRef(/* @__PURE__ */ new Set());
|
|
21370
|
+
const stateRef = useRef(() => ({
|
|
21371
|
+
hasSession: false,
|
|
21372
|
+
accessToken: null,
|
|
21373
|
+
currentSite: null,
|
|
21374
|
+
sitesLoaded: false,
|
|
21375
|
+
editMode: false,
|
|
21376
|
+
authLoading: true
|
|
21377
|
+
}));
|
|
21378
|
+
stateRef.current = () => ({
|
|
21379
|
+
hasSession: !!setto.session,
|
|
21380
|
+
accessToken: setto.session?.access_token ?? null,
|
|
21381
|
+
currentSite: setto.currentSite ? {
|
|
21382
|
+
id: setto.currentSite.id,
|
|
21383
|
+
display_name: setto.currentSite.display_name,
|
|
21384
|
+
status: setto.currentSite.status
|
|
21385
|
+
} : null,
|
|
21386
|
+
sitesLoaded: setto.sites !== null,
|
|
21387
|
+
editMode: setto.editMode,
|
|
21388
|
+
authLoading: setto.authLoading
|
|
21389
|
+
});
|
|
21390
|
+
useEffect(() => {
|
|
21391
|
+
for (const listener of listeners.current) {
|
|
21392
|
+
try {
|
|
21393
|
+
listener();
|
|
21394
|
+
} catch {
|
|
21395
|
+
}
|
|
21396
|
+
}
|
|
21397
|
+
});
|
|
21398
|
+
useEffect(() => {
|
|
21399
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
21400
|
+
if (window.__settoChromeLoaded__) return;
|
|
21401
|
+
try {
|
|
21402
|
+
window.__setto__ = {
|
|
21403
|
+
contract: CHROME_CONTRACT,
|
|
21404
|
+
react: React,
|
|
21405
|
+
reactDom: ReactDOM,
|
|
21406
|
+
config: {
|
|
21407
|
+
siteId: setto.config.siteId,
|
|
21408
|
+
apiUrl: setto.config.apiUrl,
|
|
21409
|
+
displayName: setto.config.displayName
|
|
21410
|
+
},
|
|
21411
|
+
getState: () => stateRef.current(),
|
|
21412
|
+
subscribe: (listener) => {
|
|
21413
|
+
listeners.current.add(listener);
|
|
21414
|
+
return () => listeners.current.delete(listener);
|
|
21415
|
+
}
|
|
21416
|
+
};
|
|
21417
|
+
const script = document.createElement("script");
|
|
21418
|
+
script.src = chromeUrl(setto.config.apiUrl);
|
|
21419
|
+
script.async = true;
|
|
21420
|
+
script.onerror = () => void 0;
|
|
21421
|
+
document.head.appendChild(script);
|
|
21422
|
+
window.__settoChromeLoaded__ = true;
|
|
21423
|
+
} catch {
|
|
21424
|
+
}
|
|
21425
|
+
}, []);
|
|
21426
|
+
return null;
|
|
21427
|
+
}
|
|
21361
21428
|
const SETTO_BASE = "/setto";
|
|
21362
21429
|
function siteHomeUrl(pathname = "/") {
|
|
21363
21430
|
const u = new URL(window.location.href);
|
|
@@ -21392,202 +21459,6 @@ function withoutCheckoutIntent() {
|
|
|
21392
21459
|
u.searchParams.delete(CHECKOUT_INTENT);
|
|
21393
21460
|
return u.pathname + (u.search || "") + u.hash;
|
|
21394
21461
|
}
|
|
21395
|
-
const PREVIEW_HOST = /(^|\.)site\.setto\.no$/i;
|
|
21396
|
-
function isPreviewHost(hostname) {
|
|
21397
|
-
return PREVIEW_HOST.test(hostname);
|
|
21398
|
-
}
|
|
21399
|
-
function previewBarState(input) {
|
|
21400
|
-
if (!input.onPreview || input.authLoading) return "hidden";
|
|
21401
|
-
if (input.hasSession && !input.sitesLoaded) return "hidden";
|
|
21402
|
-
if (!input.hasSession && input.soldPublic === null) return "hidden";
|
|
21403
|
-
const bought = input.paidMarker || input.currentSiteIsLive || input.soldPublic === true;
|
|
21404
|
-
if (bought) return "hidden";
|
|
21405
|
-
return input.hasSession && input.hasCurrentSite ? "owner" : "visitor";
|
|
21406
|
-
}
|
|
21407
|
-
function SettoPreviewBar() {
|
|
21408
|
-
const { supabase, api, config, session, authLoading, currentSite, sites, editMode } = useSetto();
|
|
21409
|
-
const [onPreview, setOnPreview] = useState(false);
|
|
21410
|
-
const [busy, setBusy] = useState(false);
|
|
21411
|
-
const [error, setError] = useState(null);
|
|
21412
|
-
const [paid, setPaid] = useState(false);
|
|
21413
|
-
const [soldPublic, setSoldPublic] = useState(null);
|
|
21414
|
-
useEffect(() => {
|
|
21415
|
-
setOnPreview(isPreviewHost(window.location.hostname));
|
|
21416
|
-
setPaid(new URLSearchParams(window.location.search).get("betaling") === "ok");
|
|
21417
|
-
}, []);
|
|
21418
|
-
useEffect(() => {
|
|
21419
|
-
if (!onPreview || session) return;
|
|
21420
|
-
let cancelled = false;
|
|
21421
|
-
fetch(`${config.apiUrl}/onboarding/sites/${config.siteId}/sold`, {
|
|
21422
|
-
headers: { Accept: "application/json" }
|
|
21423
|
-
}).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
21424
|
-
if (!cancelled && d) setSoldPublic(d.sold === true);
|
|
21425
|
-
}).catch(() => {
|
|
21426
|
-
});
|
|
21427
|
-
return () => {
|
|
21428
|
-
cancelled = true;
|
|
21429
|
-
};
|
|
21430
|
-
}, [onPreview, session, config.apiUrl, config.siteId]);
|
|
21431
|
-
const signIn = () => {
|
|
21432
|
-
window.location.href = checkoutIntentUrl();
|
|
21433
|
-
};
|
|
21434
|
-
const next = () => {
|
|
21435
|
-
if (window.parent !== window) {
|
|
21436
|
-
window.parent.postMessage({ type: "setto:next", siteId: config.siteId }, "*");
|
|
21437
|
-
return;
|
|
21438
|
-
}
|
|
21439
|
-
signIn();
|
|
21440
|
-
};
|
|
21441
|
-
const publish = async () => {
|
|
21442
|
-
setBusy(true);
|
|
21443
|
-
setError(null);
|
|
21444
|
-
try {
|
|
21445
|
-
const token = (await supabase.auth.getSession()).data.session?.access_token;
|
|
21446
|
-
if (!token) {
|
|
21447
|
-
signIn();
|
|
21448
|
-
return;
|
|
21449
|
-
}
|
|
21450
|
-
const data = await api.request(
|
|
21451
|
-
`/onboarding/sites/${config.siteId}/checkout`,
|
|
21452
|
-
{
|
|
21453
|
-
method: "POST",
|
|
21454
|
-
// Back to the page they were looking at, not to a chat window.
|
|
21455
|
-
body: { returnTo: window.location.origin + window.location.pathname }
|
|
21456
|
-
},
|
|
21457
|
-
"checkout"
|
|
21458
|
-
);
|
|
21459
|
-
if (!data.url) throw new Error("checkout_failed");
|
|
21460
|
-
window.location.href = data.url;
|
|
21461
|
-
} catch (err) {
|
|
21462
|
-
const detail = err instanceof Error ? err.message : "";
|
|
21463
|
-
setError(
|
|
21464
|
-
detail.includes("already_paid") ? "Denne siden er allerede betalt." : detail.includes("not_found") ? "Fant ingen bestilling for denne siden på kontoen din." : detail.includes("stripe_not_configured") ? "Betaling er ikke satt opp ennå." : "Klarte ikke starte betalingen. Prøv igjen."
|
|
21465
|
-
);
|
|
21466
|
-
setBusy(false);
|
|
21467
|
-
}
|
|
21468
|
-
};
|
|
21469
|
-
const resumed = useRef(false);
|
|
21470
|
-
useEffect(() => {
|
|
21471
|
-
if (resumed.current || !onPreview || !session || !currentSite) return;
|
|
21472
|
-
if (!wantsCheckout()) return;
|
|
21473
|
-
if (paid || currentSite.status === "live") return;
|
|
21474
|
-
resumed.current = true;
|
|
21475
|
-
window.history.replaceState(null, "", withoutCheckoutIntent());
|
|
21476
|
-
void publish();
|
|
21477
|
-
}, [onPreview, session, currentSite, paid]);
|
|
21478
|
-
const state = previewBarState({
|
|
21479
|
-
onPreview,
|
|
21480
|
-
authLoading,
|
|
21481
|
-
hasSession: !!session,
|
|
21482
|
-
sitesLoaded: sites !== null,
|
|
21483
|
-
hasCurrentSite: !!currentSite,
|
|
21484
|
-
currentSiteIsLive: currentSite?.status === "live",
|
|
21485
|
-
soldPublic,
|
|
21486
|
-
paidMarker: paid
|
|
21487
|
-
});
|
|
21488
|
-
if (state === "hidden") return null;
|
|
21489
|
-
const owner = state === "owner";
|
|
21490
|
-
const name = currentSite?.display_name?.trim() || config.displayName?.trim() || config.siteId;
|
|
21491
|
-
return createPortal(
|
|
21492
|
-
/*
|
|
21493
|
-
* Lifted above the editor's floating button when edit mode is on, which
|
|
21494
|
-
* sits in the same corner band. Sideways would collide again the moment
|
|
21495
|
-
* its toolbar opens, since that expands rightward.
|
|
21496
|
-
*/
|
|
21497
|
-
/* @__PURE__ */ jsx(
|
|
21498
|
-
"div",
|
|
21499
|
-
{
|
|
21500
|
-
style: editMode ? { ...barStyle, bottom: "calc(max(16px, env(safe-area-inset-bottom, 0px)) + 68px)" } : barStyle,
|
|
21501
|
-
"data-setto-preview-bar": "",
|
|
21502
|
-
children: owner ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21503
|
-
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
21504
|
-
/* @__PURE__ */ jsxs("span", { style: labelStyle, children: [
|
|
21505
|
-
"Du forhåndsviser nå ",
|
|
21506
|
-
/* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children: name })
|
|
21507
|
-
] }),
|
|
21508
|
-
error && /* @__PURE__ */ jsx("span", { style: errorStyle$1, children: error }),
|
|
21509
|
-
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: publish, disabled: busy, children: busy ? "Åpner betaling …" : "Publiser" })
|
|
21510
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21511
|
-
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
21512
|
-
/* @__PURE__ */ jsx("span", { style: labelStyle, children: "Forhåndsvisning" }),
|
|
21513
|
-
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: next, children: "Neste" })
|
|
21514
|
-
] })
|
|
21515
|
-
}
|
|
21516
|
-
),
|
|
21517
|
-
document.body
|
|
21518
|
-
);
|
|
21519
|
-
}
|
|
21520
|
-
const barStyle = {
|
|
21521
|
-
position: "fixed",
|
|
21522
|
-
left: "50%",
|
|
21523
|
-
bottom: "max(16px, env(safe-area-inset-bottom, 0px))",
|
|
21524
|
-
transform: "translateX(-50%)",
|
|
21525
|
-
zIndex: 2147483e3,
|
|
21526
|
-
display: "flex",
|
|
21527
|
-
alignItems: "center",
|
|
21528
|
-
gap: 14,
|
|
21529
|
-
maxWidth: "calc(100vw - 24px)",
|
|
21530
|
-
padding: "10px 10px 10px 18px",
|
|
21531
|
-
borderRadius: 999,
|
|
21532
|
-
background: "#211711",
|
|
21533
|
-
color: "#fffdf8",
|
|
21534
|
-
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
21535
|
-
fontSize: 14,
|
|
21536
|
-
lineHeight: 1.3,
|
|
21537
|
-
boxShadow: "0 18px 40px -18px rgba(0, 0, 0, 0.65)",
|
|
21538
|
-
/*
|
|
21539
|
-
* A frosted rim, so the pill reads as an object on any page.
|
|
21540
|
-
*
|
|
21541
|
-
* The bar sits on sites Setto did not choose the colours for: on a dark hero
|
|
21542
|
-
* a dark pill disappears, and on a pale section its shadow is all that holds
|
|
21543
|
-
* it. A translucent white border with `padding-box` clipping lets the page
|
|
21544
|
-
* show through the rim itself, which separates the pill from either ground
|
|
21545
|
-
* without picking a side.
|
|
21546
|
-
*/
|
|
21547
|
-
border: "5px solid rgba(255, 255, 255, 0.22)",
|
|
21548
|
-
backgroundClip: "padding-box",
|
|
21549
|
-
backdropFilter: "blur(10px)",
|
|
21550
|
-
WebkitBackdropFilter: "blur(10px)"
|
|
21551
|
-
};
|
|
21552
|
-
const dotStyle = {
|
|
21553
|
-
width: 7,
|
|
21554
|
-
height: 7,
|
|
21555
|
-
borderRadius: "50%",
|
|
21556
|
-
background: "#c4502a",
|
|
21557
|
-
flex: "none"
|
|
21558
|
-
};
|
|
21559
|
-
const labelStyle = {
|
|
21560
|
-
whiteSpace: "nowrap",
|
|
21561
|
-
overflow: "hidden",
|
|
21562
|
-
textOverflow: "ellipsis"
|
|
21563
|
-
};
|
|
21564
|
-
const primaryStyle = {
|
|
21565
|
-
flex: "none",
|
|
21566
|
-
padding: "9px 18px",
|
|
21567
|
-
border: 0,
|
|
21568
|
-
borderRadius: 999,
|
|
21569
|
-
background: "#c4502a",
|
|
21570
|
-
color: "#fffdf8",
|
|
21571
|
-
font: "inherit",
|
|
21572
|
-
fontWeight: 600,
|
|
21573
|
-
cursor: "pointer"
|
|
21574
|
-
};
|
|
21575
|
-
const errorStyle$1 = {
|
|
21576
|
-
color: "#ffb4a2",
|
|
21577
|
-
fontSize: 13,
|
|
21578
|
-
/*
|
|
21579
|
-
* Shrinkable, unlike the label beside it.
|
|
21580
|
-
*
|
|
21581
|
-
* `nowrap` on a flex child whose `min-width` is `auto` cannot shrink below
|
|
21582
|
-
* its own text, so on a 320px phone the error shouldered «Publiser» 19px
|
|
21583
|
-
* past the right edge of the screen and the bar clipped it — the button
|
|
21584
|
-
* that takes the money, unreachable, exactly when something had gone wrong.
|
|
21585
|
-
*/
|
|
21586
|
-
minWidth: 0,
|
|
21587
|
-
overflow: "hidden",
|
|
21588
|
-
textOverflow: "ellipsis",
|
|
21589
|
-
whiteSpace: "nowrap"
|
|
21590
|
-
};
|
|
21591
21462
|
const FAB_SIZE = 52;
|
|
21592
21463
|
const FAB_INSET = 16;
|
|
21593
21464
|
const STYLE_ID$3 = "setto-edit-layout";
|
|
@@ -22268,13 +22139,13 @@ function isDeploymentInProgress(status) {
|
|
|
22268
22139
|
}
|
|
22269
22140
|
const STALE_PENDING_MS = 20 * 60 * 1e3;
|
|
22270
22141
|
const STALE_BUILDING_MS = 45 * 60 * 1e3;
|
|
22271
|
-
function isDeploymentStale(
|
|
22272
|
-
if (
|
|
22142
|
+
function isDeploymentStale(row2) {
|
|
22143
|
+
if (row2.status !== "pending" && row2.status !== "building") return false;
|
|
22273
22144
|
const now = Date.now();
|
|
22274
|
-
if (
|
|
22275
|
-
return now - new Date(
|
|
22145
|
+
if (row2.status === "pending") {
|
|
22146
|
+
return now - new Date(row2.started_at).getTime() > STALE_PENDING_MS;
|
|
22276
22147
|
}
|
|
22277
|
-
return now - new Date(
|
|
22148
|
+
return now - new Date(row2.updated_at).getTime() > STALE_BUILDING_MS;
|
|
22278
22149
|
}
|
|
22279
22150
|
async function fetchInProgressDeploymentId(supabase, siteId) {
|
|
22280
22151
|
const { data } = await supabase.from("deployments").select("id, status, started_at, updated_at").eq("site_id", siteId).in("status", [...IN_PROGRESS_STATUSES]).order("started_at", { ascending: false }).limit(5);
|
|
@@ -22292,7 +22163,7 @@ async function waitForDeploymentByCommit(supabase, siteId, commitSha, timeoutMs
|
|
|
22292
22163
|
return null;
|
|
22293
22164
|
}
|
|
22294
22165
|
function useDeploymentStatus(supabase, deploymentId) {
|
|
22295
|
-
const [
|
|
22166
|
+
const [row2, setRow] = useState(null);
|
|
22296
22167
|
useEffect(() => {
|
|
22297
22168
|
if (!deploymentId) {
|
|
22298
22169
|
setRow(null);
|
|
@@ -22322,7 +22193,7 @@ function useDeploymentStatus(supabase, deploymentId) {
|
|
|
22322
22193
|
if (channel) supabase.removeChannel(channel);
|
|
22323
22194
|
};
|
|
22324
22195
|
}, [supabase, deploymentId]);
|
|
22325
|
-
return { row, status:
|
|
22196
|
+
return { row: row2, status: row2?.status ?? null };
|
|
22326
22197
|
}
|
|
22327
22198
|
function deploymentStepIndex(status) {
|
|
22328
22199
|
switch (status) {
|
|
@@ -22461,14 +22332,14 @@ function PublishProgressBar({ status, compact, href }) {
|
|
|
22461
22332
|
return /* @__PURE__ */ jsx("div", { style: wrapStyle, children: inner });
|
|
22462
22333
|
}
|
|
22463
22334
|
function PublishDialog({
|
|
22464
|
-
row,
|
|
22335
|
+
row: row2,
|
|
22465
22336
|
status,
|
|
22466
22337
|
onClose,
|
|
22467
22338
|
onComplete
|
|
22468
22339
|
}) {
|
|
22469
22340
|
const buildingStartedAt = useRef(null);
|
|
22470
22341
|
const [elapsed, setElapsed] = useState(0);
|
|
22471
|
-
const progressStatus =
|
|
22342
|
+
const progressStatus = row2?.status ?? status ?? "pending";
|
|
22472
22343
|
const errored = progressStatus === "error" || progressStatus === "canceled";
|
|
22473
22344
|
const isReady = progressStatus === "ready";
|
|
22474
22345
|
const isBuilding = progressStatus === "building";
|
|
@@ -22495,7 +22366,7 @@ function PublishDialog({
|
|
|
22495
22366
|
/* @__PURE__ */ jsx("h2", { style: { margin: "0 0 12px", fontSize: 18, fontWeight: 600, color: "#dc2626" }, children: "Publisering feilet" }),
|
|
22496
22367
|
/* @__PURE__ */ jsxs("p", { style: { margin: "0 0 20px", fontSize: 14, lineHeight: 1.5, color: "#444" }, children: [
|
|
22497
22368
|
progressStatus === "error" ? "Bygg feilet." : "Avbrutt.",
|
|
22498
|
-
|
|
22369
|
+
row2?.error_message ? ` ${row2.error_message}` : ""
|
|
22499
22370
|
] }),
|
|
22500
22371
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: onComplete, style: secondaryBtnStyle$1, children: "OK" })
|
|
22501
22372
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -22576,6 +22447,226 @@ function useEditBaseline() {
|
|
|
22576
22447
|
};
|
|
22577
22448
|
}, [session, store, config.siteId]);
|
|
22578
22449
|
}
|
|
22450
|
+
const EMPTY = {
|
|
22451
|
+
host: null,
|
|
22452
|
+
verified: false,
|
|
22453
|
+
status: null,
|
|
22454
|
+
instructions: [],
|
|
22455
|
+
statusLine: null
|
|
22456
|
+
};
|
|
22457
|
+
function Copyable({ value }) {
|
|
22458
|
+
const [copied, setCopied] = useState(false);
|
|
22459
|
+
return /* @__PURE__ */ jsx(
|
|
22460
|
+
"button",
|
|
22461
|
+
{
|
|
22462
|
+
type: "button",
|
|
22463
|
+
onClick: () => {
|
|
22464
|
+
void navigator.clipboard?.writeText(value).then(
|
|
22465
|
+
() => {
|
|
22466
|
+
setCopied(true);
|
|
22467
|
+
window.setTimeout(() => setCopied(false), 1400);
|
|
22468
|
+
},
|
|
22469
|
+
() => void 0
|
|
22470
|
+
);
|
|
22471
|
+
},
|
|
22472
|
+
title: "Kopier",
|
|
22473
|
+
style: {
|
|
22474
|
+
font: "inherit",
|
|
22475
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
22476
|
+
background: "none",
|
|
22477
|
+
border: 0,
|
|
22478
|
+
padding: 0,
|
|
22479
|
+
cursor: "pointer",
|
|
22480
|
+
color: "inherit",
|
|
22481
|
+
textAlign: "left",
|
|
22482
|
+
wordBreak: "break-all"
|
|
22483
|
+
},
|
|
22484
|
+
children: copied ? "Kopiert" : value
|
|
22485
|
+
}
|
|
22486
|
+
);
|
|
22487
|
+
}
|
|
22488
|
+
function DomainView({ siteId }) {
|
|
22489
|
+
const { api } = useSetto();
|
|
22490
|
+
const [state, setState] = useState(EMPTY);
|
|
22491
|
+
const [host, setHost] = useState("");
|
|
22492
|
+
const [loading, setLoading] = useState(true);
|
|
22493
|
+
const [busy, setBusy] = useState(false);
|
|
22494
|
+
const [error, setError] = useState(null);
|
|
22495
|
+
const load = async () => {
|
|
22496
|
+
try {
|
|
22497
|
+
setState(await api.request(`/onboarding/sites/${siteId}/domain`, {}, "domain"));
|
|
22498
|
+
} catch {
|
|
22499
|
+
setState(EMPTY);
|
|
22500
|
+
} finally {
|
|
22501
|
+
setLoading(false);
|
|
22502
|
+
}
|
|
22503
|
+
};
|
|
22504
|
+
useEffect(() => {
|
|
22505
|
+
void load();
|
|
22506
|
+
}, [siteId]);
|
|
22507
|
+
async function attach() {
|
|
22508
|
+
setBusy(true);
|
|
22509
|
+
setError(null);
|
|
22510
|
+
try {
|
|
22511
|
+
setState(
|
|
22512
|
+
await api.request(
|
|
22513
|
+
`/onboarding/sites/${siteId}/domain`,
|
|
22514
|
+
{ method: "POST", body: { host } },
|
|
22515
|
+
"attachDomain"
|
|
22516
|
+
)
|
|
22517
|
+
);
|
|
22518
|
+
setHost("");
|
|
22519
|
+
} catch (err) {
|
|
22520
|
+
const detail = err instanceof Error ? err.message : "";
|
|
22521
|
+
setError(
|
|
22522
|
+
detail.includes("invalid_domain") ? "Det ser ikke ut som et domenenavn. Skriv det som dittfirma.no." : detail.includes("domain_taken") ? "Domenet er allerede i bruk på en annen Setto-side." : detail.includes("site_not_deployed") ? "Nettsiden er ikke publisert ennå." : "Klarte ikke legge til domenet. Prøv igjen."
|
|
22523
|
+
);
|
|
22524
|
+
} finally {
|
|
22525
|
+
setBusy(false);
|
|
22526
|
+
}
|
|
22527
|
+
}
|
|
22528
|
+
async function recheck() {
|
|
22529
|
+
setBusy(true);
|
|
22530
|
+
setError(null);
|
|
22531
|
+
try {
|
|
22532
|
+
setState(
|
|
22533
|
+
await api.request(
|
|
22534
|
+
`/onboarding/sites/${siteId}/domain/verify`,
|
|
22535
|
+
{ method: "POST" },
|
|
22536
|
+
"verifyDomain"
|
|
22537
|
+
)
|
|
22538
|
+
);
|
|
22539
|
+
} catch {
|
|
22540
|
+
setError("Klarte ikke sjekke akkurat nå. Prøv igjen om litt.");
|
|
22541
|
+
} finally {
|
|
22542
|
+
setBusy(false);
|
|
22543
|
+
}
|
|
22544
|
+
}
|
|
22545
|
+
async function detach() {
|
|
22546
|
+
setBusy(true);
|
|
22547
|
+
setError(null);
|
|
22548
|
+
try {
|
|
22549
|
+
setState(
|
|
22550
|
+
await api.request(
|
|
22551
|
+
`/onboarding/sites/${siteId}/domain`,
|
|
22552
|
+
{ method: "DELETE" },
|
|
22553
|
+
"detachDomain"
|
|
22554
|
+
)
|
|
22555
|
+
);
|
|
22556
|
+
} catch {
|
|
22557
|
+
setError("Klarte ikke fjerne domenet.");
|
|
22558
|
+
} finally {
|
|
22559
|
+
setBusy(false);
|
|
22560
|
+
}
|
|
22561
|
+
}
|
|
22562
|
+
if (loading) return /* @__PURE__ */ jsx("p", { style: muted, children: "Henter …" });
|
|
22563
|
+
if (!state.host) {
|
|
22564
|
+
return /* @__PURE__ */ jsxs("div", { style: wrap, children: [
|
|
22565
|
+
/* @__PURE__ */ jsx("p", { style: lead, children: "Har du et domene fra før — dittfirma.no — kan du peke det hit. Nettsiden beholder adressen den har nå i tillegg, så ingenting slutter å virke underveis." }),
|
|
22566
|
+
/* @__PURE__ */ jsxs("div", { style: row, children: [
|
|
22567
|
+
/* @__PURE__ */ jsx(
|
|
22568
|
+
"input",
|
|
22569
|
+
{
|
|
22570
|
+
value: host,
|
|
22571
|
+
onChange: (e) => setHost(e.target.value),
|
|
22572
|
+
placeholder: "dittfirma.no",
|
|
22573
|
+
"aria-label": "Domenet ditt",
|
|
22574
|
+
autoCapitalize: "off",
|
|
22575
|
+
autoCorrect: "off",
|
|
22576
|
+
spellCheck: false,
|
|
22577
|
+
style: input
|
|
22578
|
+
}
|
|
22579
|
+
),
|
|
22580
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void attach(), disabled: busy || !host.trim(), style: primary, children: busy ? "Legger til …" : "Legg til domene" })
|
|
22581
|
+
] }),
|
|
22582
|
+
error && /* @__PURE__ */ jsx("p", { style: errorStyle$2, children: error })
|
|
22583
|
+
] });
|
|
22584
|
+
}
|
|
22585
|
+
return /* @__PURE__ */ jsxs("div", { style: wrap, children: [
|
|
22586
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "0.6rem", flexWrap: "wrap" }, children: [
|
|
22587
|
+
/* @__PURE__ */ jsx("strong", { style: { fontSize: "1.05rem" }, children: state.host }),
|
|
22588
|
+
/* @__PURE__ */ jsx("span", { style: state.verified ? liveBadge : pendingBadge, children: state.verified ? "Live" : "Venter på DNS" })
|
|
22589
|
+
] }),
|
|
22590
|
+
state.statusLine && /* @__PURE__ */ jsx("p", { style: lead, children: state.statusLine }),
|
|
22591
|
+
state.instructions.length > 0 && !state.verified && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22592
|
+
/* @__PURE__ */ jsx("p", { style: muted, children: "Logg inn der du kjøpte domenet, finn DNS-innstillingene, og legg inn dette:" }),
|
|
22593
|
+
/* @__PURE__ */ jsx("div", { style: { display: "grid", gap: "0.6rem" }, children: state.instructions.map((rec) => /* @__PURE__ */ jsxs("div", { style: card, children: [
|
|
22594
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1.2rem", flexWrap: "wrap" }, children: [
|
|
22595
|
+
/* @__PURE__ */ jsx(Field, { label: "Type", value: rec.type }),
|
|
22596
|
+
/* @__PURE__ */ jsx(Field, { label: "Navn", value: rec.name }),
|
|
22597
|
+
/* @__PURE__ */ jsx(Field, { label: "Peker til", value: rec.value, wide: true })
|
|
22598
|
+
] }),
|
|
22599
|
+
/* @__PURE__ */ jsx("p", { style: { ...muted, margin: "0.35rem 0 0" }, children: rec.why })
|
|
22600
|
+
] }, `${rec.type}-${rec.name}`)) })
|
|
22601
|
+
] }),
|
|
22602
|
+
error && /* @__PURE__ */ jsx("p", { style: errorStyle$2, children: error }),
|
|
22603
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.6rem", flexWrap: "wrap" }, children: [
|
|
22604
|
+
!state.verified && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void recheck(), disabled: busy, style: primary, children: busy ? "Sjekker …" : "Jeg har lagt inn radene" }),
|
|
22605
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void detach(), disabled: busy, style: quiet, children: "Fjern domenet" })
|
|
22606
|
+
] })
|
|
22607
|
+
] });
|
|
22608
|
+
}
|
|
22609
|
+
function Field({ label, value, wide }) {
|
|
22610
|
+
return /* @__PURE__ */ jsxs("div", { style: { minWidth: wide ? "14rem" : "5rem", flex: wide ? "1 1 14rem" : "0 0 auto" }, children: [
|
|
22611
|
+
/* @__PURE__ */ jsx("div", { style: { ...muted, fontSize: "0.72rem", textTransform: "uppercase", letterSpacing: "0.04em" }, children: label }),
|
|
22612
|
+
/* @__PURE__ */ jsx(Copyable, { value })
|
|
22613
|
+
] });
|
|
22614
|
+
}
|
|
22615
|
+
const wrap = { display: "grid", gap: "0.9rem", maxWidth: "42rem" };
|
|
22616
|
+
const lead = { margin: 0, lineHeight: 1.5 };
|
|
22617
|
+
const muted = { margin: 0, opacity: 0.65, lineHeight: 1.5 };
|
|
22618
|
+
const row = { display: "flex", gap: "0.6rem", flexWrap: "wrap" };
|
|
22619
|
+
const input = {
|
|
22620
|
+
flex: "1 1 14rem",
|
|
22621
|
+
padding: "0.6rem 0.75rem",
|
|
22622
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
22623
|
+
borderRadius: "0.5rem",
|
|
22624
|
+
font: "inherit"
|
|
22625
|
+
};
|
|
22626
|
+
const card = {
|
|
22627
|
+
padding: "0.7rem 0.85rem",
|
|
22628
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
22629
|
+
borderRadius: "0.6rem",
|
|
22630
|
+
background: "rgba(0,0,0,0.02)"
|
|
22631
|
+
};
|
|
22632
|
+
const primary = {
|
|
22633
|
+
padding: "0.6rem 1.1rem",
|
|
22634
|
+
borderRadius: "999px",
|
|
22635
|
+
border: 0,
|
|
22636
|
+
background: "#c4502a",
|
|
22637
|
+
color: "#fff",
|
|
22638
|
+
font: "inherit",
|
|
22639
|
+
fontWeight: 600,
|
|
22640
|
+
cursor: "pointer"
|
|
22641
|
+
};
|
|
22642
|
+
const quiet = {
|
|
22643
|
+
padding: "0.6rem 0.9rem",
|
|
22644
|
+
borderRadius: "999px",
|
|
22645
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
22646
|
+
background: "transparent",
|
|
22647
|
+
font: "inherit",
|
|
22648
|
+
cursor: "pointer"
|
|
22649
|
+
};
|
|
22650
|
+
const errorStyle$2 = { margin: 0, color: "#b42318" };
|
|
22651
|
+
const liveBadge = {
|
|
22652
|
+
fontSize: "0.75rem",
|
|
22653
|
+
padding: "0.15rem 0.5rem",
|
|
22654
|
+
borderRadius: "999px",
|
|
22655
|
+
background: "rgba(15,123,79,0.12)",
|
|
22656
|
+
color: "#0f7b4f"
|
|
22657
|
+
};
|
|
22658
|
+
const pendingBadge = {
|
|
22659
|
+
fontSize: "0.75rem",
|
|
22660
|
+
padding: "0.15rem 0.5rem",
|
|
22661
|
+
borderRadius: "999px",
|
|
22662
|
+
background: "rgba(196,80,42,0.12)",
|
|
22663
|
+
color: "#c4502a"
|
|
22664
|
+
};
|
|
22665
|
+
const domainModule = {
|
|
22666
|
+
id: "domain",
|
|
22667
|
+
label: "Domene",
|
|
22668
|
+
AdminView: DomainView
|
|
22669
|
+
};
|
|
22579
22670
|
const shellStyle$1 = {
|
|
22580
22671
|
minHeight: "100dvh",
|
|
22581
22672
|
background: "#0a0a0d",
|
|
@@ -22846,28 +22937,28 @@ function SubmissionList({ siteId }) {
|
|
|
22846
22937
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
|
|
22847
22938
|
/* @__PURE__ */ jsx("h3", { style: subheadingStyle, children: "Henvendelser" }),
|
|
22848
22939
|
error ? /* @__PURE__ */ jsx("p", { style: errStyle, children: error }) : null,
|
|
22849
|
-
rows.length === 0 ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Ingen henvendelser ennå. Når noen sender inn et skjema på siden, dukker det opp her og i e-posten." }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 6 }, children: rows.map((
|
|
22940
|
+
rows.length === 0 ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Ingen henvendelser ennå. Når noen sender inn et skjema på siden, dukker det opp her og i e-posten." }) : /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 6 }, children: rows.map((row2) => /* @__PURE__ */ jsx(
|
|
22850
22941
|
SubmissionItem,
|
|
22851
22942
|
{
|
|
22852
|
-
row,
|
|
22853
|
-
open: openId ===
|
|
22854
|
-
onToggle: () => setOpenId(openId ===
|
|
22855
|
-
onDelete: () => void remove2(
|
|
22943
|
+
row: row2,
|
|
22944
|
+
open: openId === row2.id,
|
|
22945
|
+
onToggle: () => setOpenId(openId === row2.id ? null : row2.id),
|
|
22946
|
+
onDelete: () => void remove2(row2.id)
|
|
22856
22947
|
},
|
|
22857
|
-
|
|
22948
|
+
row2.id
|
|
22858
22949
|
)) }),
|
|
22859
22950
|
hasMore ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void loadMore(), disabled: loadingMore, style: secondaryBtnStyle, children: loadingMore ? "Laster …" : "Vis eldre" }) : null
|
|
22860
22951
|
] });
|
|
22861
22952
|
}
|
|
22862
22953
|
function SubmissionItem({
|
|
22863
|
-
row,
|
|
22954
|
+
row: row2,
|
|
22864
22955
|
open,
|
|
22865
22956
|
onToggle,
|
|
22866
22957
|
onDelete
|
|
22867
22958
|
}) {
|
|
22868
|
-
const fields = submissionFields(
|
|
22959
|
+
const fields = submissionFields(row2);
|
|
22869
22960
|
const sender = senderOf(fields);
|
|
22870
|
-
const title =
|
|
22961
|
+
const title = row2.meta.title ?? row2.form_id;
|
|
22871
22962
|
return /* @__PURE__ */ jsxs("li", { children: [
|
|
22872
22963
|
/* @__PURE__ */ jsxs(
|
|
22873
22964
|
"button",
|
|
@@ -22877,7 +22968,7 @@ function SubmissionItem({
|
|
|
22877
22968
|
"aria-expanded": open,
|
|
22878
22969
|
style: { ...rowStyle, width: "100%", textAlign: "left", color: "inherit", border: "none", cursor: "pointer", font: "inherit" },
|
|
22879
22970
|
children: [
|
|
22880
|
-
/* @__PURE__ */ jsx("span", { style: statusDotStyle(
|
|
22971
|
+
/* @__PURE__ */ jsx("span", { style: statusDotStyle(row2.email_status), title: emailStatusLabel(row2) }),
|
|
22881
22972
|
/* @__PURE__ */ jsxs("span", { style: { flex: 1, fontSize: 13, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
|
|
22882
22973
|
sender ?? /* @__PURE__ */ jsx("span", { style: { color: "#aaa" }, children: "Uten navn" }),
|
|
22883
22974
|
/* @__PURE__ */ jsxs("span", { style: { color: "#888" }, children: [
|
|
@@ -22885,7 +22976,7 @@ function SubmissionItem({
|
|
|
22885
22976
|
title
|
|
22886
22977
|
] })
|
|
22887
22978
|
] }),
|
|
22888
|
-
/* @__PURE__ */ jsx("time", { style: { fontSize: 11, color: "#888", flexShrink: 0 }, children: formatWhen(
|
|
22979
|
+
/* @__PURE__ */ jsx("time", { style: { fontSize: 11, color: "#888", flexShrink: 0 }, children: formatWhen(row2.created_at) })
|
|
22889
22980
|
]
|
|
22890
22981
|
}
|
|
22891
22982
|
),
|
|
@@ -22895,26 +22986,26 @@ function SubmissionItem({
|
|
|
22895
22986
|
/* @__PURE__ */ jsx("dd", { style: { margin: 0, fontSize: 14, whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: f.value === "" ? /* @__PURE__ */ jsx("span", { style: { color: "#666" }, children: "—" }) : f.value })
|
|
22896
22987
|
] }, f.name)) }),
|
|
22897
22988
|
/* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
|
|
22898
|
-
emailStatusLabel(
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22989
|
+
emailStatusLabel(row2),
|
|
22990
|
+
row2.recipients.length > 0 ? ` til ${row2.recipients.join(", ")}` : "",
|
|
22991
|
+
row2.email_error ? ` — ${row2.email_error}` : "",
|
|
22992
|
+
row2.meta.page ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22902
22993
|
" · ",
|
|
22903
|
-
/* @__PURE__ */ jsx("a", { href:
|
|
22994
|
+
/* @__PURE__ */ jsx("a", { href: row2.meta.page, style: { color: "#bda6ff" }, target: "_blank", rel: "noreferrer", children: "Sendt fra siden" })
|
|
22904
22995
|
] }) : null
|
|
22905
22996
|
] }),
|
|
22906
22997
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: onDelete, style: dangerBtnStyle, children: "Slett henvendelse" })
|
|
22907
22998
|
] }) : null
|
|
22908
22999
|
] });
|
|
22909
23000
|
}
|
|
22910
|
-
function submissionFields(
|
|
23001
|
+
function submissionFields(row2) {
|
|
22911
23002
|
const flat = [];
|
|
22912
|
-
flatten(
|
|
22913
|
-
const rank = new Map((
|
|
23003
|
+
flatten(row2.data, "", flat);
|
|
23004
|
+
const rank = new Map((row2.field_order ?? []).map((n, i) => [n, i]));
|
|
22914
23005
|
const rankOf = (name) => rank.get(name) ?? rank.get(name.split(".")[0]) ?? Number.MAX_SAFE_INTEGER;
|
|
22915
23006
|
return flat.map((f, i) => ({ f, i })).sort((a, b) => rankOf(a.f.name) - rankOf(b.f.name) || a.i - b.i).map(({ f }) => ({
|
|
22916
23007
|
name: f.name,
|
|
22917
|
-
label:
|
|
23008
|
+
label: row2.labels?.[f.name] ?? row2.labels?.[f.name.split(".")[0]] ?? prettify(f.name),
|
|
22918
23009
|
value: f.value
|
|
22919
23010
|
}));
|
|
22920
23011
|
}
|
|
@@ -22953,8 +23044,8 @@ function senderOf(fields) {
|
|
|
22953
23044
|
const first = fields.find((f) => f.value !== "");
|
|
22954
23045
|
return first ? first.value.slice(0, 80) : null;
|
|
22955
23046
|
}
|
|
22956
|
-
function emailStatusLabel(
|
|
22957
|
-
switch (
|
|
23047
|
+
function emailStatusLabel(row2) {
|
|
23048
|
+
switch (row2.email_status) {
|
|
22958
23049
|
case "sent":
|
|
22959
23050
|
return "E-post sendt";
|
|
22960
23051
|
case "failed":
|
|
@@ -24404,7 +24495,7 @@ const formsModule = {
|
|
|
24404
24495
|
label: "Skjemaer",
|
|
24405
24496
|
AdminView: FormsView
|
|
24406
24497
|
};
|
|
24407
|
-
const SETTO_MODULES = [formsModule];
|
|
24498
|
+
const SETTO_MODULES = [formsModule, domainModule];
|
|
24408
24499
|
function findModule(id) {
|
|
24409
24500
|
return id ? SETTO_MODULES.find((m) => m.id === id) : void 0;
|
|
24410
24501
|
}
|
|
@@ -24710,7 +24801,7 @@ function EditToolbar() {
|
|
|
24710
24801
|
}
|
|
24711
24802
|
),
|
|
24712
24803
|
expanded ? /* @__PURE__ */ jsxs("div", { style: contentStyle, children: [
|
|
24713
|
-
error ? /* @__PURE__ */ jsx("span", { style: errorStyle, children: error }) : null,
|
|
24804
|
+
error ? /* @__PURE__ */ jsx("span", { style: errorStyle$1, children: error }) : null,
|
|
24714
24805
|
hasDrafts && !publishing && activeDeployment === null ? /* @__PURE__ */ jsx("button", { type: "button", onClick: publish, style: publishBtnStyle, children: "Publiser" }) : null,
|
|
24715
24806
|
showPublishedNotice ? /* @__PURE__ */ jsxs(
|
|
24716
24807
|
"button",
|
|
@@ -24831,7 +24922,7 @@ const contentStyle = {
|
|
|
24831
24922
|
paddingLeft: 4,
|
|
24832
24923
|
whiteSpace: "nowrap"
|
|
24833
24924
|
};
|
|
24834
|
-
const errorStyle = {
|
|
24925
|
+
const errorStyle$1 = {
|
|
24835
24926
|
color: "#ffb4a8",
|
|
24836
24927
|
fontSize: 12,
|
|
24837
24928
|
maxWidth: 240,
|
|
@@ -25085,7 +25176,7 @@ function SettoProvider({ config, children }) {
|
|
|
25085
25176
|
const editShellActive = editMode && !!value.store;
|
|
25086
25177
|
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsxs(SectionEditProvider, { children: [
|
|
25087
25178
|
/* @__PURE__ */ jsx(EditModeShell, { active: editShellActive, themeStore: value.themeStore, children }),
|
|
25088
|
-
!onAdminRoute && /* @__PURE__ */ jsx(
|
|
25179
|
+
!onAdminRoute && /* @__PURE__ */ jsx(SettoChrome, {})
|
|
25089
25180
|
] }) });
|
|
25090
25181
|
}
|
|
25091
25182
|
function useSetto() {
|
|
@@ -25095,6 +25186,202 @@ function useSetto() {
|
|
|
25095
25186
|
}
|
|
25096
25187
|
return ctx;
|
|
25097
25188
|
}
|
|
25189
|
+
const PREVIEW_HOST = /(^|\.)site\.setto\.no$/i;
|
|
25190
|
+
function isPreviewHost(hostname) {
|
|
25191
|
+
return PREVIEW_HOST.test(hostname);
|
|
25192
|
+
}
|
|
25193
|
+
function previewBarState(input2) {
|
|
25194
|
+
if (!input2.onPreview || input2.authLoading) return "hidden";
|
|
25195
|
+
if (input2.hasSession && !input2.sitesLoaded) return "hidden";
|
|
25196
|
+
if (!input2.hasSession && input2.soldPublic === null) return "hidden";
|
|
25197
|
+
const bought = input2.paidMarker || input2.currentSiteIsLive || input2.soldPublic === true;
|
|
25198
|
+
if (bought) return "hidden";
|
|
25199
|
+
return input2.hasSession && input2.hasCurrentSite ? "owner" : "visitor";
|
|
25200
|
+
}
|
|
25201
|
+
function SettoPreviewBar() {
|
|
25202
|
+
const { supabase, api, config, session, authLoading, currentSite, sites, editMode } = useSetto();
|
|
25203
|
+
const [onPreview, setOnPreview] = useState(false);
|
|
25204
|
+
const [busy, setBusy] = useState(false);
|
|
25205
|
+
const [error, setError] = useState(null);
|
|
25206
|
+
const [paid, setPaid] = useState(false);
|
|
25207
|
+
const [soldPublic, setSoldPublic] = useState(null);
|
|
25208
|
+
useEffect(() => {
|
|
25209
|
+
setOnPreview(isPreviewHost(window.location.hostname));
|
|
25210
|
+
setPaid(new URLSearchParams(window.location.search).get("betaling") === "ok");
|
|
25211
|
+
}, []);
|
|
25212
|
+
useEffect(() => {
|
|
25213
|
+
if (!onPreview || session) return;
|
|
25214
|
+
let cancelled = false;
|
|
25215
|
+
fetch(`${config.apiUrl}/onboarding/sites/${config.siteId}/sold`, {
|
|
25216
|
+
headers: { Accept: "application/json" }
|
|
25217
|
+
}).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
25218
|
+
if (!cancelled && d) setSoldPublic(d.sold === true);
|
|
25219
|
+
}).catch(() => {
|
|
25220
|
+
});
|
|
25221
|
+
return () => {
|
|
25222
|
+
cancelled = true;
|
|
25223
|
+
};
|
|
25224
|
+
}, [onPreview, session, config.apiUrl, config.siteId]);
|
|
25225
|
+
const signIn = () => {
|
|
25226
|
+
window.location.href = checkoutIntentUrl();
|
|
25227
|
+
};
|
|
25228
|
+
const next = () => {
|
|
25229
|
+
if (window.parent !== window) {
|
|
25230
|
+
window.parent.postMessage({ type: "setto:next", siteId: config.siteId }, "*");
|
|
25231
|
+
return;
|
|
25232
|
+
}
|
|
25233
|
+
signIn();
|
|
25234
|
+
};
|
|
25235
|
+
const publish = async () => {
|
|
25236
|
+
setBusy(true);
|
|
25237
|
+
setError(null);
|
|
25238
|
+
try {
|
|
25239
|
+
const token = (await supabase.auth.getSession()).data.session?.access_token;
|
|
25240
|
+
if (!token) {
|
|
25241
|
+
signIn();
|
|
25242
|
+
return;
|
|
25243
|
+
}
|
|
25244
|
+
const data = await api.request(
|
|
25245
|
+
`/onboarding/sites/${config.siteId}/checkout`,
|
|
25246
|
+
{
|
|
25247
|
+
method: "POST",
|
|
25248
|
+
// Back to the page they were looking at, not to a chat window.
|
|
25249
|
+
body: { returnTo: window.location.origin + window.location.pathname }
|
|
25250
|
+
},
|
|
25251
|
+
"checkout"
|
|
25252
|
+
);
|
|
25253
|
+
if (!data.url) throw new Error("checkout_failed");
|
|
25254
|
+
window.location.href = data.url;
|
|
25255
|
+
} catch (err) {
|
|
25256
|
+
const detail = err instanceof Error ? err.message : "";
|
|
25257
|
+
setError(
|
|
25258
|
+
detail.includes("already_paid") ? "Denne siden er allerede betalt." : detail.includes("not_found") ? "Fant ingen bestilling for denne siden på kontoen din." : detail.includes("stripe_not_configured") ? "Betaling er ikke satt opp ennå." : "Klarte ikke starte betalingen. Prøv igjen."
|
|
25259
|
+
);
|
|
25260
|
+
setBusy(false);
|
|
25261
|
+
}
|
|
25262
|
+
};
|
|
25263
|
+
const resumed = useRef(false);
|
|
25264
|
+
useEffect(() => {
|
|
25265
|
+
if (resumed.current || !onPreview || !session || !currentSite) return;
|
|
25266
|
+
if (!wantsCheckout()) return;
|
|
25267
|
+
if (paid || currentSite.status === "live") return;
|
|
25268
|
+
resumed.current = true;
|
|
25269
|
+
window.history.replaceState(null, "", withoutCheckoutIntent());
|
|
25270
|
+
void publish();
|
|
25271
|
+
}, [onPreview, session, currentSite, paid]);
|
|
25272
|
+
const state = previewBarState({
|
|
25273
|
+
onPreview,
|
|
25274
|
+
authLoading,
|
|
25275
|
+
hasSession: !!session,
|
|
25276
|
+
sitesLoaded: sites !== null,
|
|
25277
|
+
hasCurrentSite: !!currentSite,
|
|
25278
|
+
currentSiteIsLive: currentSite?.status === "live",
|
|
25279
|
+
soldPublic,
|
|
25280
|
+
paidMarker: paid
|
|
25281
|
+
});
|
|
25282
|
+
if (state === "hidden") return null;
|
|
25283
|
+
const owner = state === "owner";
|
|
25284
|
+
const name = currentSite?.display_name?.trim() || config.displayName?.trim() || config.siteId;
|
|
25285
|
+
return createPortal(
|
|
25286
|
+
/*
|
|
25287
|
+
* Lifted above the editor's floating button when edit mode is on, which
|
|
25288
|
+
* sits in the same corner band. Sideways would collide again the moment
|
|
25289
|
+
* its toolbar opens, since that expands rightward.
|
|
25290
|
+
*/
|
|
25291
|
+
/* @__PURE__ */ jsx(
|
|
25292
|
+
"div",
|
|
25293
|
+
{
|
|
25294
|
+
style: editMode ? { ...barStyle, bottom: "calc(max(16px, env(safe-area-inset-bottom, 0px)) + 68px)" } : barStyle,
|
|
25295
|
+
"data-setto-preview-bar": "",
|
|
25296
|
+
children: owner ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25297
|
+
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
25298
|
+
/* @__PURE__ */ jsxs("span", { style: labelStyle, children: [
|
|
25299
|
+
"Du forhåndsviser nå ",
|
|
25300
|
+
/* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children: name })
|
|
25301
|
+
] }),
|
|
25302
|
+
error && /* @__PURE__ */ jsx("span", { style: errorStyle, children: error }),
|
|
25303
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: publish, disabled: busy, children: busy ? "Åpner betaling …" : "Publiser" })
|
|
25304
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25305
|
+
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
25306
|
+
/* @__PURE__ */ jsx("span", { style: labelStyle, children: "Forhåndsvisning" }),
|
|
25307
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: next, children: "Neste" })
|
|
25308
|
+
] })
|
|
25309
|
+
}
|
|
25310
|
+
),
|
|
25311
|
+
document.body
|
|
25312
|
+
);
|
|
25313
|
+
}
|
|
25314
|
+
const barStyle = {
|
|
25315
|
+
position: "fixed",
|
|
25316
|
+
left: "50%",
|
|
25317
|
+
bottom: "max(16px, env(safe-area-inset-bottom, 0px))",
|
|
25318
|
+
transform: "translateX(-50%)",
|
|
25319
|
+
zIndex: 2147483e3,
|
|
25320
|
+
display: "flex",
|
|
25321
|
+
alignItems: "center",
|
|
25322
|
+
gap: 14,
|
|
25323
|
+
maxWidth: "calc(100vw - 24px)",
|
|
25324
|
+
padding: "10px 10px 10px 18px",
|
|
25325
|
+
borderRadius: 999,
|
|
25326
|
+
background: "#211711",
|
|
25327
|
+
color: "#fffdf8",
|
|
25328
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
25329
|
+
fontSize: 14,
|
|
25330
|
+
lineHeight: 1.3,
|
|
25331
|
+
boxShadow: "0 18px 40px -18px rgba(0, 0, 0, 0.65)",
|
|
25332
|
+
/*
|
|
25333
|
+
* A frosted rim, so the pill reads as an object on any page.
|
|
25334
|
+
*
|
|
25335
|
+
* The bar sits on sites Setto did not choose the colours for: on a dark hero
|
|
25336
|
+
* a dark pill disappears, and on a pale section its shadow is all that holds
|
|
25337
|
+
* it. A translucent white border with `padding-box` clipping lets the page
|
|
25338
|
+
* show through the rim itself, which separates the pill from either ground
|
|
25339
|
+
* without picking a side.
|
|
25340
|
+
*/
|
|
25341
|
+
border: "5px solid rgba(255, 255, 255, 0.22)",
|
|
25342
|
+
backgroundClip: "padding-box",
|
|
25343
|
+
backdropFilter: "blur(10px)",
|
|
25344
|
+
WebkitBackdropFilter: "blur(10px)"
|
|
25345
|
+
};
|
|
25346
|
+
const dotStyle = {
|
|
25347
|
+
width: 7,
|
|
25348
|
+
height: 7,
|
|
25349
|
+
borderRadius: "50%",
|
|
25350
|
+
background: "#c4502a",
|
|
25351
|
+
flex: "none"
|
|
25352
|
+
};
|
|
25353
|
+
const labelStyle = {
|
|
25354
|
+
whiteSpace: "nowrap",
|
|
25355
|
+
overflow: "hidden",
|
|
25356
|
+
textOverflow: "ellipsis"
|
|
25357
|
+
};
|
|
25358
|
+
const primaryStyle = {
|
|
25359
|
+
flex: "none",
|
|
25360
|
+
padding: "9px 18px",
|
|
25361
|
+
border: 0,
|
|
25362
|
+
borderRadius: 999,
|
|
25363
|
+
background: "#c4502a",
|
|
25364
|
+
color: "#fffdf8",
|
|
25365
|
+
font: "inherit",
|
|
25366
|
+
fontWeight: 600,
|
|
25367
|
+
cursor: "pointer"
|
|
25368
|
+
};
|
|
25369
|
+
const errorStyle = {
|
|
25370
|
+
color: "#ffb4a2",
|
|
25371
|
+
fontSize: 13,
|
|
25372
|
+
/*
|
|
25373
|
+
* Shrinkable, unlike the label beside it.
|
|
25374
|
+
*
|
|
25375
|
+
* `nowrap` on a flex child whose `min-width` is `auto` cannot shrink below
|
|
25376
|
+
* its own text, so on a 320px phone the error shouldered «Publiser» 19px
|
|
25377
|
+
* past the right edge of the screen and the bar clipped it — the button
|
|
25378
|
+
* that takes the money, unreachable, exactly when something had gone wrong.
|
|
25379
|
+
*/
|
|
25380
|
+
minWidth: 0,
|
|
25381
|
+
overflow: "hidden",
|
|
25382
|
+
textOverflow: "ellipsis",
|
|
25383
|
+
whiteSpace: "nowrap"
|
|
25384
|
+
};
|
|
25098
25385
|
function isTextEditClick(target) {
|
|
25099
25386
|
if (target.closest("[data-setto-key]")) return true;
|
|
25100
25387
|
if (target.closest("[data-setto-ui]")) return true;
|
|
@@ -26230,8 +26517,8 @@ function DeploymentsView({
|
|
|
26230
26517
|
] })
|
|
26231
26518
|
] });
|
|
26232
26519
|
}
|
|
26233
|
-
function statusLabel(
|
|
26234
|
-
switch (
|
|
26520
|
+
function statusLabel(row2) {
|
|
26521
|
+
switch (row2.status) {
|
|
26235
26522
|
case "pending":
|
|
26236
26523
|
return "Venter";
|
|
26237
26524
|
case "building":
|
|
@@ -26239,7 +26526,7 @@ function statusLabel(row) {
|
|
|
26239
26526
|
case "ready":
|
|
26240
26527
|
return "Publisert";
|
|
26241
26528
|
case "error":
|
|
26242
|
-
return
|
|
26529
|
+
return row2.error_message?.includes("webhook") ? "Utløpt" : "Feilet";
|
|
26243
26530
|
case "canceled":
|
|
26244
26531
|
return "Avbrutt";
|
|
26245
26532
|
}
|
|
@@ -26400,20 +26687,20 @@ function nodeChildren(node) {
|
|
|
26400
26687
|
}
|
|
26401
26688
|
}
|
|
26402
26689
|
const ANIMATION_PRESETS = ["fadeIn", "slideUp", "stagger", "scrollReveal"];
|
|
26403
|
-
function validateBlockTree(
|
|
26690
|
+
function validateBlockTree(input2) {
|
|
26404
26691
|
const issues = [];
|
|
26405
|
-
if (!isRecord(
|
|
26692
|
+
if (!isRecord(input2)) {
|
|
26406
26693
|
return fail("$", "Block tree must be an object");
|
|
26407
26694
|
}
|
|
26408
|
-
if (
|
|
26695
|
+
if (input2.version !== "1") {
|
|
26409
26696
|
issues.push({ path: "$.version", message: 'Expected version "1"' });
|
|
26410
26697
|
}
|
|
26411
|
-
if (!Array.isArray(
|
|
26698
|
+
if (!Array.isArray(input2.root)) {
|
|
26412
26699
|
return fail("$.root", "root must be an array of block nodes");
|
|
26413
26700
|
}
|
|
26414
26701
|
const seenIds = /* @__PURE__ */ new Set();
|
|
26415
|
-
for (let i = 0; i <
|
|
26416
|
-
validateNode(
|
|
26702
|
+
for (let i = 0; i < input2.root.length; i++) {
|
|
26703
|
+
validateNode(input2.root[i], `root[${i}]`, seenIds, issues);
|
|
26417
26704
|
}
|
|
26418
26705
|
return issues.length ? { ok: false, issues } : { ok: true };
|
|
26419
26706
|
}
|
|
@@ -26563,14 +26850,14 @@ function isRecord(value) {
|
|
|
26563
26850
|
function fail(path, message) {
|
|
26564
26851
|
return { ok: false, issues: [{ path, message }] };
|
|
26565
26852
|
}
|
|
26566
|
-
function parseBlockTree(
|
|
26567
|
-
const result = validateBlockTree(
|
|
26853
|
+
function parseBlockTree(input2) {
|
|
26854
|
+
const result = validateBlockTree(input2);
|
|
26568
26855
|
if (!result.ok) {
|
|
26569
26856
|
const first = result.issues[0];
|
|
26570
26857
|
const detail = first ? `${first.path}: ${first.message}` : "unknown";
|
|
26571
26858
|
throw new Error(`Invalid block tree at ${detail}`);
|
|
26572
26859
|
}
|
|
26573
|
-
return
|
|
26860
|
+
return input2;
|
|
26574
26861
|
}
|
|
26575
26862
|
const IconRegistryContext = createContext(null);
|
|
26576
26863
|
const RepeaterScopeContext = createContext(null);
|
|
@@ -26856,6 +27143,7 @@ export {
|
|
|
26856
27143
|
SettoAnimation,
|
|
26857
27144
|
SettoBlock,
|
|
26858
27145
|
SettoBlockTree,
|
|
27146
|
+
SettoChrome,
|
|
26859
27147
|
SettoForm,
|
|
26860
27148
|
SettoIcon,
|
|
26861
27149
|
SettoImage,
|
|
@@ -26872,6 +27160,7 @@ export {
|
|
|
26872
27160
|
isPreviewHost,
|
|
26873
27161
|
nodeChildren,
|
|
26874
27162
|
parseBlockTree,
|
|
27163
|
+
previewBarState,
|
|
26875
27164
|
readLocaleFields,
|
|
26876
27165
|
settoSubmissionUrl,
|
|
26877
27166
|
useGuestEdit,
|