@sevenfold/setto-client 0.12.0 → 0.13.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.
@@ -5041,26 +5041,26 @@ var PostgrestClient = class PostgrestClient2 {
5041
5041
  this.schemaName = schema;
5042
5042
  this.urlLengthLimit = urlLengthLimit;
5043
5043
  const originalFetch = fetch$1 !== null && fetch$1 !== void 0 ? fetch$1 : globalThis.fetch;
5044
- if (timeout !== void 0 && timeout > 0) this.fetch = (input2, init) => {
5044
+ if (timeout !== void 0 && timeout > 0) this.fetch = (input, init) => {
5045
5045
  const controller = new AbortController();
5046
5046
  const timeoutId = setTimeout(() => controller.abort(), timeout);
5047
5047
  const existingSignal = init === null || init === void 0 ? void 0 : init.signal;
5048
5048
  if (existingSignal) {
5049
5049
  if (existingSignal.aborted) {
5050
5050
  clearTimeout(timeoutId);
5051
- return originalFetch(input2, init);
5051
+ return originalFetch(input, init);
5052
5052
  }
5053
5053
  const abortHandler = () => {
5054
5054
  clearTimeout(timeoutId);
5055
5055
  controller.abort();
5056
5056
  };
5057
5057
  existingSignal.addEventListener("abort", abortHandler, { once: true });
5058
- return originalFetch(input2, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => {
5058
+ return originalFetch(input, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => {
5059
5059
  clearTimeout(timeoutId);
5060
5060
  existingSignal.removeEventListener("abort", abortHandler);
5061
5061
  });
5062
5062
  }
5063
- return originalFetch(input2, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => clearTimeout(timeoutId));
5063
+ return originalFetch(input, _objectSpread2$2(_objectSpread2$2({}, init), {}, { signal: controller.signal })).finally(() => clearTimeout(timeoutId));
5064
5064
  };
5065
5065
  else this.fetch = originalFetch;
5066
5066
  this.retry = retry;
@@ -20184,25 +20184,25 @@ const fetchWithAuth = (supabaseKey, supabaseUrl, getAccessToken, customFetch, tr
20184
20184
  const traceEnabled = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.enabled) === true;
20185
20185
  const respectSampling = (tracePropagationOptions === null || tracePropagationOptions === void 0 ? void 0 : tracePropagationOptions.respectSamplingDecision) !== false;
20186
20186
  const traceTargets = traceEnabled ? getDefaultPropagationTargets(supabaseUrl) : null;
20187
- return async (input2, init) => {
20187
+ return async (input, init) => {
20188
20188
  var _await$getAccessToken;
20189
20189
  const accessToken = (_await$getAccessToken = await getAccessToken()) !== null && _await$getAccessToken !== void 0 ? _await$getAccessToken : supabaseKey;
20190
20190
  let headers = new HeadersConstructor(init === null || init === void 0 ? void 0 : init.headers);
20191
20191
  if (!headers.has("apikey")) headers.set("apikey", supabaseKey);
20192
20192
  if (!headers.has("Authorization")) headers.set("Authorization", `Bearer ${accessToken}`);
20193
20193
  if (traceTargets) {
20194
- const traceHeaders = await getTraceHeaders(input2, traceTargets, respectSampling);
20194
+ const traceHeaders = await getTraceHeaders(input, traceTargets, respectSampling);
20195
20195
  if (traceHeaders) {
20196
20196
  if (traceHeaders.traceparent && !headers.has("traceparent")) headers.set("traceparent", traceHeaders.traceparent);
20197
20197
  if (traceHeaders.tracestate && !headers.has("tracestate")) headers.set("tracestate", traceHeaders.tracestate);
20198
20198
  if (traceHeaders.baggage && !headers.has("baggage")) headers.set("baggage", traceHeaders.baggage);
20199
20199
  }
20200
20200
  }
20201
- return fetch$1(input2, _objectSpread2(_objectSpread2({}, init), {}, { headers }));
20201
+ return fetch$1(input, _objectSpread2(_objectSpread2({}, init), {}, { headers }));
20202
20202
  };
20203
20203
  };
20204
- async function getTraceHeaders(input2, targets, respectSampling) {
20205
- if (!shouldPropagateToTarget(typeof input2 === "string" ? input2 : input2 instanceof URL ? input2 : input2.url, targets)) return null;
20204
+ async function getTraceHeaders(input, targets, respectSampling) {
20205
+ if (!shouldPropagateToTarget(typeof input === "string" ? input : input instanceof URL ? input : input.url, targets)) return null;
20206
20206
  const traceContext = await extractTraceContext();
20207
20207
  if (!traceContext || !traceContext.traceparent) return null;
20208
20208
  if (respectSampling) {
@@ -21312,14 +21312,14 @@ class ThemeStore {
21312
21312
  for (const fn of this.listeners) fn(this.cachedSnapshot);
21313
21313
  }
21314
21314
  }
21315
- const EMPTY$1 = {
21315
+ const EMPTY = {
21316
21316
  sites: null,
21317
21317
  currentSite: null,
21318
21318
  loading: true,
21319
21319
  error: null
21320
21320
  };
21321
21321
  function useSiteAccess(supabase, session, siteId) {
21322
- const [state, setState] = useState(EMPTY$1);
21322
+ const [state, setState] = useState(EMPTY);
21323
21323
  const cachedUserIdRef = useRef(null);
21324
21324
  useEffect(() => {
21325
21325
  if (!session) {
@@ -22185,13 +22185,13 @@ function isDeploymentInProgress(status) {
22185
22185
  }
22186
22186
  const STALE_PENDING_MS = 20 * 60 * 1e3;
22187
22187
  const STALE_BUILDING_MS = 45 * 60 * 1e3;
22188
- function isDeploymentStale(row2) {
22189
- if (row2.status !== "pending" && row2.status !== "building") return false;
22188
+ function isDeploymentStale(row) {
22189
+ if (row.status !== "pending" && row.status !== "building") return false;
22190
22190
  const now = Date.now();
22191
- if (row2.status === "pending") {
22192
- return now - new Date(row2.started_at).getTime() > STALE_PENDING_MS;
22191
+ if (row.status === "pending") {
22192
+ return now - new Date(row.started_at).getTime() > STALE_PENDING_MS;
22193
22193
  }
22194
- return now - new Date(row2.updated_at).getTime() > STALE_BUILDING_MS;
22194
+ return now - new Date(row.updated_at).getTime() > STALE_BUILDING_MS;
22195
22195
  }
22196
22196
  async function fetchInProgressDeploymentId(supabase, siteId) {
22197
22197
  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);
@@ -22209,7 +22209,7 @@ async function waitForDeploymentByCommit(supabase, siteId, commitSha, timeoutMs
22209
22209
  return null;
22210
22210
  }
22211
22211
  function useDeploymentStatus(supabase, deploymentId) {
22212
- const [row2, setRow] = useState(null);
22212
+ const [row, setRow] = useState(null);
22213
22213
  useEffect(() => {
22214
22214
  if (!deploymentId) {
22215
22215
  setRow(null);
@@ -22239,7 +22239,7 @@ function useDeploymentStatus(supabase, deploymentId) {
22239
22239
  if (channel) supabase.removeChannel(channel);
22240
22240
  };
22241
22241
  }, [supabase, deploymentId]);
22242
- return { row: row2, status: row2?.status ?? null };
22242
+ return { row, status: row?.status ?? null };
22243
22243
  }
22244
22244
  function deploymentStepIndex(status) {
22245
22245
  switch (status) {
@@ -22378,14 +22378,14 @@ function PublishProgressBar({ status, compact, href }) {
22378
22378
  return /* @__PURE__ */ jsx("div", { style: wrapStyle, children: inner });
22379
22379
  }
22380
22380
  function PublishDialog({
22381
- row: row2,
22381
+ row,
22382
22382
  status,
22383
22383
  onClose,
22384
22384
  onComplete
22385
22385
  }) {
22386
22386
  const buildingStartedAt = useRef(null);
22387
22387
  const [elapsed, setElapsed] = useState(0);
22388
- const progressStatus = row2?.status ?? status ?? "pending";
22388
+ const progressStatus = row?.status ?? status ?? "pending";
22389
22389
  const errored = progressStatus === "error" || progressStatus === "canceled";
22390
22390
  const isReady = progressStatus === "ready";
22391
22391
  const isBuilding = progressStatus === "building";
@@ -22412,7 +22412,7 @@ function PublishDialog({
22412
22412
  /* @__PURE__ */ jsx("h2", { style: { margin: "0 0 12px", fontSize: 18, fontWeight: 600, color: "#dc2626" }, children: "Publisering feilet" }),
22413
22413
  /* @__PURE__ */ jsxs("p", { style: { margin: "0 0 20px", fontSize: 14, lineHeight: 1.5, color: "#444" }, children: [
22414
22414
  progressStatus === "error" ? "Bygg feilet." : "Avbrutt.",
22415
- row2?.error_message ? ` ${row2.error_message}` : ""
22415
+ row?.error_message ? ` ${row.error_message}` : ""
22416
22416
  ] }),
22417
22417
  /* @__PURE__ */ jsx("button", { type: "button", onClick: onComplete, style: secondaryBtnStyle$1, children: "OK" })
22418
22418
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -22493,625 +22493,10 @@ function useEditBaseline() {
22493
22493
  };
22494
22494
  }, [session, store, config.siteId]);
22495
22495
  }
22496
- const EMPTY = {
22497
- host: null,
22498
- verified: false,
22499
- status: null,
22500
- instructions: [],
22501
- statusLine: null
22502
- };
22503
- function Copyable({ value }) {
22504
- const [copied, setCopied] = useState(false);
22505
- return /* @__PURE__ */ jsx(
22506
- "button",
22507
- {
22508
- type: "button",
22509
- onClick: () => {
22510
- void navigator.clipboard?.writeText(value).then(
22511
- () => {
22512
- setCopied(true);
22513
- window.setTimeout(() => setCopied(false), 1400);
22514
- },
22515
- () => void 0
22516
- );
22517
- },
22518
- title: "Kopier",
22519
- style: {
22520
- font: "inherit",
22521
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
22522
- background: "none",
22523
- border: 0,
22524
- padding: 0,
22525
- cursor: "pointer",
22526
- color: "inherit",
22527
- textAlign: "left",
22528
- wordBreak: "break-all"
22529
- },
22530
- children: copied ? "Kopiert" : value
22531
- }
22532
- );
22533
- }
22534
- function DomainView({ siteId }) {
22535
- const { api } = useSetto();
22536
- const [state, setState] = useState(EMPTY);
22537
- const [host, setHost] = useState("");
22538
- const [loading, setLoading] = useState(true);
22539
- const [busy, setBusy] = useState(false);
22540
- const [error, setError] = useState(null);
22541
- const load = async () => {
22542
- try {
22543
- setState(await api.request(`/onboarding/sites/${siteId}/domain`, {}, "domain"));
22544
- } catch {
22545
- setState(EMPTY);
22546
- } finally {
22547
- setLoading(false);
22548
- }
22549
- };
22550
- useEffect(() => {
22551
- void load();
22552
- }, [siteId]);
22553
- async function attach() {
22554
- setBusy(true);
22555
- setError(null);
22556
- try {
22557
- setState(
22558
- await api.request(
22559
- `/onboarding/sites/${siteId}/domain`,
22560
- { method: "POST", body: { host } },
22561
- "attachDomain"
22562
- )
22563
- );
22564
- setHost("");
22565
- } catch (err) {
22566
- const detail = err instanceof Error ? err.message : "";
22567
- setError(
22568
- 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."
22569
- );
22570
- } finally {
22571
- setBusy(false);
22572
- }
22573
- }
22574
- async function recheck() {
22575
- setBusy(true);
22576
- setError(null);
22577
- try {
22578
- setState(
22579
- await api.request(
22580
- `/onboarding/sites/${siteId}/domain/verify`,
22581
- { method: "POST" },
22582
- "verifyDomain"
22583
- )
22584
- );
22585
- } catch {
22586
- setError("Klarte ikke sjekke akkurat nå. Prøv igjen om litt.");
22587
- } finally {
22588
- setBusy(false);
22589
- }
22590
- }
22591
- async function detach() {
22592
- setBusy(true);
22593
- setError(null);
22594
- try {
22595
- setState(
22596
- await api.request(
22597
- `/onboarding/sites/${siteId}/domain`,
22598
- { method: "DELETE" },
22599
- "detachDomain"
22600
- )
22601
- );
22602
- } catch {
22603
- setError("Klarte ikke fjerne domenet.");
22604
- } finally {
22605
- setBusy(false);
22606
- }
22607
- }
22608
- if (loading) return /* @__PURE__ */ jsx("p", { style: muted, children: "Henter …" });
22609
- if (!state.host) {
22610
- return /* @__PURE__ */ jsxs("div", { style: wrap, children: [
22611
- /* @__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." }),
22612
- /* @__PURE__ */ jsxs("div", { style: row, children: [
22613
- /* @__PURE__ */ jsx(
22614
- "input",
22615
- {
22616
- value: host,
22617
- onChange: (e) => setHost(e.target.value),
22618
- placeholder: "dittfirma.no",
22619
- "aria-label": "Domenet ditt",
22620
- autoCapitalize: "off",
22621
- autoCorrect: "off",
22622
- spellCheck: false,
22623
- style: input
22624
- }
22625
- ),
22626
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void attach(), disabled: busy || !host.trim(), style: primary, children: busy ? "Legger til …" : "Legg til domene" })
22627
- ] }),
22628
- error && /* @__PURE__ */ jsx("p", { style: errorStyle$2, children: error })
22629
- ] });
22630
- }
22631
- return /* @__PURE__ */ jsxs("div", { style: wrap, children: [
22632
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "0.6rem", flexWrap: "wrap" }, children: [
22633
- /* @__PURE__ */ jsx("strong", { style: { fontSize: "1.05rem" }, children: state.host }),
22634
- /* @__PURE__ */ jsx("span", { style: state.verified ? liveBadge : pendingBadge, children: state.verified ? "Live" : "Venter på DNS" })
22635
- ] }),
22636
- state.statusLine && /* @__PURE__ */ jsx("p", { style: lead, children: state.statusLine }),
22637
- state.instructions.length > 0 && !state.verified && /* @__PURE__ */ jsxs(Fragment, { children: [
22638
- /* @__PURE__ */ jsx("p", { style: muted, children: "Logg inn der du kjøpte domenet, finn DNS-innstillingene, og legg inn dette:" }),
22639
- /* @__PURE__ */ jsx("div", { style: { display: "grid", gap: "0.6rem" }, children: state.instructions.map((rec) => /* @__PURE__ */ jsxs("div", { style: card, children: [
22640
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1.2rem", flexWrap: "wrap" }, children: [
22641
- /* @__PURE__ */ jsx(Field, { label: "Type", value: rec.type }),
22642
- /* @__PURE__ */ jsx(Field, { label: "Navn", value: rec.name }),
22643
- /* @__PURE__ */ jsx(Field, { label: "Peker til", value: rec.value, wide: true })
22644
- ] }),
22645
- /* @__PURE__ */ jsx("p", { style: { ...muted, margin: "0.35rem 0 0" }, children: rec.why })
22646
- ] }, `${rec.type}-${rec.name}`)) })
22647
- ] }),
22648
- error && /* @__PURE__ */ jsx("p", { style: errorStyle$2, children: error }),
22649
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.6rem", flexWrap: "wrap" }, children: [
22650
- !state.verified && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void recheck(), disabled: busy, style: primary, children: busy ? "Sjekker …" : "Jeg har lagt inn radene" }),
22651
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void detach(), disabled: busy, style: quiet, children: "Fjern domenet" })
22652
- ] })
22653
- ] });
22654
- }
22655
- function Field({ label, value, wide }) {
22656
- return /* @__PURE__ */ jsxs("div", { style: { minWidth: wide ? "14rem" : "5rem", flex: wide ? "1 1 14rem" : "0 0 auto" }, children: [
22657
- /* @__PURE__ */ jsx("div", { style: { ...muted, fontSize: "0.72rem", textTransform: "uppercase", letterSpacing: "0.04em" }, children: label }),
22658
- /* @__PURE__ */ jsx(Copyable, { value })
22659
- ] });
22660
- }
22661
- const wrap = { display: "grid", gap: "0.9rem", maxWidth: "42rem" };
22662
- const lead = { margin: 0, lineHeight: 1.5 };
22663
- const muted = { margin: 0, opacity: 0.65, lineHeight: 1.5 };
22664
- const row = { display: "flex", gap: "0.6rem", flexWrap: "wrap" };
22665
- const input = {
22666
- flex: "1 1 14rem",
22667
- padding: "0.6rem 0.75rem",
22668
- border: "1px solid rgba(0,0,0,0.18)",
22669
- borderRadius: "0.5rem",
22670
- font: "inherit"
22671
- };
22672
- const card = {
22673
- padding: "0.7rem 0.85rem",
22674
- border: "1px solid rgba(0,0,0,0.12)",
22675
- borderRadius: "0.6rem",
22676
- background: "rgba(0,0,0,0.02)"
22677
- };
22678
- const primary = {
22679
- padding: "0.6rem 1.1rem",
22680
- borderRadius: "999px",
22681
- border: 0,
22682
- background: "#c4502a",
22683
- color: "#fff",
22684
- font: "inherit",
22685
- fontWeight: 600,
22686
- cursor: "pointer"
22687
- };
22688
- const quiet = {
22689
- padding: "0.6rem 0.9rem",
22690
- borderRadius: "999px",
22691
- border: "1px solid rgba(0,0,0,0.18)",
22692
- background: "transparent",
22693
- font: "inherit",
22694
- cursor: "pointer"
22695
- };
22696
- const errorStyle$2 = { margin: 0, color: "#b42318" };
22697
- const liveBadge = {
22698
- fontSize: "0.75rem",
22699
- padding: "0.15rem 0.5rem",
22700
- borderRadius: "999px",
22701
- background: "rgba(15,123,79,0.12)",
22702
- color: "#0f7b4f"
22703
- };
22704
- const pendingBadge = {
22705
- fontSize: "0.75rem",
22706
- padding: "0.15rem 0.5rem",
22707
- borderRadius: "999px",
22708
- background: "rgba(196,80,42,0.12)",
22709
- color: "#c4502a"
22710
- };
22711
22496
  const domainModule = {
22712
22497
  id: "domain",
22713
- label: "Domene",
22714
- AdminView: DomainView
22715
- };
22716
- const shellStyle$1 = {
22717
- minHeight: "100dvh",
22718
- background: "#0a0a0d",
22719
- color: "#fff",
22720
- fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
22721
- };
22722
- const headerStyle = {
22723
- display: "flex",
22724
- alignItems: "center",
22725
- justifyContent: "space-between",
22726
- padding: "16px 24px",
22727
- borderBottom: "1px solid #1c1c22"
22498
+ label: "Domene"
22728
22499
  };
22729
- const mainStyle = {
22730
- maxWidth: 720,
22731
- margin: "0 auto",
22732
- padding: 24,
22733
- display: "flex",
22734
- flexDirection: "column",
22735
- gap: 16
22736
- };
22737
- const cardStyle = {
22738
- background: "#15151a",
22739
- borderRadius: 12,
22740
- padding: 20,
22741
- display: "flex",
22742
- flexDirection: "column",
22743
- gap: 12
22744
- };
22745
- const panelStyle = {
22746
- background: "#1a1a20",
22747
- borderRadius: 8,
22748
- padding: 14,
22749
- display: "flex",
22750
- flexDirection: "column",
22751
- gap: 10
22752
- };
22753
- const rowStyle = {
22754
- display: "flex",
22755
- alignItems: "center",
22756
- gap: 10,
22757
- background: "#1a1a20",
22758
- padding: "6px 10px",
22759
- borderRadius: 6
22760
- };
22761
- const mutedStyle = {
22762
- color: "#888",
22763
- fontSize: 13,
22764
- margin: 0
22765
- };
22766
- const errStyle = {
22767
- color: "#ff7a7a",
22768
- fontSize: 13,
22769
- margin: 0
22770
- };
22771
- const subheadingStyle = {
22772
- margin: 0,
22773
- fontSize: 14,
22774
- color: "#aaa"
22775
- };
22776
- const primaryBtnStyle = {
22777
- display: "inline-block",
22778
- background: "#640AFF",
22779
- color: "#fff",
22780
- border: "none",
22781
- borderRadius: 6,
22782
- padding: "10px 14px",
22783
- fontSize: 14,
22784
- fontWeight: 500,
22785
- textDecoration: "none",
22786
- cursor: "pointer"
22787
- };
22788
- const secondaryBtnStyle = {
22789
- background: "transparent",
22790
- color: "#bda6ff",
22791
- border: "1px solid #2a2a32",
22792
- borderRadius: 6,
22793
- padding: "6px 10px",
22794
- fontSize: 12,
22795
- cursor: "pointer"
22796
- };
22797
- const dangerBtnStyle = {
22798
- alignSelf: "flex-start",
22799
- background: "transparent",
22800
- color: "#ff7a7a",
22801
- border: "1px solid #4a3030",
22802
- borderRadius: 6,
22803
- padding: "8px 14px",
22804
- fontSize: 13,
22805
- cursor: "pointer"
22806
- };
22807
- const inputStyle$2 = {
22808
- width: "100%",
22809
- boxSizing: "border-box",
22810
- background: "#0f0f13",
22811
- color: "#fff",
22812
- border: "1px solid #2a2a32",
22813
- borderRadius: 6,
22814
- padding: "8px 10px",
22815
- fontSize: 13,
22816
- fontFamily: "inherit"
22817
- };
22818
- const tabBarStyle = {
22819
- display: "flex",
22820
- gap: 4,
22821
- borderBottom: "1px solid #1c1c22",
22822
- paddingBottom: 0
22823
- };
22824
- function tabStyle(active) {
22825
- return {
22826
- background: "transparent",
22827
- border: "none",
22828
- borderBottom: active ? "2px solid #640AFF" : "2px solid transparent",
22829
- color: active ? "#fff" : "#888",
22830
- padding: "8px 12px",
22831
- fontSize: 13,
22832
- fontWeight: 500,
22833
- cursor: "pointer",
22834
- marginBottom: -1
22835
- };
22836
- }
22837
- function FormsView({ siteId }) {
22838
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
22839
- /* @__PURE__ */ jsx(RecipientsPanel, { siteId }),
22840
- /* @__PURE__ */ jsx(SubmissionList, { siteId })
22841
- ] });
22842
- }
22843
- function RecipientsPanel({ siteId }) {
22844
- const { api } = useSetto();
22845
- const [settings, setSettings] = useState(null);
22846
- const [editing, setEditing] = useState(false);
22847
- const [draft, setDraft] = useState("");
22848
- const [saving, setSaving] = useState(false);
22849
- const [error, setError] = useState(null);
22850
- useEffect(() => {
22851
- let cancelled = false;
22852
- api.forms.getSettings(siteId).then((s) => {
22853
- if (!cancelled) setSettings(s);
22854
- }).catch((err) => {
22855
- if (!cancelled) setError(err instanceof Error ? err.message : "Kunne ikke hente innstillinger");
22856
- });
22857
- return () => {
22858
- cancelled = true;
22859
- };
22860
- }, [api, siteId]);
22861
- const startEditing = () => {
22862
- setDraft((settings?.recipients ?? []).join("\n"));
22863
- setEditing(true);
22864
- setError(null);
22865
- };
22866
- const save = async (recipients) => {
22867
- setSaving(true);
22868
- setError(null);
22869
- try {
22870
- const next = await api.forms.saveSettings(siteId, recipients);
22871
- setSettings(next);
22872
- setEditing(false);
22873
- } catch (err) {
22874
- setError(err instanceof Error ? err.message : "Kunne ikke lagre");
22875
- } finally {
22876
- setSaving(false);
22877
- }
22878
- };
22879
- const parseDraft = () => draft.split(/[\n,;]+/).map((s) => s.trim()).filter(Boolean);
22880
- return /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
22881
- /* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: 15 }, children: "Hvem får e-post?" }),
22882
- error ? /* @__PURE__ */ jsx("p", { style: errStyle, children: error }) : null,
22883
- settings === null && !error ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster …" }) : null,
22884
- settings && !editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
22885
- /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: 14 }, children: settings.effectiveRecipients.length > 0 ? settings.effectiveRecipients.join(", ") : /* @__PURE__ */ jsx("span", { style: { color: "#ff7a7a" }, children: "Ingen mottakere — innsendinger lagres, men ingen får beskjed." }) }),
22886
- /* @__PURE__ */ jsx("p", { style: mutedStyle, children: settings.recipients.length === 0 ? "Standard: eierne av siden. Hver innsending lagres også her i historikken." : `Standard er ${settings.defaultRecipients.join(", ") || "ingen"}.` }),
22887
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
22888
- /* @__PURE__ */ jsx("button", { type: "button", onClick: startEditing, style: secondaryBtnStyle, children: "Endre mottakere" }),
22889
- settings.recipients.length > 0 ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void save([]), disabled: saving, style: secondaryBtnStyle, children: "Bruk standard" }) : null
22890
- ] })
22891
- ] }) : null,
22892
- settings && editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
22893
- /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: 6 }, children: [
22894
- /* @__PURE__ */ jsx("span", { style: mutedStyle, children: "E-postadresser, én per linje. Tomt betyr standard (eierne)." }),
22895
- /* @__PURE__ */ jsx(
22896
- "textarea",
22897
- {
22898
- value: draft,
22899
- rows: 3,
22900
- onChange: (e) => setDraft(e.target.value),
22901
- style: { ...inputStyle$2, resize: "vertical" },
22902
- placeholder: settings.defaultRecipients.join("\n"),
22903
- autoFocus: true
22904
- }
22905
- )
22906
- ] }),
22907
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
22908
- /* @__PURE__ */ jsx(
22909
- "button",
22910
- {
22911
- type: "button",
22912
- onClick: () => void save(parseDraft()),
22913
- disabled: saving,
22914
- style: { ...primaryBtnStyle, padding: "8px 12px", fontSize: 13 },
22915
- children: saving ? "Lagrer …" : "Lagre"
22916
- }
22917
- ),
22918
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setEditing(false), disabled: saving, style: secondaryBtnStyle, children: "Avbryt" })
22919
- ] })
22920
- ] }) : null
22921
- ] });
22922
- }
22923
- const PAGE = 50;
22924
- function SubmissionList({ siteId }) {
22925
- const { supabase, api } = useSetto();
22926
- const [rows, setRows] = useState(null);
22927
- const [error, setError] = useState(null);
22928
- const [openId, setOpenId] = useState(null);
22929
- const [hasMore, setHasMore] = useState(false);
22930
- const [loadingMore, setLoadingMore] = useState(false);
22931
- useEffect(() => {
22932
- let cancelled = false;
22933
- supabase.from("form_submissions").select("*").eq("site_id", siteId).order("created_at", { ascending: false }).limit(PAGE).then(({ data, error: err }) => {
22934
- if (cancelled) return;
22935
- if (err) setError(err.message);
22936
- const list = data ?? [];
22937
- setRows(list);
22938
- setHasMore(list.length === PAGE);
22939
- });
22940
- const channel = supabase.channel(`setto-site-forms-${siteId}`).on(
22941
- "postgres_changes",
22942
- { event: "*", schema: "public", table: "form_submissions", filter: `site_id=eq.${siteId}` },
22943
- (payload) => {
22944
- const next = payload.new;
22945
- if (!next || !next.id) return;
22946
- setRows((prev) => {
22947
- if (!prev) return [next];
22948
- const without = prev.filter((r) => r.id !== next.id);
22949
- return [next, ...without].sort((a, b) => b.created_at.localeCompare(a.created_at));
22950
- });
22951
- }
22952
- ).subscribe();
22953
- return () => {
22954
- cancelled = true;
22955
- supabase.removeChannel(channel);
22956
- };
22957
- }, [supabase, siteId]);
22958
- const loadMore = async () => {
22959
- if (!rows || rows.length === 0) return;
22960
- setLoadingMore(true);
22961
- const oldest = rows[rows.length - 1].created_at;
22962
- const { data, error: err } = await supabase.from("form_submissions").select("*").eq("site_id", siteId).lt("created_at", oldest).order("created_at", { ascending: false }).limit(PAGE);
22963
- setLoadingMore(false);
22964
- if (err) {
22965
- setError(err.message);
22966
- return;
22967
- }
22968
- const more = data ?? [];
22969
- setRows((prev) => [...prev ?? [], ...more]);
22970
- setHasMore(more.length === PAGE);
22971
- };
22972
- const remove2 = async (id) => {
22973
- if (!window.confirm("Slette denne henvendelsen? Dette kan ikke angres.")) return;
22974
- try {
22975
- await api.forms.deleteSubmission(siteId, id);
22976
- setRows((prev) => (prev ?? []).filter((r) => r.id !== id));
22977
- if (openId === id) setOpenId(null);
22978
- } catch (err) {
22979
- setError(err instanceof Error ? err.message : "Kunne ikke slette");
22980
- }
22981
- };
22982
- if (!rows) return /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster henvendelser …" });
22983
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
22984
- /* @__PURE__ */ jsx("h3", { style: subheadingStyle, children: "Henvendelser" }),
22985
- error ? /* @__PURE__ */ jsx("p", { style: errStyle, children: error }) : null,
22986
- 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(
22987
- SubmissionItem,
22988
- {
22989
- row: row2,
22990
- open: openId === row2.id,
22991
- onToggle: () => setOpenId(openId === row2.id ? null : row2.id),
22992
- onDelete: () => void remove2(row2.id)
22993
- },
22994
- row2.id
22995
- )) }),
22996
- hasMore ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void loadMore(), disabled: loadingMore, style: secondaryBtnStyle, children: loadingMore ? "Laster …" : "Vis eldre" }) : null
22997
- ] });
22998
- }
22999
- function SubmissionItem({
23000
- row: row2,
23001
- open,
23002
- onToggle,
23003
- onDelete
23004
- }) {
23005
- const fields = submissionFields(row2);
23006
- const sender = senderOf(fields);
23007
- const title = row2.meta.title ?? row2.form_id;
23008
- return /* @__PURE__ */ jsxs("li", { children: [
23009
- /* @__PURE__ */ jsxs(
23010
- "button",
23011
- {
23012
- type: "button",
23013
- onClick: onToggle,
23014
- "aria-expanded": open,
23015
- style: { ...rowStyle, width: "100%", textAlign: "left", color: "inherit", border: "none", cursor: "pointer", font: "inherit" },
23016
- children: [
23017
- /* @__PURE__ */ jsx("span", { style: statusDotStyle(row2.email_status), title: emailStatusLabel(row2) }),
23018
- /* @__PURE__ */ jsxs("span", { style: { flex: 1, fontSize: 13, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
23019
- sender ?? /* @__PURE__ */ jsx("span", { style: { color: "#aaa" }, children: "Uten navn" }),
23020
- /* @__PURE__ */ jsxs("span", { style: { color: "#888" }, children: [
23021
- " · ",
23022
- title
23023
- ] })
23024
- ] }),
23025
- /* @__PURE__ */ jsx("time", { style: { fontSize: 11, color: "#888", flexShrink: 0 }, children: formatWhen(row2.created_at) })
23026
- ]
23027
- }
23028
- ),
23029
- open ? /* @__PURE__ */ jsxs("div", { style: { ...panelStyle, marginTop: 4, gap: 12 }, children: [
23030
- /* @__PURE__ */ jsx("dl", { style: { margin: 0, display: "grid", gap: 10 }, children: fields.map((f) => /* @__PURE__ */ jsxs("div", { children: [
23031
- /* @__PURE__ */ jsx("dt", { style: { fontSize: 11, color: "#888", textTransform: "uppercase", letterSpacing: "0.04em" }, children: f.label }),
23032
- /* @__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 })
23033
- ] }, f.name)) }),
23034
- /* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
23035
- emailStatusLabel(row2),
23036
- row2.recipients.length > 0 ? ` til ${row2.recipients.join(", ")}` : "",
23037
- row2.email_error ? ` — ${row2.email_error}` : "",
23038
- row2.meta.page ? /* @__PURE__ */ jsxs(Fragment, { children: [
23039
- " · ",
23040
- /* @__PURE__ */ jsx("a", { href: row2.meta.page, style: { color: "#bda6ff" }, target: "_blank", rel: "noreferrer", children: "Sendt fra siden" })
23041
- ] }) : null
23042
- ] }),
23043
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onDelete, style: dangerBtnStyle, children: "Slett henvendelse" })
23044
- ] }) : null
23045
- ] });
23046
- }
23047
- function submissionFields(row2) {
23048
- const flat = [];
23049
- flatten(row2.data, "", flat);
23050
- const rank = new Map((row2.field_order ?? []).map((n, i) => [n, i]));
23051
- const rankOf = (name) => rank.get(name) ?? rank.get(name.split(".")[0]) ?? Number.MAX_SAFE_INTEGER;
23052
- return flat.map((f, i) => ({ f, i })).sort((a, b) => rankOf(a.f.name) - rankOf(b.f.name) || a.i - b.i).map(({ f }) => ({
23053
- name: f.name,
23054
- label: row2.labels?.[f.name] ?? row2.labels?.[f.name.split(".")[0]] ?? prettify(f.name),
23055
- value: f.value
23056
- }));
23057
- }
23058
- function flatten(value, path, out) {
23059
- if (value === null || value === void 0) {
23060
- out.push({ name: path, value: "" });
23061
- } else if (typeof value === "boolean") {
23062
- out.push({ name: path, value: value ? "Ja" : "Nei" });
23063
- } else if (typeof value === "string" || typeof value === "number") {
23064
- out.push({ name: path, value: String(value) });
23065
- } else if (Array.isArray(value)) {
23066
- if (value.every((v) => typeof v !== "object" || v === null)) {
23067
- out.push({ name: path, value: value.map((v) => v == null ? "" : String(v)).filter(Boolean).join(", ") });
23068
- } else {
23069
- value.forEach((item, i) => flatten(item, `${path}.${i + 1}`, out));
23070
- }
23071
- } else if (typeof value === "object") {
23072
- for (const [k, v] of Object.entries(value)) {
23073
- flatten(v, path ? `${path}.${k}` : k, out);
23074
- }
23075
- }
23076
- }
23077
- function prettify(name) {
23078
- const leaf = name.split(".").pop() ?? name;
23079
- const words = leaf.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-.]+/g, " ").trim().toLowerCase();
23080
- return words ? words[0].toUpperCase() + words.slice(1) : name;
23081
- }
23082
- const EMAIL_RE$1 = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
23083
- function senderOf(fields) {
23084
- const named = fields.find(
23085
- (f) => /(^|[._-])(navn|name|fullname|full_name|fornavn|firstname|first_name)$/i.test(f.name) && f.value && !EMAIL_RE$1.test(f.value)
23086
- );
23087
- if (named) return named.value.slice(0, 80);
23088
- const email = fields.find((f) => EMAIL_RE$1.test(f.value));
23089
- if (email) return email.value;
23090
- const first = fields.find((f) => f.value !== "");
23091
- return first ? first.value.slice(0, 80) : null;
23092
- }
23093
- function emailStatusLabel(row2) {
23094
- switch (row2.email_status) {
23095
- case "sent":
23096
- return "E-post sendt";
23097
- case "failed":
23098
- return "E-post feilet";
23099
- case "skipped":
23100
- return "Ingen mottakere";
23101
- case "pending":
23102
- return "Sender e-post";
23103
- }
23104
- }
23105
- function formatWhen(iso) {
23106
- const d = new Date(iso);
23107
- const now = /* @__PURE__ */ new Date();
23108
- const sameDay = d.toDateString() === now.toDateString();
23109
- return sameDay ? d.toLocaleTimeString("nb-NO", { hour: "2-digit", minute: "2-digit" }) : d.toLocaleDateString("nb-NO", { day: "numeric", month: "short" }) + " " + d.toLocaleTimeString("nb-NO", { hour: "2-digit", minute: "2-digit" });
23110
- }
23111
- function statusDotStyle(status) {
23112
- const colour = status === "sent" ? "#34d399" : status === "failed" ? "#ff7a7a" : status === "skipped" ? "#fbbf24" : "#888";
23113
- return { width: 8, height: 8, borderRadius: "50%", background: colour, flexShrink: 0 };
23114
- }
23115
22500
  const MARGIN = 10;
23116
22501
  const OFFSET = 8;
23117
22502
  const MIN_TOUCH = 44;
@@ -24538,13 +23923,9 @@ const previewHintStyle = {
24538
23923
  };
24539
23924
  const formsModule = {
24540
23925
  id: "forms",
24541
- label: "Skjemaer",
24542
- AdminView: FormsView
23926
+ label: "Skjemaer"
24543
23927
  };
24544
23928
  const SETTO_MODULES = [formsModule, domainModule];
24545
- function findModule(id) {
24546
- return id ? SETTO_MODULES.find((m) => m.id === id) : void 0;
24547
- }
24548
23929
  function SettoMark({
24549
23930
  size = 24,
24550
23931
  letterColor = "#F2EBDF",
@@ -25236,13 +24617,13 @@ const PREVIEW_HOST = /(^|\.)site\.setto\.no$/i;
25236
24617
  function isPreviewHost(hostname) {
25237
24618
  return PREVIEW_HOST.test(hostname);
25238
24619
  }
25239
- function previewBarState(input2) {
25240
- if (!input2.onPreview || input2.authLoading) return "hidden";
25241
- if (input2.hasSession && !input2.sitesLoaded) return "hidden";
25242
- if (!input2.hasSession && input2.soldPublic === null) return "hidden";
25243
- const bought = input2.paidMarker || input2.currentSiteIsLive || input2.soldPublic === true;
24620
+ function previewBarState(input) {
24621
+ if (!input.onPreview || input.authLoading) return "hidden";
24622
+ if (input.hasSession && !input.sitesLoaded) return "hidden";
24623
+ if (!input.hasSession && input.soldPublic === null) return "hidden";
24624
+ const bought = input.paidMarker || input.currentSiteIsLive || input.soldPublic === true;
25244
24625
  if (bought) return "hidden";
25245
- return input2.hasSession && input2.hasCurrentSite ? "owner" : "visitor";
24626
+ return input.hasSession && input.hasCurrentSite ? "owner" : "visitor";
25246
24627
  }
25247
24628
  function SettoPreviewBar() {
25248
24629
  const { supabase, api, config, session, authLoading, currentSite, sites, editMode } = useSetto();
@@ -26267,7 +25648,7 @@ function AuthGate({ children }) {
26267
25648
  };
26268
25649
  if (mode === "set_password") {
26269
25650
  const callback = authCallbackType();
26270
- return /* @__PURE__ */ jsx("div", { style: shellStyle, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitSetPassword, style: formStyle, children: [
25651
+ return /* @__PURE__ */ jsx("div", { style: shellStyle$1, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitSetPassword, style: formStyle, children: [
26271
25652
  /* @__PURE__ */ jsx("h1", { style: { margin: 0, fontSize: 22 }, children: "Setto" }),
26272
25653
  /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#888", fontSize: 13 }, children: callback === "invite" ? "Velg et passord for å fullføre invitasjonen." : "Velg et nytt passord." }),
26273
25654
  /* @__PURE__ */ jsx(
@@ -26302,7 +25683,7 @@ function AuthGate({ children }) {
26302
25683
  ] }) });
26303
25684
  }
26304
25685
  if (mode === "forgot") {
26305
- return /* @__PURE__ */ jsx("div", { style: shellStyle, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitForgot, style: formStyle, children: [
25686
+ return /* @__PURE__ */ jsx("div", { style: shellStyle$1, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitForgot, style: formStyle, children: [
26306
25687
  /* @__PURE__ */ jsx("h1", { style: { margin: 0, fontSize: 22 }, children: "Setto" }),
26307
25688
  /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#888", fontSize: 13 }, children: "Skriv inn e-posten din, så sender vi en lenke for å nullstille passordet." }),
26308
25689
  /* @__PURE__ */ jsx(
@@ -26335,7 +25716,7 @@ function AuthGate({ children }) {
26335
25716
  )
26336
25717
  ] }) });
26337
25718
  }
26338
- return /* @__PURE__ */ jsx("div", { style: shellStyle, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitSignIn, style: formStyle, children: [
25719
+ return /* @__PURE__ */ jsx("div", { style: shellStyle$1, children: /* @__PURE__ */ jsxs("form", { onSubmit: submitSignIn, style: formStyle, children: [
26339
25720
  /* @__PURE__ */ jsx("h1", { style: { margin: 0, fontSize: 22 }, children: "Setto" }),
26340
25721
  /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#888", fontSize: 13 }, children: "Logg inn for å redigere innholdet. Har du fått invitasjon, bruk lenken i e-posten første gang." }),
26341
25722
  /* @__PURE__ */ jsx(
@@ -26380,7 +25761,7 @@ function AuthGate({ children }) {
26380
25761
  )
26381
25762
  ] }) });
26382
25763
  }
26383
- const shellStyle = {
25764
+ const shellStyle$1 = {
26384
25765
  minHeight: "100dvh",
26385
25766
  display: "flex",
26386
25767
  alignItems: "center",
@@ -26425,178 +25806,72 @@ const linkStyle = {
26425
25806
  cursor: "pointer"
26426
25807
  };
26427
25808
  const loadingStyle = {
26428
- ...shellStyle,
25809
+ ...shellStyle$1,
26429
25810
  fontSize: 14,
26430
25811
  color: "#888"
26431
25812
  };
26432
- function deploymentAdminUrl(deploymentId) {
26433
- const u = new URL(window.location.href);
26434
- u.pathname = SETTO_BASE;
26435
- u.search = "";
26436
- u.searchParams.set("deployment", deploymentId);
26437
- return u.pathname + u.search;
26438
- }
26439
- function focusedDeploymentId() {
26440
- return new URLSearchParams(window.location.search).get("deployment");
26441
- }
26442
- function DeploymentsView({
26443
- siteId,
26444
- onError
26445
- }) {
26446
- const { supabase, api, config } = useSetto();
26447
- const [rows, setRows] = useState(null);
26448
- const [focusId, setFocusId] = useState(focusedDeploymentId);
26449
- const [canceling, setCanceling] = useState(false);
26450
- const [cancelError, setCancelError] = useState(null);
26451
- const { row: focusRow } = useDeploymentStatus(supabase, focusId);
26452
- useEffect(() => {
26453
- const onPopState = () => setFocusId(focusedDeploymentId());
26454
- window.addEventListener("popstate", onPopState);
26455
- return () => window.removeEventListener("popstate", onPopState);
26456
- }, []);
26457
- const cancelDeployment = async () => {
26458
- if (!focusId) return;
26459
- setCanceling(true);
26460
- setCancelError(null);
26461
- try {
26462
- await api.cancelDeployment(config.siteId, focusId);
26463
- setFocusId(null);
26464
- window.history.replaceState(null, "", SETTO_BASE);
26465
- } catch (err) {
26466
- setCancelError(err instanceof Error ? err.message : "Kunne ikke avbryte");
26467
- } finally {
26468
- setCanceling(false);
26469
- }
26470
- };
26471
- useEffect(() => {
26472
- void api.expireStaleDeployments(config.siteId).catch(() => {
26473
- });
26474
- }, [api, config.siteId]);
26475
- useEffect(() => {
26476
- let cancelled = false;
26477
- supabase.from("deployments").select("*").eq("site_id", siteId).order("started_at", { ascending: false }).limit(10).then(({ data, error }) => {
26478
- if (cancelled) return;
26479
- if (error) onError?.(error.message);
26480
- setRows(data ?? []);
26481
- });
26482
- const channel = supabase.channel(`setto-site-deps-${siteId}`).on(
26483
- "postgres_changes",
26484
- { event: "*", schema: "public", table: "deployments", filter: `site_id=eq.${siteId}` },
26485
- (payload) => {
26486
- const next = payload.new;
26487
- if (!next) return;
26488
- setRows((prev) => {
26489
- if (!prev) return [next];
26490
- const without = prev.filter((r) => r.id !== next.id);
26491
- return [next, ...without].slice(0, 10);
26492
- });
26493
- }
26494
- ).subscribe();
26495
- return () => {
26496
- cancelled = true;
26497
- supabase.removeChannel(channel);
26498
- };
26499
- }, [supabase, siteId, onError]);
26500
- const showFocusPanel = focusId !== null && (focusRow === null || isDeploymentInProgress(focusRow.status));
26501
- if (!rows) return /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster deploys…" });
26502
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
26503
- showFocusPanel ? /* @__PURE__ */ jsxs("section", { style: panelStyle, children: [
26504
- /* @__PURE__ */ jsx("h3", { style: { margin: 0, fontSize: 15 }, children: "Publisering pågår" }),
26505
- focusRow ? /* @__PURE__ */ jsxs(Fragment, { children: [
26506
- /* @__PURE__ */ jsx(PublishProgressBar, { status: focusRow.status }),
26507
- /* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
26508
- "Commit ",
26509
- /* @__PURE__ */ jsx("code", { style: { color: "#bda6ff" }, children: focusRow.commit_sha.slice(0, 7) }),
26510
- focusRow.vercel_deployment_id ? /* @__PURE__ */ jsxs(Fragment, { children: [
26511
- " ",
26512
- "· Vercel",
26513
- " ",
26514
- /* @__PURE__ */ jsxs("code", { style: { color: "#888", fontSize: 11 }, children: [
26515
- focusRow.vercel_deployment_id.slice(0, 12),
26516
- "…"
26517
- ] })
26518
- ] }) : null
26519
- ] }),
26520
- cancelError ? /* @__PURE__ */ jsx("p", { style: errStyle, children: cancelError }) : null,
26521
- /* @__PURE__ */ jsx(
26522
- "button",
26523
- {
26524
- type: "button",
26525
- onClick: () => void cancelDeployment(),
26526
- disabled: canceling,
26527
- style: dangerBtnStyle,
26528
- children: canceling ? "Avbryter …" : "Avbryt publisering"
26529
- }
26530
- )
26531
- ] }) : /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster status …" })
26532
- ] }) : null,
26533
- rows.length === 0 ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Ingen deploys ennå." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
26534
- /* @__PURE__ */ jsx("h3", { style: subheadingStyle, children: "Historikk" }),
26535
- /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 6 }, children: rows.map((r) => {
26536
- const focused = r.id === focusId;
26537
- const inProgress = isDeploymentInProgress(r.status);
26538
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
26539
- "a",
26540
- {
26541
- href: inProgress ? deploymentAdminUrl(r.id) : void 0,
26542
- style: {
26543
- ...rowStyle,
26544
- textDecoration: "none",
26545
- color: "inherit",
26546
- outline: focused ? "1px solid #640AFF" : void 0,
26547
- cursor: inProgress ? "pointer" : "default"
26548
- },
26549
- onClick: inProgress ? (e) => {
26550
- e.preventDefault();
26551
- setFocusId(r.id);
26552
- window.history.pushState(null, "", deploymentAdminUrl(r.id));
26553
- } : void 0,
26554
- children: [
26555
- /* @__PURE__ */ jsx("span", { style: depDotStyle(r.status) }),
26556
- /* @__PURE__ */ jsx("code", { style: { fontSize: 12, color: "#aaa" }, children: r.commit_sha.slice(0, 7) }),
26557
- /* @__PURE__ */ jsx("span", { style: { flex: 1, fontSize: 12 }, children: statusLabel(r) }),
26558
- /* @__PURE__ */ jsx("time", { style: { fontSize: 11, color: "#888" }, children: new Date(r.started_at).toLocaleString() })
26559
- ]
26560
- }
26561
- ) }, r.id);
26562
- }) })
26563
- ] })
26564
- ] });
26565
- }
26566
- function statusLabel(row2) {
26567
- switch (row2.status) {
26568
- case "pending":
26569
- return "Venter";
26570
- case "building":
26571
- return "Bygger";
26572
- case "ready":
26573
- return "Publisert";
26574
- case "error":
26575
- return row2.error_message?.includes("webhook") ? "Utløpt" : "Feilet";
26576
- case "canceled":
26577
- return "Avbrutt";
26578
- }
26579
- }
26580
- function depDotStyle(status) {
26581
- const colour = status === "ready" ? "#34d399" : status === "error" || status === "canceled" ? "#ff7a7a" : status === "building" ? "#fbbf24" : "#888";
26582
- return {
26583
- width: 8,
26584
- height: 8,
26585
- borderRadius: "50%",
26586
- background: colour,
26587
- flexShrink: 0
26588
- };
26589
- }
25813
+ const shellStyle = {
25814
+ minHeight: "100dvh",
25815
+ background: "#0a0a0d",
25816
+ color: "#fff",
25817
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
25818
+ };
25819
+ const headerStyle = {
25820
+ display: "flex",
25821
+ alignItems: "center",
25822
+ justifyContent: "space-between",
25823
+ padding: "16px 24px",
25824
+ borderBottom: "1px solid #1c1c22"
25825
+ };
25826
+ const mainStyle = {
25827
+ maxWidth: 720,
25828
+ margin: "0 auto",
25829
+ padding: 24,
25830
+ display: "flex",
25831
+ flexDirection: "column",
25832
+ gap: 16
25833
+ };
25834
+ const cardStyle = {
25835
+ background: "#15151a",
25836
+ borderRadius: 12,
25837
+ padding: 20,
25838
+ display: "flex",
25839
+ flexDirection: "column",
25840
+ gap: 12
25841
+ };
25842
+ const mutedStyle = {
25843
+ color: "#888",
25844
+ fontSize: 13,
25845
+ margin: 0
25846
+ };
25847
+ const primaryBtnStyle = {
25848
+ display: "inline-block",
25849
+ background: "#640AFF",
25850
+ color: "#fff",
25851
+ border: "none",
25852
+ borderRadius: 6,
25853
+ padding: "10px 14px",
25854
+ fontSize: 14,
25855
+ fontWeight: 500,
25856
+ textDecoration: "none",
25857
+ cursor: "pointer"
25858
+ };
25859
+ const secondaryBtnStyle = {
25860
+ background: "transparent",
25861
+ color: "#bda6ff",
25862
+ border: "1px solid #2a2a32",
25863
+ borderRadius: 6,
25864
+ padding: "6px 10px",
25865
+ fontSize: 12,
25866
+ cursor: "pointer"
25867
+ };
26590
25868
  function SettoAdminApp() {
26591
25869
  const Chrome = useSettoAdminChrome();
26592
25870
  if (Chrome) return /* @__PURE__ */ jsx(Chrome, {});
26593
- return /* @__PURE__ */ jsx(AuthGate, { children: /* @__PURE__ */ jsx(SiteList, {}) });
25871
+ return /* @__PURE__ */ jsx(AuthGate, { children: /* @__PURE__ */ jsx(SiteDoor, {}) });
26594
25872
  }
26595
- const DEPLOYMENTS_VIEW = "deployments";
26596
- function SiteList() {
25873
+ function SiteDoor() {
26597
25874
  const { supabase, session, sites, currentSite, config } = useSetto();
26598
- const [error, setError] = useState(null);
26599
- const [view, setView] = useState(() => currentAdminView() ?? DEPLOYMENTS_VIEW);
26600
25875
  const redirectAfterSignIn = useRef(false);
26601
25876
  useEffect(() => {
26602
25877
  const { data: sub } = supabase.auth.onAuthStateChange((event) => {
@@ -26619,76 +25894,26 @@ function SiteList() {
26619
25894
  }
26620
25895
  redirectAfterSignIn.current = false;
26621
25896
  }, [session, sites, currentSite]);
26622
- useEffect(() => {
26623
- const onPopState = () => setView(currentAdminView() ?? DEPLOYMENTS_VIEW);
26624
- window.addEventListener("popstate", onPopState);
26625
- return () => window.removeEventListener("popstate", onPopState);
26626
- }, []);
26627
- const showView = (id) => {
26628
- setView(id);
26629
- setError(null);
26630
- window.history.pushState(null, "", id === DEPLOYMENTS_VIEW ? SETTO_BASE : adminViewUrl(id));
26631
- };
26632
25897
  if (redirectAfterSignIn.current && session && (sites === null || currentSite)) {
26633
25898
  return /* @__PURE__ */ jsx("div", { style: loadingRedirectStyle, children: "Åpner redigering …" });
26634
25899
  }
26635
- const activeModule = findModule(view);
26636
- return /* @__PURE__ */ jsxs("div", { style: shellStyle$1, children: [
25900
+ return /* @__PURE__ */ jsxs("div", { style: shellStyle, children: [
26637
25901
  /* @__PURE__ */ jsxs("header", { style: headerStyle, children: [
26638
25902
  /* @__PURE__ */ jsx("h1", { style: { margin: 0, fontSize: 22 }, children: "Setto" }),
26639
25903
  /* @__PURE__ */ jsx(SignOutButton, {})
26640
25904
  ] }),
26641
25905
  /* @__PURE__ */ jsxs("main", { style: mainStyle, children: [
26642
- error ? /* @__PURE__ */ jsx("p", { style: errStyle, children: error }) : null,
26643
25906
  sites === null ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster sider…" }) : null,
26644
25907
  currentSite ? /* @__PURE__ */ jsxs("section", { style: cardStyle, children: [
26645
25908
  /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 18 }, children: currentSite.display_name }),
26646
- /* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
26647
- currentSite.repo_owner,
26648
- "/",
26649
- currentSite.repo_name,
26650
- " · branch ",
26651
- currentSite.branch
26652
- ] }),
26653
25909
  /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8 }, children: /* @__PURE__ */ jsx("a", { href: siteHomeUrl(), style: primaryBtnStyle, children: "Begynn å redigere" }) }),
26654
- /* @__PURE__ */ jsxs("nav", { style: tabBarStyle, "aria-label": "Visninger", children: [
26655
- /* @__PURE__ */ jsx(
26656
- "button",
26657
- {
26658
- type: "button",
26659
- style: tabStyle(!activeModule),
26660
- "aria-current": !activeModule ? "page" : void 0,
26661
- onClick: () => showView(DEPLOYMENTS_VIEW),
26662
- children: "Publiseringer"
26663
- }
26664
- ),
26665
- SETTO_MODULES.map((m) => /* @__PURE__ */ jsx(
26666
- "button",
26667
- {
26668
- type: "button",
26669
- style: tabStyle(activeModule?.id === m.id),
26670
- "aria-current": activeModule?.id === m.id ? "page" : void 0,
26671
- onClick: () => showView(m.id),
26672
- children: m.label
26673
- },
26674
- m.id
26675
- ))
26676
- ] }),
26677
- activeModule ? /* @__PURE__ */ jsx(activeModule.AdminView, { siteId: currentSite.id }) : /* @__PURE__ */ jsx(DeploymentsView, { siteId: currentSite.id, onError: setError })
25910
+ /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Publiseringer, skjemaer og domene er ikke tilgjengelig akkurat nå. Last siden på nytt om litt — redigering virker som vanlig i mellomtiden." })
26678
25911
  ] }) : sites && sites.length === 0 ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Du er ikke medlem av noen sider ennå. Be administratoren legge deg til." }) : null,
26679
- sites && !currentSite && sites.length > 0 ? /* @__PURE__ */ jsxs("section", { style: cardStyle, children: [
26680
- /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 16 }, children: "Andre sider du har tilgang til" }),
26681
- /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0 }, children: sites.map((s) => /* @__PURE__ */ jsxs("li", { style: { padding: "6px 0" }, children: [
26682
- /* @__PURE__ */ jsx("code", { style: { color: "#bda6ff" }, children: s.id }),
26683
- " — ",
26684
- s.display_name
26685
- ] }, s.id)) }),
26686
- /* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
26687
- "Du er logget inn på denne siden (",
26688
- config.siteId,
26689
- "), men er ikke medlem her. Spør administrator om tilgang."
26690
- ] })
26691
- ] }) : null
25912
+ sites && !currentSite && sites.length > 0 ? /* @__PURE__ */ jsx("section", { style: cardStyle, children: /* @__PURE__ */ jsxs("p", { style: mutedStyle, children: [
25913
+ "Du er logget inn på denne siden (",
25914
+ config.siteId,
25915
+ "), men er ikke medlem her. Spør administrator om tilgang."
25916
+ ] }) }) : null
26692
25917
  ] })
26693
25918
  ] });
26694
25919
  }
@@ -26735,20 +25960,20 @@ function nodeChildren(node) {
26735
25960
  }
26736
25961
  }
26737
25962
  const ANIMATION_PRESETS = ["fadeIn", "slideUp", "stagger", "scrollReveal"];
26738
- function validateBlockTree(input2) {
25963
+ function validateBlockTree(input) {
26739
25964
  const issues = [];
26740
- if (!isRecord(input2)) {
25965
+ if (!isRecord(input)) {
26741
25966
  return fail("$", "Block tree must be an object");
26742
25967
  }
26743
- if (input2.version !== "1") {
25968
+ if (input.version !== "1") {
26744
25969
  issues.push({ path: "$.version", message: 'Expected version "1"' });
26745
25970
  }
26746
- if (!Array.isArray(input2.root)) {
25971
+ if (!Array.isArray(input.root)) {
26747
25972
  return fail("$.root", "root must be an array of block nodes");
26748
25973
  }
26749
25974
  const seenIds = /* @__PURE__ */ new Set();
26750
- for (let i = 0; i < input2.root.length; i++) {
26751
- validateNode(input2.root[i], `root[${i}]`, seenIds, issues);
25975
+ for (let i = 0; i < input.root.length; i++) {
25976
+ validateNode(input.root[i], `root[${i}]`, seenIds, issues);
26752
25977
  }
26753
25978
  return issues.length ? { ok: false, issues } : { ok: true };
26754
25979
  }
@@ -26898,14 +26123,14 @@ function isRecord(value) {
26898
26123
  function fail(path, message) {
26899
26124
  return { ok: false, issues: [{ path, message }] };
26900
26125
  }
26901
- function parseBlockTree(input2) {
26902
- const result = validateBlockTree(input2);
26126
+ function parseBlockTree(input) {
26127
+ const result = validateBlockTree(input);
26903
26128
  if (!result.ok) {
26904
26129
  const first = result.issues[0];
26905
26130
  const detail = first ? `${first.path}: ${first.message}` : "unknown";
26906
26131
  throw new Error(`Invalid block tree at ${detail}`);
26907
26132
  }
26908
- return input2;
26133
+ return input;
26909
26134
  }
26910
26135
  const IconRegistryContext = createContext(null);
26911
26136
  const RepeaterScopeContext = createContext(null);
@@ -27187,6 +26412,7 @@ export {
27187
26412
  FORM_FIELD_TYPE_LABELS,
27188
26413
  GuestEditProvider,
27189
26414
  IN_PROGRESS_STATUSES,
26415
+ PublishProgressBar,
27190
26416
  SETTO_BASE,
27191
26417
  SETTO_MODULES,
27192
26418
  STALE_BUILDING_MS,