your-ai-workflow-firebase-os 1.2.38 → 1.2.39
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.
|
@@ -10501,14 +10501,48 @@ var qa = {
|
|
|
10501
10501
|
tabTitle: "Team Page",
|
|
10502
10502
|
template: "none",
|
|
10503
10503
|
form: ""
|
|
10504
|
-
}, Za = [
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10504
|
+
}, Za = [
|
|
10505
|
+
{
|
|
10506
|
+
id: "home",
|
|
10507
|
+
title: "Home",
|
|
10508
|
+
path: "/",
|
|
10509
|
+
type: "public",
|
|
10510
|
+
isSystem: !0,
|
|
10511
|
+
enabled: !0
|
|
10512
|
+
},
|
|
10513
|
+
{
|
|
10514
|
+
id: "contact",
|
|
10515
|
+
title: "Contact",
|
|
10516
|
+
path: "/contact",
|
|
10517
|
+
type: "public",
|
|
10518
|
+
isSystem: !0,
|
|
10519
|
+
enabled: !0
|
|
10520
|
+
},
|
|
10521
|
+
{
|
|
10522
|
+
id: "guide",
|
|
10523
|
+
title: "Guide",
|
|
10524
|
+
path: "/guide",
|
|
10525
|
+
type: "public",
|
|
10526
|
+
isSystem: !0,
|
|
10527
|
+
enabled: !0
|
|
10528
|
+
},
|
|
10529
|
+
{
|
|
10530
|
+
id: "templates",
|
|
10531
|
+
title: "Templates",
|
|
10532
|
+
path: "/templates",
|
|
10533
|
+
type: "public",
|
|
10534
|
+
isSystem: !0,
|
|
10535
|
+
enabled: !0
|
|
10536
|
+
},
|
|
10537
|
+
{
|
|
10538
|
+
id: "support",
|
|
10539
|
+
title: "Support",
|
|
10540
|
+
path: "/support",
|
|
10541
|
+
type: "user",
|
|
10542
|
+
isSystem: !0,
|
|
10543
|
+
enabled: !0
|
|
10544
|
+
}
|
|
10545
|
+
], Qa = (e) => [...e, ...Za.filter((t) => !e.some((e) => e.id === t.id))], $a = {
|
|
10512
10546
|
public: "Anyone",
|
|
10513
10547
|
user: "Signed-in users",
|
|
10514
10548
|
internal: "Your team",
|
|
@@ -10593,26 +10627,43 @@ var ao = "Add it here — its web address, a name, and what you would like to se
|
|
|
10593
10627
|
let n = String(t?.route || "").trim();
|
|
10594
10628
|
return n ? n.startsWith("/") ? n : "/" + n : e.path || "";
|
|
10595
10629
|
}, mo = (e, t) => po(e, t).replace(/^\/+/, "") ? String(t?.brief || "").trim() ? "" : "Say what you would like to see on this page, and this sentence is ready to copy." : "Give this page a web address and it will be created — then this sentence is ready to copy.", ho = (e, t) => {
|
|
10596
|
-
let n = po(e, t), r = String(t?.tabName || t?.pageName || "").trim(), i = r === "New page" ? "" : r, a = String(t?.template || ""), o = String(t?.form || "").trim(), s = String(t?.brief || "").trim(), c = [
|
|
10597
|
-
|
|
10598
|
-
|
|
10630
|
+
let n = po(e, t), r = String(t?.tabName || t?.pageName || "").trim(), i = r === "New page" ? "" : r, a = String(t?.template || ""), o = String(t?.form || "").trim(), s = String(t?.brief || "").trim(), c = [
|
|
10631
|
+
`My app already has a page${i ? ` called ${i}` : ""} at ${n}. It is already made and it is already in the menu — do not create a page, a web address or a settings file for it, and do not edit any other file.`,
|
|
10632
|
+
`Write one file: ${fo(e, t)}, with a default export. That file is what the page shows.`,
|
|
10633
|
+
"Make it match the rest of my app: style with the app’s own theme styles so it looks right in light and dark. Do not pick your own colors, and do not add a <style> tag or any global CSS."
|
|
10634
|
+
];
|
|
10635
|
+
return so.includes(a) && c.push(`The page already starts from the ${co[a]} layout — build on it, do not rebuild it.`), o && c.push(`Show my form ${So(o)} on it with <ContactPopup formId='${o}' /> — never write a form by hand.`), c.push(`Here is what I would like on it: ${s || "___"}`), c.join("\n");
|
|
10636
|
+
}, go = {
|
|
10637
|
+
home: {
|
|
10638
|
+
title: "Change the home page",
|
|
10639
|
+
prompt: "On my home page, change the big heading to Bright Studio and the line under it to We design websites for small businesses. Add a button that says Get in touch which opens my contact form as a pop-up, and below it three short sections: what we do, who it is for, and how to reach us."
|
|
10640
|
+
},
|
|
10641
|
+
contact: {
|
|
10642
|
+
title: "Change the contact page",
|
|
10643
|
+
prompt: "My app already has a contact page at /contact and a contact form. Show that form built into the page instead of as a pop-up, with a short line above it saying I reply within one working day. Write one file and nothing else: src/your-ai-workflow-firebase-os/microcomponents/ContactContent.tsx, with a default export that shows the form as <ContactPopup isInline formId='contact' />. Do not create a page, do not create a form, and do not change the questions the form asks."
|
|
10644
|
+
},
|
|
10645
|
+
support: {
|
|
10646
|
+
title: "Change the support page",
|
|
10647
|
+
prompt: "My app already has a support page at /support and a support form. Show that form built into the page instead of as a pop-up, with a short line above it saying what to include in a request. Write one file and nothing else: src/your-ai-workflow-firebase-os/microcomponents/SupportContent.tsx, with a default export that shows the form as <ContactPopup isInline formId='support' />. Do not create a page, do not create a form, and do not change the questions the form asks."
|
|
10648
|
+
}
|
|
10649
|
+
}, _o = (e, t) => {
|
|
10599
10650
|
let n = (e) => {
|
|
10600
10651
|
let { _uiKey: t, pageType: n, route: r, contentKey: i, brief: a, ...o } = e || {};
|
|
10601
10652
|
return o;
|
|
10602
10653
|
}, r = n(e), i = n(t), a = new Set([...Object.keys(r), ...Object.keys(i)]);
|
|
10603
10654
|
for (let e of a) if ((r[e] ?? "") !== (i[e] ?? "")) return !0;
|
|
10604
10655
|
return !1;
|
|
10605
|
-
},
|
|
10656
|
+
}, vo = (e, t) => {
|
|
10606
10657
|
let { _uiKey: n, ...r } = e || {};
|
|
10607
10658
|
return Object.keys(r).length > 0 ? {
|
|
10608
10659
|
...r,
|
|
10609
10660
|
pageType: t
|
|
10610
10661
|
} : { pageType: t };
|
|
10611
|
-
},
|
|
10662
|
+
}, yo = "w-6 h-5 rounded-md flex items-center justify-center text-foreground/35 hover:text-foreground hover:bg-foreground/[0.07] transition-colors cursor-pointer", bo = "hidden sm:inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-widest text-foreground/40 border border-[var(--panel-border)] px-2 py-0.5 rounded-md", xo = (e) => {
|
|
10612
10663
|
let t = typeof window < "u" ? window.location.host : "";
|
|
10613
10664
|
return t ? `${t}${e}` : e;
|
|
10614
|
-
},
|
|
10615
|
-
function
|
|
10665
|
+
}, So = (e) => e.replace(/[_-]+/g, " ").replace(/^./, (e) => e.toUpperCase());
|
|
10666
|
+
function Co({ page: e, config: t, forms: n, saving: r, onSave: i, addressTaken: a, actions: o, footer: s, isNew: c }) {
|
|
10616
10667
|
let l = e.type === "public", u = l ? "pageName" : "tabName", d = l ? "pageTitle" : "tabTitle", [f, p] = K(() => ({ ...t || {} })), [m, h] = K(""), g = (e, t) => p((n) => ({
|
|
10617
10668
|
...n,
|
|
10618
10669
|
[e]: t
|
|
@@ -10694,7 +10745,7 @@ function So({ page: e, config: t, forms: n, saving: r, onSave: i, addressTaken:
|
|
|
10694
10745
|
onChange: (e) => _({ form: e }),
|
|
10695
10746
|
options: n.map((e) => ({
|
|
10696
10747
|
value: e,
|
|
10697
|
-
label:
|
|
10748
|
+
label: So(e)
|
|
10698
10749
|
})),
|
|
10699
10750
|
placeholder: "No form"
|
|
10700
10751
|
}),
|
|
@@ -10718,7 +10769,7 @@ function So({ page: e, config: t, forms: n, saving: r, onSave: i, addressTaken:
|
|
|
10718
10769
|
]
|
|
10719
10770
|
});
|
|
10720
10771
|
}
|
|
10721
|
-
function
|
|
10772
|
+
function wo() {
|
|
10722
10773
|
let { userRole: t } = dn(), n = zt(), [r, i] = K(() => {
|
|
10723
10774
|
try {
|
|
10724
10775
|
if (typeof window < "u") {
|
|
@@ -10798,6 +10849,8 @@ function Co() {
|
|
|
10798
10849
|
"drive",
|
|
10799
10850
|
"assets",
|
|
10800
10851
|
"contact",
|
|
10852
|
+
"guide",
|
|
10853
|
+
"templates",
|
|
10801
10854
|
"support",
|
|
10802
10855
|
"theme",
|
|
10803
10856
|
"page_order",
|
|
@@ -11013,7 +11066,7 @@ function Co() {
|
|
|
11013
11066
|
...t,
|
|
11014
11067
|
[e.id]: !0
|
|
11015
11068
|
}));
|
|
11016
|
-
let r =
|
|
11069
|
+
let r = vo(f[e.id], t);
|
|
11017
11070
|
try {
|
|
11018
11071
|
await ht(J(D, "sys_tabs", e.id), r, { merge: !0 }), p((n) => n[e.id] ? {
|
|
11019
11072
|
...n,
|
|
@@ -11154,9 +11207,9 @@ function Co() {
|
|
|
11154
11207
|
});
|
|
11155
11208
|
}, 100);
|
|
11156
11209
|
}, he = async (e, t) => {
|
|
11157
|
-
if (e.
|
|
11210
|
+
if (e.isSystem) return;
|
|
11158
11211
|
let n = e.type === "public", i = n ? "page_order" : "tab_order", a = [...n ? A : j];
|
|
11159
|
-
r.filter((e) => e.type === "public" === n && e.
|
|
11212
|
+
r.filter((e) => e.type === "public" === n && !e.isSystem && !a.some((t) => t.id === e.id)).forEach((e) => a.push({
|
|
11160
11213
|
id: e.id,
|
|
11161
11214
|
hidden: !1
|
|
11162
11215
|
}));
|
|
@@ -11309,7 +11362,7 @@ function Co() {
|
|
|
11309
11362
|
let n = a.findIndex((t) => t.id === e.id), r = a.findIndex((e) => e.id === t.id);
|
|
11310
11363
|
return n === -1 && r === -1 ? 0 : n === -1 ? 1 : r === -1 ? -1 : n - r;
|
|
11311
11364
|
}), u = t.icon, d = (e) => e.type === "public" ? ee.has(e.id) : O.has(e.id), p = l.filter((e) => !d(e)), h = l.filter((e) => d(e)), g = (n, r) => {
|
|
11312
|
-
let i = n.id
|
|
11365
|
+
let i = n.isSystem ? go[n.id] : void 0, a = o === n.id, l = ce(n), u = !n.isSystem || !!i, d = l ? "Not saved yet — give it a web address" : r ? "Hidden" : n.type === "public" ? "Shown in the site menu" : "Shown in the dashboard";
|
|
11313
11366
|
return /* @__PURE__ */ Z("div", {
|
|
11314
11367
|
id: `page-${n.id}`,
|
|
11315
11368
|
onClick: () => {
|
|
@@ -11323,7 +11376,7 @@ function Co() {
|
|
|
11323
11376
|
className: "flex flex-col sm:flex-row sm:items-center justify-between w-full gap-3",
|
|
11324
11377
|
children: [/* @__PURE__ */ Z("div", {
|
|
11325
11378
|
className: "flex items-start gap-2 flex-1 min-w-0",
|
|
11326
|
-
children: [!
|
|
11379
|
+
children: [!n.isSystem && !l && /* @__PURE__ */ Z("div", {
|
|
11327
11380
|
className: "flex flex-col shrink-0 pt-0.5",
|
|
11328
11381
|
onClick: (e) => e.stopPropagation(),
|
|
11329
11382
|
children: [/* @__PURE__ */ X("button", {
|
|
@@ -11331,14 +11384,14 @@ function Co() {
|
|
|
11331
11384
|
title: "Move up",
|
|
11332
11385
|
"aria-label": `Move ${n.title} up`,
|
|
11333
11386
|
onClick: () => he(n, "up"),
|
|
11334
|
-
className:
|
|
11387
|
+
className: yo,
|
|
11335
11388
|
children: /* @__PURE__ */ X(Me, { className: "w-3.5 h-3.5" })
|
|
11336
11389
|
}), /* @__PURE__ */ X("button", {
|
|
11337
11390
|
type: "button",
|
|
11338
11391
|
title: "Move down",
|
|
11339
11392
|
"aria-label": `Move ${n.title} down`,
|
|
11340
11393
|
onClick: () => he(n, "down"),
|
|
11341
|
-
className:
|
|
11394
|
+
className: yo,
|
|
11342
11395
|
children: /* @__PURE__ */ X(B, { className: "w-3.5 h-3.5" })
|
|
11343
11396
|
})]
|
|
11344
11397
|
}), /* @__PURE__ */ Z("div", {
|
|
@@ -11349,13 +11402,13 @@ function Co() {
|
|
|
11349
11402
|
className: "text-[14px] font-bold text-foreground truncate",
|
|
11350
11403
|
children: n.title
|
|
11351
11404
|
}), n.fromFile && /* @__PURE__ */ Z("span", {
|
|
11352
|
-
className:
|
|
11405
|
+
className: bo,
|
|
11353
11406
|
title: "Your assistant built this one. Delete hides it and gives you the sentence that removes its files.",
|
|
11354
11407
|
children: [/* @__PURE__ */ X(e, { className: "w-3 h-3" }), " Built for you"]
|
|
11355
11408
|
})]
|
|
11356
11409
|
}), /* @__PURE__ */ Z("span", {
|
|
11357
11410
|
className: "text-[11.5px] font-medium text-foreground/45 truncate",
|
|
11358
|
-
children: [d, n.path ? ` · Web address ${
|
|
11411
|
+
children: [d, n.path ? ` · Web address ${xo(n.path)}` : ""]
|
|
11359
11412
|
})]
|
|
11360
11413
|
})]
|
|
11361
11414
|
}), /* @__PURE__ */ Z("div", {
|
|
@@ -11366,7 +11419,7 @@ function Co() {
|
|
|
11366
11419
|
busy: m[n.id],
|
|
11367
11420
|
onChange: (e) => ue(n, e)
|
|
11368
11421
|
}),
|
|
11369
|
-
|
|
11422
|
+
n.id !== "home" && !l && /* @__PURE__ */ X(Hn, {
|
|
11370
11423
|
icon: r ? pe : Le,
|
|
11371
11424
|
onClick: (e) => {
|
|
11372
11425
|
e.stopPropagation(), z(n.id, n.isSystem, n.type);
|
|
@@ -11396,8 +11449,8 @@ function Co() {
|
|
|
11396
11449
|
onClick: (e) => e.stopPropagation(),
|
|
11397
11450
|
children: /* @__PURE__ */ X(lr, {
|
|
11398
11451
|
compact: !0,
|
|
11399
|
-
title:
|
|
11400
|
-
prompt:
|
|
11452
|
+
title: i.title,
|
|
11453
|
+
prompt: i.prompt
|
|
11401
11454
|
})
|
|
11402
11455
|
}), a && !n.isSystem && f[n.id] && /* @__PURE__ */ X(H.div, {
|
|
11403
11456
|
initial: {
|
|
@@ -11414,7 +11467,7 @@ function Co() {
|
|
|
11414
11467
|
},
|
|
11415
11468
|
className: "overflow-hidden mt-4 pt-4 border-t border-[var(--panel-border)]/50 pb-1",
|
|
11416
11469
|
onClick: (e) => e.stopPropagation(),
|
|
11417
|
-
children: /* @__PURE__ */ X(
|
|
11470
|
+
children: /* @__PURE__ */ X(Co, {
|
|
11418
11471
|
page: n,
|
|
11419
11472
|
config: f[n.id],
|
|
11420
11473
|
forms: n.type === "public" ? F : ie,
|
|
@@ -11435,7 +11488,7 @@ function Co() {
|
|
|
11435
11488
|
onClick: () => me(n),
|
|
11436
11489
|
children: "Duplicate"
|
|
11437
11490
|
}),
|
|
11438
|
-
!n.fromFile &&
|
|
11491
|
+
!n.fromFile && _o(f[n.id], _e(n.type)) && /* @__PURE__ */ X(Hn, {
|
|
11439
11492
|
icon: s,
|
|
11440
11493
|
tone: "danger",
|
|
11441
11494
|
busy: v[n.id],
|
|
@@ -11536,7 +11589,7 @@ function Co() {
|
|
|
11536
11589
|
}
|
|
11537
11590
|
//#endregion
|
|
11538
11591
|
//#region src/pages/FormsAdmin.tsx
|
|
11539
|
-
var
|
|
11592
|
+
var To = new Set(["contact", "support"]), Eo = [
|
|
11540
11593
|
{
|
|
11541
11594
|
value: "text",
|
|
11542
11595
|
label: "Short text"
|
|
@@ -11569,33 +11622,32 @@ var wo = new Set(["contact", "support"]), To = [
|
|
|
11569
11622
|
value: "select",
|
|
11570
11623
|
label: "Choose one"
|
|
11571
11624
|
}
|
|
11572
|
-
],
|
|
11625
|
+
], Do = { age: "Age" }, Oo = (e) => Eo.find((t) => t.value === e)?.label || Do[e || ""] || "Short text", ko = (e) => Eo.some((t) => t.value === e) || !e ? Eo : [...Eo, {
|
|
11573
11626
|
value: e,
|
|
11574
|
-
label:
|
|
11575
|
-
}],
|
|
11627
|
+
label: Oo(e)
|
|
11628
|
+
}], Ao = {
|
|
11576
11629
|
public: "Anyone",
|
|
11577
11630
|
private: "Signed-in users"
|
|
11578
|
-
},
|
|
11631
|
+
}, jo = (e) => (e || "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, ""), Mo = (e, t) => {
|
|
11579
11632
|
if (!t.includes(e)) return e;
|
|
11580
11633
|
let n = 2;
|
|
11581
11634
|
for (; t.includes(`${e}_${n}`);) n++;
|
|
11582
11635
|
return `${e}_${n}`;
|
|
11583
|
-
},
|
|
11584
|
-
if (!(!e.name || e.name ===
|
|
11585
|
-
let r =
|
|
11586
|
-
return r ?
|
|
11587
|
-
},
|
|
11636
|
+
}, No = (e, t, n) => {
|
|
11637
|
+
if (!(!e.name || e.name === jo(e.label || ""))) return e.name;
|
|
11638
|
+
let r = jo(t);
|
|
11639
|
+
return r ? Mo(r, n.filter((t) => t !== e).map((e) => e.name)) : "";
|
|
11640
|
+
}, Po = () => ({
|
|
11588
11641
|
name: "",
|
|
11589
11642
|
type: "text",
|
|
11590
11643
|
label: "",
|
|
11591
11644
|
placeholder: "",
|
|
11592
11645
|
required: !1
|
|
11593
|
-
}),
|
|
11646
|
+
}), Fo = {
|
|
11594
11647
|
firstForm: "Add the form here — its fields and who can fill it in — then tell your assistant which page should use it.",
|
|
11595
|
-
forPage: (e, t) => `Use the form called ${e || t} (its id is ${t}) on the page I name — show it with <ContactPopup formId='${t}' />. Do not create a form, do not change this form's fields, do not write a form by hand
|
|
11596
|
-
more: "On my page at /contact, show my form called Get in touch built into the page instead of as a pop-up, and after sending take people to the home page. Do not create a form, do not change its questions, and keep everything else on the page as it is."
|
|
11648
|
+
forPage: (e, t) => `Use the form called ${e || t} (its id is ${t}) on the page I name — show it with <ContactPopup formId='${t}' />. Do not create a form, do not change this form's fields, do not write a form by hand.`
|
|
11597
11649
|
};
|
|
11598
|
-
function
|
|
11650
|
+
function Io({ choices: e, onChange: t }) {
|
|
11599
11651
|
return /* @__PURE__ */ X(Wn, {
|
|
11600
11652
|
label: "Choices",
|
|
11601
11653
|
hint: "The options people pick from.",
|
|
@@ -11632,7 +11684,7 @@ function Fo({ choices: e, onChange: t }) {
|
|
|
11632
11684
|
})
|
|
11633
11685
|
});
|
|
11634
11686
|
}
|
|
11635
|
-
function
|
|
11687
|
+
function Lo() {
|
|
11636
11688
|
let { userRole: e } = dn(), t = Bt(), n = S(), r = () => {
|
|
11637
11689
|
let e = (typeof window < "u" ? window.__FBOS_FORMS__ : null) || {}, t = {
|
|
11638
11690
|
...oe,
|
|
@@ -11798,7 +11850,7 @@ function Io() {
|
|
|
11798
11850
|
})), I(!1);
|
|
11799
11851
|
}
|
|
11800
11852
|
}, ae = async (e) => {
|
|
11801
|
-
if (!
|
|
11853
|
+
if (!To.has(e)) {
|
|
11802
11854
|
x(!0);
|
|
11803
11855
|
try {
|
|
11804
11856
|
await ot(J(D, "sys_forms", e)), a((t) => {
|
|
@@ -11817,7 +11869,7 @@ function Io() {
|
|
|
11817
11869
|
return;
|
|
11818
11870
|
}
|
|
11819
11871
|
x(!0);
|
|
11820
|
-
let t = (typeof window < "u" ? window.__FBOS_FORMS__ : null) || {}, i =
|
|
11872
|
+
let t = (typeof window < "u" ? window.__FBOS_FORMS__ : null) || {}, i = To.has(e) || !!t[e] || !!n?.defaultConfigs?.[e] || !!n?.defaultConfigs?.[`${e}Form`];
|
|
11821
11873
|
try {
|
|
11822
11874
|
i ? (u((t) => ({
|
|
11823
11875
|
...t,
|
|
@@ -11888,7 +11940,7 @@ function Io() {
|
|
|
11888
11940
|
" ",
|
|
11889
11941
|
/* @__PURE__ */ Z("span", {
|
|
11890
11942
|
className: "text-foreground/40 font-medium",
|
|
11891
|
-
children: ["· ",
|
|
11943
|
+
children: ["· ", Oo(n.type)]
|
|
11892
11944
|
})
|
|
11893
11945
|
]
|
|
11894
11946
|
}), /* @__PURE__ */ Z("div", {
|
|
@@ -11925,7 +11977,7 @@ function Io() {
|
|
|
11925
11977
|
placeholder: "e.g. What is your budget?",
|
|
11926
11978
|
onChange: (e) => a({
|
|
11927
11979
|
label: e,
|
|
11928
|
-
name:
|
|
11980
|
+
name: No(n, e, i)
|
|
11929
11981
|
}, !1),
|
|
11930
11982
|
onBlur: () => P(e)
|
|
11931
11983
|
}),
|
|
@@ -11933,7 +11985,7 @@ function Io() {
|
|
|
11933
11985
|
label: "Kind of answer",
|
|
11934
11986
|
hint: "Sets the box people type into and what counts as a valid answer.",
|
|
11935
11987
|
value: n.type || "text",
|
|
11936
|
-
options:
|
|
11988
|
+
options: ko(n.type),
|
|
11937
11989
|
allowEmpty: !1,
|
|
11938
11990
|
onChange: (e) => a(e === "select" ? {
|
|
11939
11991
|
type: e,
|
|
@@ -11946,7 +11998,7 @@ function Io() {
|
|
|
11946
11998
|
enabled: !!n.required,
|
|
11947
11999
|
onChange: (e) => a({ required: e }, !0)
|
|
11948
12000
|
}),
|
|
11949
|
-
n.type === "select" && /* @__PURE__ */ X(
|
|
12001
|
+
n.type === "select" && /* @__PURE__ */ X(Io, {
|
|
11950
12002
|
choices: n.options || [],
|
|
11951
12003
|
onChange: (e) => a({ options: e }, !0)
|
|
11952
12004
|
}),
|
|
@@ -11969,7 +12021,7 @@ function Io() {
|
|
|
11969
12021
|
]
|
|
11970
12022
|
}, r);
|
|
11971
12023
|
}, le = (e, t) => {
|
|
11972
|
-
let n = C[e] || t, r =
|
|
12024
|
+
let n = C[e] || t, r = To.has(e), i = n.formType || "public", a = n.fields || [], o = p.has(e);
|
|
11973
12025
|
return /* @__PURE__ */ Z("div", {
|
|
11974
12026
|
className: "flex flex-col gap-4",
|
|
11975
12027
|
children: [
|
|
@@ -12035,7 +12087,7 @@ function Io() {
|
|
|
12035
12087
|
description: "Everything this form asks for, in the order people answer it.",
|
|
12036
12088
|
actions: /* @__PURE__ */ X(Hn, {
|
|
12037
12089
|
icon: Se,
|
|
12038
|
-
onClick: () => F(e, n, [...a,
|
|
12090
|
+
onClick: () => F(e, n, [...a, Po()]),
|
|
12039
12091
|
children: "Add question"
|
|
12040
12092
|
}),
|
|
12041
12093
|
children: [a.length === 0 ? /* @__PURE__ */ X("p", {
|
|
@@ -12045,7 +12097,7 @@ function Io() {
|
|
|
12045
12097
|
className: "flex justify-start",
|
|
12046
12098
|
children: /* @__PURE__ */ X(Hn, {
|
|
12047
12099
|
icon: Se,
|
|
12048
|
-
onClick: () => F(e, n, [...a,
|
|
12100
|
+
onClick: () => F(e, n, [...a, Po()]),
|
|
12049
12101
|
children: "Add question"
|
|
12050
12102
|
})
|
|
12051
12103
|
})]
|
|
@@ -12073,7 +12125,7 @@ function Io() {
|
|
|
12073
12125
|
/* @__PURE__ */ X(lr, {
|
|
12074
12126
|
compact: !0,
|
|
12075
12127
|
title: "Ask your assistant to put this form on a page",
|
|
12076
|
-
prompt:
|
|
12128
|
+
prompt: Fo.forPage(n.title || "", e)
|
|
12077
12129
|
})
|
|
12078
12130
|
]
|
|
12079
12131
|
});
|
|
@@ -12145,109 +12197,101 @@ function Io() {
|
|
|
12145
12197
|
}),
|
|
12146
12198
|
/* @__PURE__ */ X("p", {
|
|
12147
12199
|
className: "text-[12.5px] text-foreground/55 font-medium leading-relaxed text-center max-w-md",
|
|
12148
|
-
children:
|
|
12200
|
+
children: Fo.firstForm
|
|
12149
12201
|
})
|
|
12150
12202
|
]
|
|
12151
12203
|
})
|
|
12152
12204
|
}) : /* @__PURE__ */ Z("div", {
|
|
12153
12205
|
className: "flex flex-col gap-4",
|
|
12154
|
-
children: [
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12206
|
+
children: [/* @__PURE__ */ X(W, { children: Object.entries(i).map(([e, t]) => {
|
|
12207
|
+
let n = C[e] || t, r = h[e], i = l[e], a = (n.fields || []).length;
|
|
12208
|
+
return /* @__PURE__ */ Z(H.div, {
|
|
12209
|
+
initial: {
|
|
12210
|
+
opacity: 0,
|
|
12211
|
+
y: 12
|
|
12212
|
+
},
|
|
12213
|
+
animate: {
|
|
12214
|
+
opacity: 1,
|
|
12215
|
+
y: 0
|
|
12216
|
+
},
|
|
12217
|
+
exit: {
|
|
12218
|
+
opacity: 0,
|
|
12219
|
+
y: -12
|
|
12220
|
+
},
|
|
12221
|
+
transition: {
|
|
12222
|
+
duration: .35,
|
|
12223
|
+
ease: "easeOut"
|
|
12224
|
+
},
|
|
12225
|
+
onClick: () => se(e),
|
|
12226
|
+
className: `group flex flex-col p-3 px-4 glass-panel border transition-all rounded-2xl hover:shadow-sm cursor-pointer hover:border-accent/40 ${r ? "border-[var(--panel-border)] bg-background/50" : "border-transparent hover:border-[var(--panel-border)]"} flex-shrink-0 ${n.enabled === !1 ? "opacity-40 grayscale bg-foreground/[0.01] border-[var(--panel-border)]/50" : ""}`,
|
|
12227
|
+
children: [/* @__PURE__ */ Z("div", {
|
|
12228
|
+
className: "flex flex-col sm:flex-row items-start sm:items-center justify-between w-full",
|
|
12229
|
+
children: [/* @__PURE__ */ Z("div", {
|
|
12230
|
+
className: "flex flex-col gap-1 w-full sm:w-auto pr-4",
|
|
12231
|
+
children: [/* @__PURE__ */ Z("div", {
|
|
12232
|
+
className: "flex items-center gap-2",
|
|
12233
|
+
children: [/* @__PURE__ */ X("h3", {
|
|
12234
|
+
className: "text-[14px] font-bold text-foreground break-words ml-1 leading-snug",
|
|
12235
|
+
children: n.title || "Untitled form"
|
|
12236
|
+
}), i && /* @__PURE__ */ X(y, { className: "w-3 h-3 animate-spin text-accent ml-2 shrink-0" })]
|
|
12237
|
+
}), /* @__PURE__ */ Z("div", {
|
|
12238
|
+
className: "flex items-center gap-2 mt-1.5 ml-1",
|
|
12239
|
+
children: [/* @__PURE__ */ X("span", {
|
|
12240
|
+
className: `text-[10px] font-bold uppercase tracking-widest border px-2 py-0.5 rounded-md shrink-0 ${n.formType === "private" ? "text-blue-500 bg-blue-500/10 border-blue-500/20" : "text-green-500 bg-green-500/10 border-green-500/20"}`,
|
|
12241
|
+
children: Ao[n.formType || "public"]
|
|
12242
|
+
}), /* @__PURE__ */ X("span", {
|
|
12243
|
+
className: "text-[11px] font-medium text-foreground/45",
|
|
12244
|
+
children: a === 1 ? "1 question" : `${a} questions`
|
|
12245
|
+
})]
|
|
12246
|
+
})]
|
|
12247
|
+
}), /* @__PURE__ */ Z("div", {
|
|
12248
|
+
className: "flex items-center gap-1.5 w-full sm:w-auto justify-end mt-2 sm:mt-0 shrink-0",
|
|
12249
|
+
children: [/* @__PURE__ */ X(Hn, {
|
|
12250
|
+
icon: pe,
|
|
12251
|
+
onClick: (e) => {
|
|
12252
|
+
e.stopPropagation(), f(n);
|
|
12253
|
+
},
|
|
12254
|
+
children: "Preview"
|
|
12255
|
+
}), /* @__PURE__ */ X("div", {
|
|
12256
|
+
className: `p-1.5 transition-transform duration-300 text-foreground/40 ${r ? "rotate-180" : ""}`,
|
|
12257
|
+
children: /* @__PURE__ */ X("svg", {
|
|
12258
|
+
width: "12",
|
|
12259
|
+
height: "12",
|
|
12260
|
+
viewBox: "0 0 24 24",
|
|
12261
|
+
fill: "none",
|
|
12262
|
+
stroke: "currentColor",
|
|
12263
|
+
strokeWidth: "2.5",
|
|
12264
|
+
strokeLinecap: "round",
|
|
12265
|
+
strokeLinejoin: "round",
|
|
12266
|
+
children: /* @__PURE__ */ X("polyline", { points: "6 9 12 15 18 9" })
|
|
12267
|
+
})
|
|
12268
|
+
})]
|
|
12269
|
+
})]
|
|
12270
|
+
}), /* @__PURE__ */ X(W, { children: r && /* @__PURE__ */ X(H.div, {
|
|
12158
12271
|
initial: {
|
|
12159
|
-
|
|
12160
|
-
|
|
12272
|
+
height: 0,
|
|
12273
|
+
opacity: 0
|
|
12161
12274
|
},
|
|
12162
12275
|
animate: {
|
|
12163
|
-
|
|
12164
|
-
|
|
12276
|
+
height: "auto",
|
|
12277
|
+
opacity: 1
|
|
12165
12278
|
},
|
|
12166
12279
|
exit: {
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
},
|
|
12170
|
-
transition: {
|
|
12171
|
-
duration: .35,
|
|
12172
|
-
ease: "easeOut"
|
|
12280
|
+
height: 0,
|
|
12281
|
+
opacity: 0
|
|
12173
12282
|
},
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
children:
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
}), /* @__PURE__ */ Z("div", {
|
|
12187
|
-
className: "flex items-center gap-2 mt-1.5 ml-1",
|
|
12188
|
-
children: [/* @__PURE__ */ X("span", {
|
|
12189
|
-
className: `text-[10px] font-bold uppercase tracking-widest border px-2 py-0.5 rounded-md shrink-0 ${n.formType === "private" ? "text-blue-500 bg-blue-500/10 border-blue-500/20" : "text-green-500 bg-green-500/10 border-green-500/20"}`,
|
|
12190
|
-
children: ko[n.formType || "public"]
|
|
12191
|
-
}), /* @__PURE__ */ X("span", {
|
|
12192
|
-
className: "text-[11px] font-medium text-foreground/45",
|
|
12193
|
-
children: a === 1 ? "1 question" : `${a} questions`
|
|
12194
|
-
})]
|
|
12195
|
-
})]
|
|
12196
|
-
}), /* @__PURE__ */ Z("div", {
|
|
12197
|
-
className: "flex items-center gap-1.5 w-full sm:w-auto justify-end mt-2 sm:mt-0 shrink-0",
|
|
12198
|
-
children: [/* @__PURE__ */ X(Hn, {
|
|
12199
|
-
icon: pe,
|
|
12200
|
-
onClick: (e) => {
|
|
12201
|
-
e.stopPropagation(), f(n);
|
|
12202
|
-
},
|
|
12203
|
-
children: "Preview"
|
|
12204
|
-
}), /* @__PURE__ */ X("div", {
|
|
12205
|
-
className: `p-1.5 transition-transform duration-300 text-foreground/40 ${r ? "rotate-180" : ""}`,
|
|
12206
|
-
children: /* @__PURE__ */ X("svg", {
|
|
12207
|
-
width: "12",
|
|
12208
|
-
height: "12",
|
|
12209
|
-
viewBox: "0 0 24 24",
|
|
12210
|
-
fill: "none",
|
|
12211
|
-
stroke: "currentColor",
|
|
12212
|
-
strokeWidth: "2.5",
|
|
12213
|
-
strokeLinecap: "round",
|
|
12214
|
-
strokeLinejoin: "round",
|
|
12215
|
-
children: /* @__PURE__ */ X("polyline", { points: "6 9 12 15 18 9" })
|
|
12216
|
-
})
|
|
12217
|
-
})]
|
|
12218
|
-
})]
|
|
12219
|
-
}), /* @__PURE__ */ X(W, { children: r && /* @__PURE__ */ X(H.div, {
|
|
12220
|
-
initial: {
|
|
12221
|
-
height: 0,
|
|
12222
|
-
opacity: 0
|
|
12223
|
-
},
|
|
12224
|
-
animate: {
|
|
12225
|
-
height: "auto",
|
|
12226
|
-
opacity: 1
|
|
12227
|
-
},
|
|
12228
|
-
exit: {
|
|
12229
|
-
height: 0,
|
|
12230
|
-
opacity: 0
|
|
12231
|
-
},
|
|
12232
|
-
className: "overflow-hidden mt-4 pt-4 border-t border-[var(--panel-border)]/50",
|
|
12233
|
-
onClick: (e) => e.stopPropagation(),
|
|
12234
|
-
children: le(e, t)
|
|
12235
|
-
}) })]
|
|
12236
|
-
}, t._uiKey || e);
|
|
12237
|
-
}) }),
|
|
12238
|
-
o && /* @__PURE__ */ Z("div", {
|
|
12239
|
-
className: "flex items-center justify-center gap-3 p-6 glass-panel rounded-2xl border border-[var(--panel-border)]/40 bg-foreground/[0.01] mt-2 animate-pulse",
|
|
12240
|
-
children: [/* @__PURE__ */ X(y, { className: "w-4 h-4 animate-spin text-accent" }), /* @__PURE__ */ X("span", {
|
|
12241
|
-
className: "text-[12px] font-bold text-foreground/50 tracking-wider uppercase select-none",
|
|
12242
|
-
children: "Loading your forms"
|
|
12243
|
-
})]
|
|
12244
|
-
}),
|
|
12245
|
-
/* @__PURE__ */ X("div", { className: "w-full border-t border-[var(--panel-border)]/70 mt-6 mb-2" }),
|
|
12246
|
-
/* @__PURE__ */ X(lr, {
|
|
12247
|
-
title: "Everything else is your assistant's job",
|
|
12248
|
-
prompt: Po.more
|
|
12249
|
-
})
|
|
12250
|
-
]
|
|
12283
|
+
className: "overflow-hidden mt-4 pt-4 border-t border-[var(--panel-border)]/50",
|
|
12284
|
+
onClick: (e) => e.stopPropagation(),
|
|
12285
|
+
children: le(e, t)
|
|
12286
|
+
}) })]
|
|
12287
|
+
}, t._uiKey || e);
|
|
12288
|
+
}) }), o && /* @__PURE__ */ Z("div", {
|
|
12289
|
+
className: "flex items-center justify-center gap-3 p-6 glass-panel rounded-2xl border border-[var(--panel-border)]/40 bg-foreground/[0.01] mt-2 animate-pulse",
|
|
12290
|
+
children: [/* @__PURE__ */ X(y, { className: "w-4 h-4 animate-spin text-accent" }), /* @__PURE__ */ X("span", {
|
|
12291
|
+
className: "text-[12px] font-bold text-foreground/50 tracking-wider uppercase select-none",
|
|
12292
|
+
children: "Loading your forms"
|
|
12293
|
+
})]
|
|
12294
|
+
})]
|
|
12251
12295
|
})
|
|
12252
12296
|
})]
|
|
12253
12297
|
})]
|
|
@@ -12272,7 +12316,7 @@ function Io() {
|
|
|
12272
12316
|
}
|
|
12273
12317
|
//#endregion
|
|
12274
12318
|
//#region src/templates/PublicPageTemplate.tsx
|
|
12275
|
-
function
|
|
12319
|
+
function Ro({ config: e, customContent: t }) {
|
|
12276
12320
|
let { config: r } = n();
|
|
12277
12321
|
return G(() => {
|
|
12278
12322
|
document.title = e.pageName || e.pageTitle || r.appName || "Your AI Firebase";
|
|
@@ -12348,7 +12392,7 @@ function Lo({ config: e, customContent: t }) {
|
|
|
12348
12392
|
}
|
|
12349
12393
|
//#endregion
|
|
12350
12394
|
//#region src/templates/UserPageTemplate.tsx
|
|
12351
|
-
function
|
|
12395
|
+
function zo({ config: e, customContent: r }) {
|
|
12352
12396
|
let { user: i } = dn(), { config: a } = n(), [o, s] = K([]), [c, l] = K(!0), [d, f] = K(!1), [p, m] = K(""), [h, b] = K(""), [x, S] = K(!1), [C, w] = K(null), T = it(null), [E, ee] = K(!1), [O, k] = K(null), [A, ne] = K(null), j = (e) => {
|
|
12353
12397
|
if (ne(e), e) {
|
|
12354
12398
|
let t = e.fileType || "";
|
|
@@ -13302,7 +13346,7 @@ function Ro({ config: e, customContent: r }) {
|
|
|
13302
13346
|
}
|
|
13303
13347
|
//#endregion
|
|
13304
13348
|
//#region src/templates/AdminPageTemplate.tsx
|
|
13305
|
-
function
|
|
13349
|
+
function Bo({ config: e, customContent: r }) {
|
|
13306
13350
|
let { user: i } = dn(), { config: a } = n(), [o, s] = K([]), [c, l] = K(!0), [d, f] = K(!1), [p, m] = K(""), [h, b] = K(""), [x, S] = K(!1), [C, w] = K(null), T = it(null), [E, ee] = K(!1), [O, k] = K(null), [A, ne] = K(null), j = (e) => {
|
|
13307
13351
|
if (ne(e), e) {
|
|
13308
13352
|
let t = e.fileType || "";
|
|
@@ -14306,7 +14350,7 @@ function zo({ config: e, customContent: r }) {
|
|
|
14306
14350
|
}
|
|
14307
14351
|
//#endregion
|
|
14308
14352
|
//#region src/templates/InternalPageTemplate.tsx
|
|
14309
|
-
function
|
|
14353
|
+
function Vo({ config: e, customContent: r }) {
|
|
14310
14354
|
let { user: i } = dn(), { config: a } = n(), [o, s] = K([]), [c, l] = K(!0), [d, f] = K(!1), [p, m] = K(""), [h, b] = K(""), [x, S] = K(!1), [C, w] = K(null), T = it(null), [E, ee] = K(!1), [O, k] = K(null), [A, ne] = K(null), j = (e) => {
|
|
14311
14355
|
if (ne(e), e) {
|
|
14312
14356
|
let t = e.fileType || "";
|
|
@@ -15276,13 +15320,13 @@ function Bo({ config: e, customContent: r }) {
|
|
|
15276
15320
|
}
|
|
15277
15321
|
//#endregion
|
|
15278
15322
|
//#region src/configs/pages/templates.ts
|
|
15279
|
-
var
|
|
15323
|
+
var Ho = [
|
|
15280
15324
|
"board",
|
|
15281
15325
|
"table",
|
|
15282
15326
|
"calendar",
|
|
15283
15327
|
"drive"
|
|
15284
|
-
],
|
|
15285
|
-
function
|
|
15328
|
+
], Uo = ["inline_form", "popup_form"], Wo = (e) => typeof e != "string" || e === "" || e === "none" || e === "custom" || ![...Ho, ...Uo].includes(e), Go = (e, t) => Wo(e) || t && !Ho.includes(e), Ko = "src/your-ai-workflow-firebase-os";
|
|
15329
|
+
function qo() {
|
|
15286
15330
|
let e = /* @__PURE__ */ new Set();
|
|
15287
15331
|
for (let [t, n] of pn()) {
|
|
15288
15332
|
let r = n?.config?.route, i = typeof r == "string" && r.trim() ? r.startsWith("/") ? r : "/" + r : "/" + t.toLowerCase();
|
|
@@ -15290,12 +15334,12 @@ function Go() {
|
|
|
15290
15334
|
}
|
|
15291
15335
|
return [...e].sort();
|
|
15292
15336
|
}
|
|
15293
|
-
function
|
|
15337
|
+
function Jo(e) {
|
|
15294
15338
|
return [
|
|
15295
15339
|
`The page at ${e} shows "Page Not Found". The app only finds a page when its config file is picked up, so check these in order and fix whatever is wrong, without asking me questions.`,
|
|
15296
|
-
`1. The page's config file sits directly in ${
|
|
15340
|
+
`1. The page's config file sits directly in ${Ko}/configs/ and its name ends with Tab.config.ts (a page for signed-in users, my team or admins) or Page.config.ts (a page for anyone). Not in a subfolder, not any other name.`,
|
|
15297
15341
|
`2. It exports one const whose name ends with Config, and its route is the single string '${e}'. A page has exactly one web address — remove any other addresses you registered for it.`,
|
|
15298
|
-
`3. ${
|
|
15342
|
+
`3. ${Ko}/configs/pagesRegistry.ts exists and src/main.tsx contains the line import './your-ai-workflow-firebase-os/configs/pagesRegistry'. If either is missing, run npm install so the package's postinstall script restores them.`,
|
|
15299
15343
|
"4. Put src/App.tsx back to the version the package wrote: it only mounts <FirebaseOS />. Never add routes, a router or the customRoutes prop.",
|
|
15300
15344
|
"5. If the page is for my team, its config says pageType: 'internal'. Any form on it is a *Form.config.ts file shown through <ContactPopup formId=\"...\" />, never a form you wrote by hand.",
|
|
15301
15345
|
"Then rebuild and tell me, in one sentence, which of these was wrong."
|
|
@@ -15303,7 +15347,7 @@ function Ko(e) {
|
|
|
15303
15347
|
}
|
|
15304
15348
|
//#endregion
|
|
15305
15349
|
//#region src/components/AccessNotice.tsx
|
|
15306
|
-
function
|
|
15350
|
+
function Yo({ tone: e, title: t, message: n, action: r }) {
|
|
15307
15351
|
let i = e === "red" ? "border-red-500/20" : "border-amber-500/25", a = e === "red" ? "bg-red-500/5" : "bg-amber-500/5", o = e === "red" ? "text-red-500" : "text-amber-500";
|
|
15308
15352
|
return /* @__PURE__ */ X("div", {
|
|
15309
15353
|
className: "flex-1 flex flex-col items-center justify-center relative z-10 min-h-[60vh] animate-in fade-in duration-500",
|
|
@@ -15327,25 +15371,25 @@ function qo({ tone: e, title: t, message: n, action: r }) {
|
|
|
15327
15371
|
})
|
|
15328
15372
|
});
|
|
15329
15373
|
}
|
|
15330
|
-
var
|
|
15374
|
+
var Xo = "w-full block py-2.5 bg-foreground/5 hover:bg-foreground/10 text-foreground text-[14px] font-bold rounded-xl transition-colors", Zo = () => /* @__PURE__ */ X(Yo, {
|
|
15331
15375
|
tone: "amber",
|
|
15332
15376
|
title: "Team page",
|
|
15333
15377
|
message: "This page is for your team. Ask an admin of this app to add you to the team.",
|
|
15334
15378
|
action: /* @__PURE__ */ X(Q, {
|
|
15335
15379
|
to: "/dashboard",
|
|
15336
|
-
className:
|
|
15380
|
+
className: Xo,
|
|
15337
15381
|
children: "Back to dashboard"
|
|
15338
15382
|
})
|
|
15339
|
-
}),
|
|
15383
|
+
}), Qo = () => /* @__PURE__ */ X(Yo, {
|
|
15340
15384
|
tone: "amber",
|
|
15341
15385
|
title: "Admins only",
|
|
15342
15386
|
message: "This page is part of the admin area. Ask an admin of this app if you need to get in.",
|
|
15343
15387
|
action: /* @__PURE__ */ X(Q, {
|
|
15344
15388
|
to: "/dashboard",
|
|
15345
|
-
className:
|
|
15389
|
+
className: Xo,
|
|
15346
15390
|
children: "Back to dashboard"
|
|
15347
15391
|
})
|
|
15348
|
-
}),
|
|
15392
|
+
}), $o = (e, t) => {
|
|
15349
15393
|
let n = t ? an(e.pageType) : "public";
|
|
15350
15394
|
return {
|
|
15351
15395
|
...e,
|
|
@@ -15356,7 +15400,7 @@ var Jo = "w-full block py-2.5 bg-foreground/5 hover:bg-foreground/10 text-foregr
|
|
|
15356
15400
|
isShared: n === "internal"
|
|
15357
15401
|
};
|
|
15358
15402
|
};
|
|
15359
|
-
function
|
|
15403
|
+
function es() {
|
|
15360
15404
|
let e = zt(), { user: t, userRole: n, loading: r, roleResolved: a } = dn(), o = S(), s = (e) => {
|
|
15361
15405
|
if (typeof window < "u") {
|
|
15362
15406
|
let t = localStorage.getItem(`fbos_route_${e}`);
|
|
@@ -15366,34 +15410,36 @@ function Qo() {
|
|
|
15366
15410
|
let t = localStorage.getItem(`fbos_form_${e.form}`);
|
|
15367
15411
|
t && (e.formConfigOverride = JSON.parse(t));
|
|
15368
15412
|
}
|
|
15369
|
-
return
|
|
15413
|
+
return $o(e, e.isPrivate === !0);
|
|
15370
15414
|
} catch {}
|
|
15371
15415
|
}
|
|
15372
|
-
|
|
15416
|
+
let t = (typeof window < "u" ? window.__FBOS_PAGES__ : null) || {}, n = e.replace(/^\//, "");
|
|
15417
|
+
for (let [r, i] of Object.entries(t)) {
|
|
15418
|
+
let t = i.config, a = i.isPublic, o = t.route || "/" + r;
|
|
15419
|
+
if (o === e || r === n) return $o({
|
|
15420
|
+
...t,
|
|
15421
|
+
route: o,
|
|
15422
|
+
contentKey: t.pageId || r,
|
|
15423
|
+
pageId: r
|
|
15424
|
+
}, !a);
|
|
15425
|
+
}
|
|
15426
|
+
return e === "/contact" ? {
|
|
15373
15427
|
route: "/contact",
|
|
15374
15428
|
template: "popup_form",
|
|
15375
15429
|
form: "contact",
|
|
15376
15430
|
pageId: "contact",
|
|
15431
|
+
contentKey: "contact",
|
|
15432
|
+
pageName: "Contact",
|
|
15377
15433
|
formConfigOverride: o.defaultConfigs?.contactForm || i
|
|
15378
|
-
}
|
|
15379
|
-
if (e === "/support") return {
|
|
15434
|
+
} : e === "/support" ? {
|
|
15380
15435
|
route: "/support",
|
|
15381
15436
|
template: "popup_form",
|
|
15382
15437
|
form: "support",
|
|
15383
15438
|
pageId: "support",
|
|
15439
|
+
contentKey: "support",
|
|
15440
|
+
pageName: "Support",
|
|
15384
15441
|
formConfigOverride: o.defaultConfigs?.supportForm || k
|
|
15385
|
-
};
|
|
15386
|
-
let t = (typeof window < "u" ? window.__FBOS_PAGES__ : null) || {}, n = e.replace(/^\//, "");
|
|
15387
|
-
for (let [r, i] of Object.entries(t)) {
|
|
15388
|
-
let t = i.config, a = i.isPublic, o = t.route || "/" + r;
|
|
15389
|
-
if (o === e || r === n) return Zo({
|
|
15390
|
-
...t,
|
|
15391
|
-
route: o,
|
|
15392
|
-
contentKey: t.pageId || r,
|
|
15393
|
-
pageId: r
|
|
15394
|
-
}, !a);
|
|
15395
|
-
}
|
|
15396
|
-
return null;
|
|
15442
|
+
} : null;
|
|
15397
15443
|
}, [c, l] = K(() => s(e.pathname)), [u, d] = K(() => !s(e.pathname));
|
|
15398
15444
|
G(() => {
|
|
15399
15445
|
let t = !0, n = s(e.pathname);
|
|
@@ -15414,24 +15460,14 @@ function Qo() {
|
|
|
15414
15460
|
lt(pt(q(D, "sys_tabs"), _t("route", "==", e.pathname), ut(1))),
|
|
15415
15461
|
lt(pt(q(D, "sys_tabs"), _t("route", "==", n), ut(1)))
|
|
15416
15462
|
]), p = null, m = null, h = (e, t) => {
|
|
15417
|
-
p || e.status === "fulfilled" && (e.value.exists && e.value.exists() ? (p =
|
|
15463
|
+
p || e.status === "fulfilled" && (e.value.exists && e.value.exists() ? (p = $o(e.value.data(), t), m = e.value.id) : e.value.docs && e.value.docs.length > 0 && (p = $o(e.value.docs[0].data(), t), m = e.value.docs[0].id));
|
|
15418
15464
|
};
|
|
15419
|
-
if (h(r, !1), h(s, !1), h(c, !1), h(a, !0), h(u, !0), h(f, !0), !p)
|
|
15420
|
-
route: "/contact",
|
|
15421
|
-
template: "popup_form",
|
|
15422
|
-
form: "contact"
|
|
15423
|
-
}, m = "contact";
|
|
15424
|
-
else if (e.pathname === "/support") p = {
|
|
15425
|
-
route: "/support",
|
|
15426
|
-
template: "popup_form",
|
|
15427
|
-
form: "support"
|
|
15428
|
-
}, m = "support";
|
|
15429
|
-
else {
|
|
15465
|
+
if (h(r, !1), h(s, !1), h(c, !1), h(a, !0), h(u, !0), h(f, !0), !p) {
|
|
15430
15466
|
let t = (typeof window < "u" ? window.__FBOS_PAGES__ : null) || {};
|
|
15431
15467
|
for (let [r, i] of Object.entries(t)) {
|
|
15432
15468
|
let t = i.config, a = i.isPublic, o = t.route || "/" + r;
|
|
15433
15469
|
if (o === e.pathname || r === n) {
|
|
15434
|
-
p =
|
|
15470
|
+
p = $o({
|
|
15435
15471
|
...t,
|
|
15436
15472
|
route: o,
|
|
15437
15473
|
contentKey: t.pageId || r,
|
|
@@ -15441,7 +15477,17 @@ function Qo() {
|
|
|
15441
15477
|
}
|
|
15442
15478
|
}
|
|
15443
15479
|
}
|
|
15444
|
-
if (
|
|
15480
|
+
if (p || (e.pathname === "/contact" ? (p = {
|
|
15481
|
+
route: "/contact",
|
|
15482
|
+
template: "popup_form",
|
|
15483
|
+
form: "contact",
|
|
15484
|
+
pageName: "Contact"
|
|
15485
|
+
}, m = "contact") : e.pathname === "/support" && (p = {
|
|
15486
|
+
route: "/support",
|
|
15487
|
+
template: "popup_form",
|
|
15488
|
+
form: "support",
|
|
15489
|
+
pageName: "Support"
|
|
15490
|
+
}, m = "support")), !t) return;
|
|
15445
15491
|
if (p) {
|
|
15446
15492
|
if ((m === "contact" || m === "support") && !p.form && (p.form = m), p.contentKey ||= p.pageId || m, p.pageId = m, localStorage.setItem(`fbos_route_${e.pathname}`, JSON.stringify(p)), p.form) {
|
|
15447
15493
|
let e = (typeof window < "u" ? window.__FBOS_FORMS__ : null) || {}, n = (t) => o.defaultConfigs?.[t] || o.defaultConfigs?.[`${t}Form`] || e[t] || (t === "contact" ? i : t === "support" ? k : null);
|
|
@@ -15558,7 +15604,7 @@ function Qo() {
|
|
|
15558
15604
|
children: "The route you are looking for does not exist or has been removed."
|
|
15559
15605
|
}),
|
|
15560
15606
|
tn(n) && (() => {
|
|
15561
|
-
let t =
|
|
15607
|
+
let t = qo();
|
|
15562
15608
|
return /* @__PURE__ */ Z("div", {
|
|
15563
15609
|
className: "mt-8 w-full max-w-lg text-left flex flex-col gap-4",
|
|
15564
15610
|
children: [/* @__PURE__ */ Z("div", {
|
|
@@ -15572,7 +15618,7 @@ function Qo() {
|
|
|
15572
15618
|
})]
|
|
15573
15619
|
}), /* @__PURE__ */ X(lr, {
|
|
15574
15620
|
title: "If your assistant just made this page, paste this",
|
|
15575
|
-
prompt:
|
|
15621
|
+
prompt: Jo(e.pathname)
|
|
15576
15622
|
})]
|
|
15577
15623
|
});
|
|
15578
15624
|
})()
|
|
@@ -15583,7 +15629,7 @@ function Qo() {
|
|
|
15583
15629
|
to: "/login",
|
|
15584
15630
|
state: { from: e }
|
|
15585
15631
|
});
|
|
15586
|
-
if (!on(n, c.isPrivate ? c.pageType : "public")) return c.isAdmin ? /* @__PURE__ */ X(
|
|
15632
|
+
if (!on(n, c.isPrivate ? c.pageType : "public")) return c.isAdmin ? /* @__PURE__ */ X(Qo, {}) : /* @__PURE__ */ X(Zo, {});
|
|
15587
15633
|
let m = o.customRoutes?.[e.pathname] || (c && c.route ? o.customRoutes?.[c.route] : null), h = c.pageName?.toLowerCase().replace(/\s+/g, "_") || c.pageId || "custom_page", g = `pub_${h}`, _ = o.microcomponents || (typeof window < "u" ? window.__FBOS_MICROCOMPONENTS__ : void 0), v = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, ""), b = [
|
|
15588
15634
|
c.contentKey,
|
|
15589
15635
|
c.tabName,
|
|
@@ -15591,21 +15637,16 @@ function Qo() {
|
|
|
15591
15637
|
c.pageName,
|
|
15592
15638
|
c.pageName?.replace(/\s+/g, "_")
|
|
15593
15639
|
].filter(Boolean).map((e) => v(e)).reduce((e, t) => e || _?.[t], void 0), x = m || b || void 0, C = c.route || c.pageId || "custom";
|
|
15594
|
-
if (
|
|
15595
|
-
"board",
|
|
15596
|
-
"table",
|
|
15597
|
-
"calendar",
|
|
15598
|
-
"drive"
|
|
15599
|
-
].includes(c.template)) return c.isAdmin ? /* @__PURE__ */ X(zo, {
|
|
15640
|
+
if (Go(c.template, !!x)) return c.isAdmin ? /* @__PURE__ */ X(Bo, {
|
|
15600
15641
|
config: c,
|
|
15601
15642
|
customContent: x
|
|
15602
|
-
}, C) : c.isInternal ? /* @__PURE__ */ X(
|
|
15643
|
+
}, C) : c.isInternal ? /* @__PURE__ */ X(Vo, {
|
|
15603
15644
|
config: c,
|
|
15604
15645
|
customContent: x
|
|
15605
|
-
}, C) : c.isPrivate ? /* @__PURE__ */ X(
|
|
15646
|
+
}, C) : c.isPrivate ? /* @__PURE__ */ X(zo, {
|
|
15606
15647
|
config: c,
|
|
15607
15648
|
customContent: x
|
|
15608
|
-
}, C) : /* @__PURE__ */ X(
|
|
15649
|
+
}, C) : /* @__PURE__ */ X(Ro, {
|
|
15609
15650
|
config: c,
|
|
15610
15651
|
customContent: x
|
|
15611
15652
|
}, C);
|
|
@@ -15669,18 +15710,18 @@ function Qo() {
|
|
|
15669
15710
|
}
|
|
15670
15711
|
//#endregion
|
|
15671
15712
|
//#region node_modules/iconoir-react/dist/esm/IconoirContext.mjs
|
|
15672
|
-
var
|
|
15713
|
+
var ts = Qe.createContext({}), ns = Object.defineProperty, rs = Object.getOwnPropertySymbols, is = Object.prototype.hasOwnProperty, as = Object.prototype.propertyIsEnumerable, os = (e, t, n) => t in e ? ns(e, t, {
|
|
15673
15714
|
enumerable: !0,
|
|
15674
15715
|
configurable: !0,
|
|
15675
15716
|
writable: !0,
|
|
15676
15717
|
value: n
|
|
15677
|
-
}) : e[t] = n,
|
|
15678
|
-
for (var n in t ||= {})
|
|
15679
|
-
if (
|
|
15718
|
+
}) : e[t] = n, ss = (e, t) => {
|
|
15719
|
+
for (var n in t ||= {}) is.call(t, n) && os(e, n, t[n]);
|
|
15720
|
+
if (rs) for (var n of rs(t)) as.call(t, n) && os(e, n, t[n]);
|
|
15680
15721
|
return e;
|
|
15681
|
-
},
|
|
15682
|
-
let n =
|
|
15683
|
-
return Ze.createElement("svg",
|
|
15722
|
+
}, cs = tt((e, t) => {
|
|
15723
|
+
let n = ss(ss({}, Ze.useContext(ts)), e);
|
|
15724
|
+
return Ze.createElement("svg", ss({
|
|
15684
15725
|
width: "1.5em",
|
|
15685
15726
|
height: "1.5em",
|
|
15686
15727
|
viewBox: "0 0 24 24",
|
|
@@ -15700,7 +15741,7 @@ var $o = Qe.createContext({}), es = Object.defineProperty, ts = Object.getOwnPro
|
|
|
15700
15741
|
strokeLinecap: "round",
|
|
15701
15742
|
strokeLinejoin: "round"
|
|
15702
15743
|
}));
|
|
15703
|
-
}), ss = "rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n\n // ── Who is asking ─────────────────────────────────────────────────────\n function isAuth() { return request.auth != null; }\n\n // The profile document is the whole access model: it carries `role` and\n // `inactive`. Everything below reads it through these two helpers, so the\n // rules engine fetches it once per request no matter how many checks run.\n function profileExists() {\n return exists(/databases/$(database)/documents/user_profiles/$(request.auth.uid));\n }\n\n function profile() {\n return get(/databases/$(database)/documents/user_profiles/$(request.auth.uid)).data;\n }\n\n // Turning someone off under /users used to be decoration: `inactive` was\n // protected from being written (see grantsUntouched) but never read, so the\n // only thing stopping a deactivated person was the Access Denied screen in\n // the app. Anyone who opened devtools and drove the Firebase SDK straight\n // kept every read and write they had before. Now the flag is checked here,\n // where it cannot be walked around.\n // An account with no profile document yet is NOT deactivated — registration\n // seeds the profile a moment after the account exists, and that gap must\n // not lock a brand new person out of their own sign-up.\n function notDeactivated() {\n return isAuth() &&\n (!profileExists() || profile().get('inactive', false) != true);\n }\n\n // Firebase flips email_verified once the link in the verification mail is\n // clicked; Google sign-ins arrive already verified. Every rule below\n // spends this rather than isAuth(), so a half-registered account cannot\n // walk past the /verify screen by driving the SDK from a browser console —\n // and, since the deactivation check lives here too, neither can a\n // deactivated one.\n function isVerified() {\n return isAuth() &&\n request.auth.token.email_verified == true &&\n notDeactivated();\n }\n\n function role() {\n return profile().role;\n }\n\n function hasProfile() {\n return isVerified() && profileExists();\n }\n\n function isAdmin() { \n return hasProfile() && role() == 'admin';\n }\n\n // Internal members and admins. Registering is NOT enough: an admin has to\n // set the level under /users. Everything a free user must never see —\n // every mem_ collection, which is what internal pages read and write —\n // hangs off this one function.\n function isInternal() {\n return hasProfile() && role() in ['internal', 'admin'];\n }\n \n function isDocOwner() {\n return isVerified() && \n (resource == null || resource.data.uid == request.auth.uid) && \n (request.resource == null || request.resource.data.uid == request.auth.uid);\n }\n\n // ── The first admin ───────────────────────────────────────────────────\n // The Setup page fills this list in from VITE_ADMIN_EMAILS before you copy\n // the rules, so it already holds your own address by the time you paste.\n // It is the one and only way a person can hand themselves a role, it can\n // only ever grant 'admin', and it only works from a verified address —\n // otherwise anyone could register under your email and claim the app\n // without ever opening your inbox.\n function adminEmails() {\n return ['your@email.com'];\n }\n\n function isSeedingOwnAdmin() {\n return isVerified() &&\n request.auth.token.email.lower() in adminEmails() &&\n request.resource.data.role == 'admin' &&\n request.resource.data.diff(resource.data).affectedKeys().hasOnly(['role']);\n }\n\n // `role` and `inactive` are the entire access model, so they are read-only\n // to the person they describe: admins set them from /users, and the seed\n // above is the single exception. Name, avatar and the rest stay the\n // owner's to write.\n function grantsUntouched() {\n return resource == null\n ? !request.resource.data.keys().hasAny(['role', 'inactive'])\n : !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'inactive']);\n }\n\n // user_ collections an ordinary page reads and writes. Profiles and\n // submissions are carved out below — they each have their own rules.\n function isUserData(collectionName) {\n return collectionName.matches('^user_.*') &&\n collectionName != 'user_profiles' &&\n collectionName != 'user_submissions';\n }\n\n function ownsUserDoc(docId) {\n return isVerified() && (isDocOwner() || request.auth.uid == docId);\n }\n\n // Admins are trusted with everything in this database — that is what the\n // role is. There used to be a per-document owner test bolted onto update\n // and delete for admin_ collections, but the collection rule below grants\n // admins read+write on admin_ outright and rules OR together, so it never\n // denied anything. It has been removed rather than left to read as a\n // protection that is not there. If you ever want one admin's records to be\n // off limits to another, it has to be built in both places at once: here\n // AND in the admin_ collection rule below.\n match /{document=**} {\n allow read, write: if isAdmin();\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────\n // Unverified people reach this one document on purpose: registration seeds\n // it before the email goes out, and /verify needs to read it back. What\n // they cannot do is touch role or inactive.\n // Reading stays open to the owner even when deactivated — the app has to\n // be able to see the flag to show the Access Denied screen — but writing\n // does not: a deactivated account changes nothing about itself.\n match /user_profiles/{uid} {\n allow get: if isAuth() && request.auth.uid == uid;\n allow create: if isAuth() && request.auth.uid == uid && grantsUntouched();\n allow update: if isAuth() && request.auth.uid == uid && notDeactivated() &&\n (grantsUntouched() || isSeedingOwnAdmin());\n }\n\n match /{collectionName}/{docId} {\n \n allow read, write: if collectionName.matches('^admin_.*') && isAdmin();\n\n // sys_ holds theme, page and form config. It is read openly because the\n // login screen has to paint itself before anyone has signed in.\n allow read: if collectionName.matches('^sys_.*');\n allow write: if collectionName.matches('^sys_.*') && isAdmin();\n\n allow get: if isUserData(collectionName) && ownsUserDoc(docId);\n allow list: if isUserData(collectionName) && isDocOwner();\n allow write: if isUserData(collectionName) && ownsUserDoc(docId);\n \n // Public forms post here from pages a guest can open, so creating stays\n // open to everyone. Nothing else is: editing and deleting are the\n // admin's, via the wildcard above. Without that split anyone could\n // quietly overwrite or wipe every submission you have received.\n allow read: if collectionName == 'user_submissions' && (isDocOwner() || isAdmin());\n allow create: if collectionName == 'user_submissions';\n\n allow read, create: if collectionName.matches('^mem_.*') && isInternal();\n allow update, delete: if collectionName.matches('^mem_.*') && isInternal() && (isDocOwner() || request.auth.uid == docId);\n\n // Public pages are readable by guests — that is what makes them public —\n // but writing is not anonymous. Left open, anyone on the internet could\n // empty every public collection you have. Guest input goes through\n // user_submissions instead.\n allow read: if collectionName.matches('^pub_.*');\n allow write: if collectionName.matches('^pub_.*') && isVerified();\n\n match /{allPaths=**} {\n allow read: if (collectionName.matches('^admin_.*') && isAdmin())\n || collectionName.matches('^sys_.*')\n || collectionName.matches('^pub_.*')\n || (collectionName.matches('^mem_.*') && isInternal())\n || (collectionName.matches('^user_.*') && isDocOwner());\n \n allow create: if (collectionName.matches('^admin_.*') && isAdmin())\n || (collectionName.matches('^sys_.*') && isAdmin())\n || (isUserData(collectionName) && isDocOwner())\n || collectionName == 'user_submissions'\n || (collectionName.matches('^pub_.*') && isVerified())\n || (collectionName.matches('^mem_.*') && isInternal());\n\n allow update, delete: if collectionName.matches('^pub_.*') && isVerified();\n }\n }\n }\n}\n", cs = "rules_version = '2';\nservice firebase.storage {\n match /b/{bucket}/o {\n\n // --- Helper Functions ---\n function isAuth() {\n return request.auth != null;\n }\n\n function hasProfile(uid) {\n return firestore.exists(/databases/(default)/documents/user_profiles/$(uid));\n }\n\n function profileRole(uid) {\n return firestore.get(/databases/(default)/documents/user_profiles/$(uid)).data.role;\n }\n\n // Matches the Firestore rules: an admin who switches someone off under\n // /users expects the files to close too. Until this was checked, only the\n // app's own Access Denied screen stood in the way, and a deactivated person\n // driving the SDK from a console kept every file they had. An account with\n // no profile document yet counts as active — registration seeds the profile\n // a moment after the account exists.\n function notDeactivated() {\n return isAuth() &&\n (!hasProfile(request.auth.uid) ||\n firestore.get(/databases/(default)/documents/user_profiles/$(request.auth.uid))\n .data.get('inactive', false) != true);\n }\n\n // Matches the Firestore rules: a person who never opened the verification\n // email has a signed-in token but no verified one, and nothing here opens\n // for them. Google sign-ins arrive verified.\n function isVerified() {\n return isAuth() && request.auth.token.email_verified == true && notDeactivated();\n }\n\n function isMemberDocAdmin(uid) {\n return hasProfile(uid) && profileRole(uid) == 'admin';\n }\n\n function isAdmin() {\n return isVerified() && isMemberDocAdmin(request.auth.uid);\n }\n\n // Files in mem_ folders belong to internal pages and to Drive's internal\n // folder. Signing up does not open them — an admin marks the account\n // internal under /users first.\n function isInternal() {\n return isVerified() && hasProfile(request.auth.uid) &&\n profileRole(request.auth.uid) in ['internal', 'admin'];\n }\n\n function hasOwnerMeta(reqRes) {\n return reqRes != null && 'metadata' in reqRes && reqRes.metadata != null && 'ownerId' in reqRes.metadata;\n }\n\n // --- Upload limits ---\n // Nothing capped uploads before this: any verified account could push a\n // file of any size and any kind into user_ or mem_, and the bill is the\n // owner's. 10 MB covers the documents, photos and short clips these pages\n // are for. Admins are exempt on purpose — the Assets screen is where the\n // owner puts their own logo, fonts and anything else the app needs.\n function underSizeCap(bytes) {\n return request.resource != null && request.resource.size < bytes;\n }\n\n // An allowlist rather than a blocklist, so a new dangerous type does not\n // arrive open. SVG is excluded from images because it can carry script and\n // Firebase serves it inline from the storage domain; the admin Assets path\n // can still take one.\n function allowedFileType() {\n return request.resource != null && request.resource.contentType != null && (\n (request.resource.contentType.matches('image/.*') &&\n !request.resource.contentType.matches('image/svg.*')) ||\n request.resource.contentType.matches('audio/.*') ||\n request.resource.contentType.matches('video/.*') ||\n request.resource.contentType in [\n 'text/plain', 'text/csv', 'text/markdown', 'text/calendar',\n 'application/pdf', 'application/json', 'application/rtf',\n 'application/zip', 'application/x-zip-compressed',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-powerpoint',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'application/vnd.oasis.opendocument.text',\n 'application/vnd.oasis.opendocument.spreadsheet',\n 'application/vnd.oasis.opendocument.presentation'\n ]\n );\n }\n\n function isUploadableImage() {\n return request.resource != null &&\n request.resource.contentType != null &&\n request.resource.contentType.matches('image/.*') &&\n !request.resource.contentType.matches('image/svg.*');\n }\n\n // --- Special Match: User Avatars ---\n // A picture, and a small one: the profile screen already refuses anything\n // over 2 MB, this is the same rule where it cannot be skipped.\n match /user_avatars/{uid}/{fileName} {\n allow read: if isVerified();\n allow create, update: if isVerified()\n && request.auth.uid == uid\n && isUploadableImage()\n && underSizeCap(5 * 1024 * 1024)\n && (hasOwnerMeta(request.resource) ? request.resource.metadata.ownerId == request.auth.uid : true);\n allow delete: if isVerified() && request.auth.uid == uid;\n }\n\n // --- Generic Folder Match ---\n match /{folder}/{allPaths=**} {\n\n // Public folder access\n allow read: if folder.matches('^pub_.*');\n\n // Internal folder access — internal members and admins only.\n // The folder test comes first on purpose: isInternal() reaches across to\n // Firestore, so every read of a pub_ file would otherwise pay for a\n // profile lookup it does not need. (Signed-in reads pay one either way\n // now — isVerified() has to look the deactivation flag up.)\n allow read: if folder.matches('^mem_.*') && isInternal();\n allow create: if folder.matches('^mem_.*')\n && isInternal()\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && hasOwnerMeta(request.resource)\n && request.resource.metadata.ownerId == request.auth.uid;\n allow update: if folder.matches('^mem_.*')\n && isInternal()\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n allow delete: if folder.matches('^mem_.*')\n && isInternal()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n\n // User-specific folders\n allow create: if isVerified()\n && folder.matches('^user_.*')\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && hasOwnerMeta(request.resource)\n && request.resource.metadata.ownerId == request.auth.uid;\n allow update: if isVerified()\n && folder.matches('^user_.*')\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n allow read, delete: if isVerified()\n && folder.matches('^user_.*')\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n\n // Admin access overrides — the Assets screen holds the logo, the favicon\n // and anything else the owner needs the app to serve, so it is not capped\n // or filtered.\n allow read, create: if isAdmin();\n allow update, delete: if isAdmin();\n }\n }\n}\n", ls = [
|
|
15744
|
+
}), ls = "rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n\n // ── Who is asking ─────────────────────────────────────────────────────\n function isAuth() { return request.auth != null; }\n\n // The profile document is the whole access model: it carries `role` and\n // `inactive`. Everything below reads it through these two helpers, so the\n // rules engine fetches it once per request no matter how many checks run.\n function profileExists() {\n return exists(/databases/$(database)/documents/user_profiles/$(request.auth.uid));\n }\n\n function profile() {\n return get(/databases/$(database)/documents/user_profiles/$(request.auth.uid)).data;\n }\n\n // Turning someone off under /users used to be decoration: `inactive` was\n // protected from being written (see grantsUntouched) but never read, so the\n // only thing stopping a deactivated person was the Access Denied screen in\n // the app. Anyone who opened devtools and drove the Firebase SDK straight\n // kept every read and write they had before. Now the flag is checked here,\n // where it cannot be walked around.\n // An account with no profile document yet is NOT deactivated — registration\n // seeds the profile a moment after the account exists, and that gap must\n // not lock a brand new person out of their own sign-up.\n function notDeactivated() {\n return isAuth() &&\n (!profileExists() || profile().get('inactive', false) != true);\n }\n\n // Firebase flips email_verified once the link in the verification mail is\n // clicked; Google sign-ins arrive already verified. Every rule below\n // spends this rather than isAuth(), so a half-registered account cannot\n // walk past the /verify screen by driving the SDK from a browser console —\n // and, since the deactivation check lives here too, neither can a\n // deactivated one.\n function isVerified() {\n return isAuth() &&\n request.auth.token.email_verified == true &&\n notDeactivated();\n }\n\n function role() {\n return profile().role;\n }\n\n function hasProfile() {\n return isVerified() && profileExists();\n }\n\n function isAdmin() { \n return hasProfile() && role() == 'admin';\n }\n\n // Internal members and admins. Registering is NOT enough: an admin has to\n // set the level under /users. Everything a free user must never see —\n // every mem_ collection, which is what internal pages read and write —\n // hangs off this one function.\n function isInternal() {\n return hasProfile() && role() in ['internal', 'admin'];\n }\n \n function isDocOwner() {\n return isVerified() && \n (resource == null || resource.data.uid == request.auth.uid) && \n (request.resource == null || request.resource.data.uid == request.auth.uid);\n }\n\n // ── The first admin ───────────────────────────────────────────────────\n // The Setup page fills this list in from VITE_ADMIN_EMAILS before you copy\n // the rules, so it already holds your own address by the time you paste.\n // It is the one and only way a person can hand themselves a role, it can\n // only ever grant 'admin', and it only works from a verified address —\n // otherwise anyone could register under your email and claim the app\n // without ever opening your inbox.\n function adminEmails() {\n return ['your@email.com'];\n }\n\n function isSeedingOwnAdmin() {\n return isVerified() &&\n request.auth.token.email.lower() in adminEmails() &&\n request.resource.data.role == 'admin' &&\n request.resource.data.diff(resource.data).affectedKeys().hasOnly(['role']);\n }\n\n // `role` and `inactive` are the entire access model, so they are read-only\n // to the person they describe: admins set them from /users, and the seed\n // above is the single exception. Name, avatar and the rest stay the\n // owner's to write.\n function grantsUntouched() {\n return resource == null\n ? !request.resource.data.keys().hasAny(['role', 'inactive'])\n : !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'inactive']);\n }\n\n // user_ collections an ordinary page reads and writes. Profiles and\n // submissions are carved out below — they each have their own rules.\n function isUserData(collectionName) {\n return collectionName.matches('^user_.*') &&\n collectionName != 'user_profiles' &&\n collectionName != 'user_submissions';\n }\n\n function ownsUserDoc(docId) {\n return isVerified() && (isDocOwner() || request.auth.uid == docId);\n }\n\n // Admins are trusted with everything in this database — that is what the\n // role is. There used to be a per-document owner test bolted onto update\n // and delete for admin_ collections, but the collection rule below grants\n // admins read+write on admin_ outright and rules OR together, so it never\n // denied anything. It has been removed rather than left to read as a\n // protection that is not there. If you ever want one admin's records to be\n // off limits to another, it has to be built in both places at once: here\n // AND in the admin_ collection rule below.\n match /{document=**} {\n allow read, write: if isAdmin();\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────\n // Unverified people reach this one document on purpose: registration seeds\n // it before the email goes out, and /verify needs to read it back. What\n // they cannot do is touch role or inactive.\n // Reading stays open to the owner even when deactivated — the app has to\n // be able to see the flag to show the Access Denied screen — but writing\n // does not: a deactivated account changes nothing about itself.\n match /user_profiles/{uid} {\n allow get: if isAuth() && request.auth.uid == uid;\n allow create: if isAuth() && request.auth.uid == uid && grantsUntouched();\n allow update: if isAuth() && request.auth.uid == uid && notDeactivated() &&\n (grantsUntouched() || isSeedingOwnAdmin());\n }\n\n match /{collectionName}/{docId} {\n \n allow read, write: if collectionName.matches('^admin_.*') && isAdmin();\n\n // sys_ holds theme, page and form config. It is read openly because the\n // login screen has to paint itself before anyone has signed in.\n allow read: if collectionName.matches('^sys_.*');\n allow write: if collectionName.matches('^sys_.*') && isAdmin();\n\n allow get: if isUserData(collectionName) && ownsUserDoc(docId);\n allow list: if isUserData(collectionName) && isDocOwner();\n allow write: if isUserData(collectionName) && ownsUserDoc(docId);\n \n // Public forms post here from pages a guest can open, so creating stays\n // open to everyone. Nothing else is: editing and deleting are the\n // admin's, via the wildcard above. Without that split anyone could\n // quietly overwrite or wipe every submission you have received.\n allow read: if collectionName == 'user_submissions' && (isDocOwner() || isAdmin());\n allow create: if collectionName == 'user_submissions';\n\n allow read, create: if collectionName.matches('^mem_.*') && isInternal();\n allow update, delete: if collectionName.matches('^mem_.*') && isInternal() && (isDocOwner() || request.auth.uid == docId);\n\n // Public pages are readable by guests — that is what makes them public —\n // but writing is not anonymous. Left open, anyone on the internet could\n // empty every public collection you have. Guest input goes through\n // user_submissions instead.\n allow read: if collectionName.matches('^pub_.*');\n allow write: if collectionName.matches('^pub_.*') && isVerified();\n\n match /{allPaths=**} {\n allow read: if (collectionName.matches('^admin_.*') && isAdmin())\n || collectionName.matches('^sys_.*')\n || collectionName.matches('^pub_.*')\n || (collectionName.matches('^mem_.*') && isInternal())\n || (collectionName.matches('^user_.*') && isDocOwner());\n \n allow create: if (collectionName.matches('^admin_.*') && isAdmin())\n || (collectionName.matches('^sys_.*') && isAdmin())\n || (isUserData(collectionName) && isDocOwner())\n || collectionName == 'user_submissions'\n || (collectionName.matches('^pub_.*') && isVerified())\n || (collectionName.matches('^mem_.*') && isInternal());\n\n allow update, delete: if collectionName.matches('^pub_.*') && isVerified();\n }\n }\n }\n}\n", us = "rules_version = '2';\nservice firebase.storage {\n match /b/{bucket}/o {\n\n // --- Helper Functions ---\n function isAuth() {\n return request.auth != null;\n }\n\n function hasProfile(uid) {\n return firestore.exists(/databases/(default)/documents/user_profiles/$(uid));\n }\n\n function profileRole(uid) {\n return firestore.get(/databases/(default)/documents/user_profiles/$(uid)).data.role;\n }\n\n // Matches the Firestore rules: an admin who switches someone off under\n // /users expects the files to close too. Until this was checked, only the\n // app's own Access Denied screen stood in the way, and a deactivated person\n // driving the SDK from a console kept every file they had. An account with\n // no profile document yet counts as active — registration seeds the profile\n // a moment after the account exists.\n function notDeactivated() {\n return isAuth() &&\n (!hasProfile(request.auth.uid) ||\n firestore.get(/databases/(default)/documents/user_profiles/$(request.auth.uid))\n .data.get('inactive', false) != true);\n }\n\n // Matches the Firestore rules: a person who never opened the verification\n // email has a signed-in token but no verified one, and nothing here opens\n // for them. Google sign-ins arrive verified.\n function isVerified() {\n return isAuth() && request.auth.token.email_verified == true && notDeactivated();\n }\n\n function isMemberDocAdmin(uid) {\n return hasProfile(uid) && profileRole(uid) == 'admin';\n }\n\n function isAdmin() {\n return isVerified() && isMemberDocAdmin(request.auth.uid);\n }\n\n // Files in mem_ folders belong to internal pages and to Drive's internal\n // folder. Signing up does not open them — an admin marks the account\n // internal under /users first.\n function isInternal() {\n return isVerified() && hasProfile(request.auth.uid) &&\n profileRole(request.auth.uid) in ['internal', 'admin'];\n }\n\n function hasOwnerMeta(reqRes) {\n return reqRes != null && 'metadata' in reqRes && reqRes.metadata != null && 'ownerId' in reqRes.metadata;\n }\n\n // --- Upload limits ---\n // Nothing capped uploads before this: any verified account could push a\n // file of any size and any kind into user_ or mem_, and the bill is the\n // owner's. 10 MB covers the documents, photos and short clips these pages\n // are for. Admins are exempt on purpose — the Assets screen is where the\n // owner puts their own logo, fonts and anything else the app needs.\n function underSizeCap(bytes) {\n return request.resource != null && request.resource.size < bytes;\n }\n\n // An allowlist rather than a blocklist, so a new dangerous type does not\n // arrive open. SVG is excluded from images because it can carry script and\n // Firebase serves it inline from the storage domain; the admin Assets path\n // can still take one.\n function allowedFileType() {\n return request.resource != null && request.resource.contentType != null && (\n (request.resource.contentType.matches('image/.*') &&\n !request.resource.contentType.matches('image/svg.*')) ||\n request.resource.contentType.matches('audio/.*') ||\n request.resource.contentType.matches('video/.*') ||\n request.resource.contentType in [\n 'text/plain', 'text/csv', 'text/markdown', 'text/calendar',\n 'application/pdf', 'application/json', 'application/rtf',\n 'application/zip', 'application/x-zip-compressed',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.ms-excel',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-powerpoint',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'application/vnd.oasis.opendocument.text',\n 'application/vnd.oasis.opendocument.spreadsheet',\n 'application/vnd.oasis.opendocument.presentation'\n ]\n );\n }\n\n function isUploadableImage() {\n return request.resource != null &&\n request.resource.contentType != null &&\n request.resource.contentType.matches('image/.*') &&\n !request.resource.contentType.matches('image/svg.*');\n }\n\n // --- Special Match: User Avatars ---\n // A picture, and a small one: the profile screen already refuses anything\n // over 2 MB, this is the same rule where it cannot be skipped.\n match /user_avatars/{uid}/{fileName} {\n allow read: if isVerified();\n allow create, update: if isVerified()\n && request.auth.uid == uid\n && isUploadableImage()\n && underSizeCap(5 * 1024 * 1024)\n && (hasOwnerMeta(request.resource) ? request.resource.metadata.ownerId == request.auth.uid : true);\n allow delete: if isVerified() && request.auth.uid == uid;\n }\n\n // --- Generic Folder Match ---\n match /{folder}/{allPaths=**} {\n\n // Public folder access\n allow read: if folder.matches('^pub_.*');\n\n // Internal folder access — internal members and admins only.\n // The folder test comes first on purpose: isInternal() reaches across to\n // Firestore, so every read of a pub_ file would otherwise pay for a\n // profile lookup it does not need. (Signed-in reads pay one either way\n // now — isVerified() has to look the deactivation flag up.)\n allow read: if folder.matches('^mem_.*') && isInternal();\n allow create: if folder.matches('^mem_.*')\n && isInternal()\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && hasOwnerMeta(request.resource)\n && request.resource.metadata.ownerId == request.auth.uid;\n allow update: if folder.matches('^mem_.*')\n && isInternal()\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n allow delete: if folder.matches('^mem_.*')\n && isInternal()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n\n // User-specific folders\n allow create: if isVerified()\n && folder.matches('^user_.*')\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && hasOwnerMeta(request.resource)\n && request.resource.metadata.ownerId == request.auth.uid;\n allow update: if isVerified()\n && folder.matches('^user_.*')\n && underSizeCap(10 * 1024 * 1024)\n && allowedFileType()\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n allow read, delete: if isVerified()\n && folder.matches('^user_.*')\n && resource != null\n && hasOwnerMeta(resource)\n && resource.metadata.ownerId == request.auth.uid;\n\n // Admin access overrides — the Assets screen holds the logo, the favicon\n // and anything else the owner needs the app to serve, so it is not capped\n // or filtered.\n allow read, create: if isAdmin();\n allow update, delete: if isAdmin();\n }\n }\n}\n", ds = [
|
|
15704
15745
|
{
|
|
15705
15746
|
key: "VITE_FIREBASE_API_KEY",
|
|
15706
15747
|
label: "API Key",
|
|
@@ -15732,7 +15773,7 @@ var $o = Qe.createContext({}), es = Object.defineProperty, ts = Object.getOwnPro
|
|
|
15732
15773
|
shownAs: "appId"
|
|
15733
15774
|
}
|
|
15734
15775
|
];
|
|
15735
|
-
function
|
|
15776
|
+
function fs(e) {
|
|
15736
15777
|
let t;
|
|
15737
15778
|
try {
|
|
15738
15779
|
if (t = {
|
|
@@ -15781,8 +15822,8 @@ function us(e) {
|
|
|
15781
15822
|
} catch {}
|
|
15782
15823
|
return "";
|
|
15783
15824
|
}
|
|
15784
|
-
function
|
|
15785
|
-
let n =
|
|
15825
|
+
function ps(e, t) {
|
|
15826
|
+
let n = fs(e);
|
|
15786
15827
|
if (n) return n;
|
|
15787
15828
|
if (t?.firebaseConfig) {
|
|
15788
15829
|
let n = {
|
|
@@ -15797,9 +15838,9 @@ function ds(e, t) {
|
|
|
15797
15838
|
}
|
|
15798
15839
|
return e === "VITE_ADMIN_EMAILS" && t?.adminEmails ? Array.isArray(t.adminEmails) ? t.adminEmails.join(",") : t.adminEmails || "" : "";
|
|
15799
15840
|
}
|
|
15800
|
-
function
|
|
15801
|
-
let t = [], n = [], r = (t) =>
|
|
15802
|
-
for (let { key: e, label: i } of
|
|
15841
|
+
function ms(e) {
|
|
15842
|
+
let t = [], n = [], r = (t) => ps(t, e);
|
|
15843
|
+
for (let { key: e, label: i } of ds) {
|
|
15803
15844
|
let a = r(e);
|
|
15804
15845
|
a ? a.trim() === "" && n.push(i) : t.push(i);
|
|
15805
15846
|
}
|
|
@@ -15811,19 +15852,19 @@ function fs(e) {
|
|
|
15811
15852
|
missingAdminEmails: a
|
|
15812
15853
|
};
|
|
15813
15854
|
}
|
|
15814
|
-
function
|
|
15815
|
-
return
|
|
15855
|
+
function hs() {
|
|
15856
|
+
return ms().allSet;
|
|
15816
15857
|
}
|
|
15817
|
-
var
|
|
15818
|
-
function
|
|
15858
|
+
var gs = us;
|
|
15859
|
+
function _s(e) {
|
|
15819
15860
|
let t = e.map((e) => e.trim().toLowerCase()).filter(Boolean).filter((e, t, n) => n.indexOf(e) === t), n = t.length ? t.map((e) => `'${e.replace(/'/g, "")}'`).join(", ") : "'your@email.com'";
|
|
15820
|
-
return
|
|
15861
|
+
return ls.replace(/(function adminEmails\(\) \{\s*return )\[[^\]]*\];/, `$1[${n}];`);
|
|
15821
15862
|
}
|
|
15822
|
-
function
|
|
15863
|
+
function vs(e) {
|
|
15823
15864
|
return e.split(/[,\s]+/).map((e) => e.trim()).filter(Boolean);
|
|
15824
15865
|
}
|
|
15825
|
-
var
|
|
15826
|
-
function
|
|
15866
|
+
var ys = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
15867
|
+
function bs({ code: e, label: t }) {
|
|
15827
15868
|
let [n, r] = Qe.useState(!1);
|
|
15828
15869
|
return /* @__PURE__ */ Z(H.button, {
|
|
15829
15870
|
whileHover: { scale: 1.02 },
|
|
@@ -15835,7 +15876,7 @@ function vs({ code: e, label: t }) {
|
|
|
15835
15876
|
children: [X(n ? R : se, { className: "w-4 h-4" }), n ? "Copied to Clipboard!" : t]
|
|
15836
15877
|
});
|
|
15837
15878
|
}
|
|
15838
|
-
function
|
|
15879
|
+
function xs({ name: e, shownAs: t, hint: n }) {
|
|
15839
15880
|
let [r, i] = Qe.useState(!1);
|
|
15840
15881
|
return /* @__PURE__ */ Z("div", {
|
|
15841
15882
|
className: "flex items-center justify-between gap-3 px-4 py-3 border-b border-[var(--panel-border)]/40 last:border-0",
|
|
@@ -15858,7 +15899,7 @@ function ys({ name: e, shownAs: t, hint: n }) {
|
|
|
15858
15899
|
})]
|
|
15859
15900
|
});
|
|
15860
15901
|
}
|
|
15861
|
-
function
|
|
15902
|
+
function Ss({ number: e, title: t, children: n, worked: r }) {
|
|
15862
15903
|
return /* @__PURE__ */ Z("div", {
|
|
15863
15904
|
className: "flex flex-col relative pb-10 mb-10 border-b border-[var(--panel-border)]/50 last:border-0 last:pb-0 last:mb-0",
|
|
15864
15905
|
children: [/* @__PURE__ */ Z("div", {
|
|
@@ -15880,11 +15921,11 @@ function bs({ number: e, title: t, children: n, worked: r }) {
|
|
|
15880
15921
|
})]
|
|
15881
15922
|
});
|
|
15882
15923
|
}
|
|
15883
|
-
function
|
|
15924
|
+
function Cs({ standalone: e }) {
|
|
15884
15925
|
let t = S(), [n] = K(() => {
|
|
15885
15926
|
let e = localStorage.getItem("theme-mode");
|
|
15886
15927
|
return e === "light" ? !1 : e === "dark" ? !0 : window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
15887
|
-
}), r =
|
|
15928
|
+
}), r = vs(ps("VITE_ADMIN_EMAILS", t)), i = r.filter((e) => !ys.test(e)), a = r.length > 0 && i.length === 0, o = _s(r);
|
|
15888
15929
|
return G(() => {
|
|
15889
15930
|
e && document.documentElement.setAttribute("data-theme", n ? "dark" : "light");
|
|
15890
15931
|
}, [n, e]), G(() => {
|
|
@@ -15952,7 +15993,7 @@ function xs({ standalone: e }) {
|
|
|
15952
15993
|
rel: "noopener noreferrer",
|
|
15953
15994
|
title: "WhatsApp Support",
|
|
15954
15995
|
className: "w-10 h-10 flex items-center justify-center rounded-xl transition-all duration-300 cursor-pointer btn-secondary",
|
|
15955
|
-
children: /* @__PURE__ */ X(
|
|
15996
|
+
children: /* @__PURE__ */ X(cs, { className: "w-4 h-4 text-foreground/80" })
|
|
15956
15997
|
})]
|
|
15957
15998
|
})]
|
|
15958
15999
|
})
|
|
@@ -15960,7 +16001,7 @@ function xs({ standalone: e }) {
|
|
|
15960
16001
|
/* @__PURE__ */ Z("div", {
|
|
15961
16002
|
className: "flex flex-col relative z-10 p-6 md:p-8 bg-foreground/[0.015] rounded-3xl border border-[var(--panel-border)]/50 mt-4",
|
|
15962
16003
|
children: [
|
|
15963
|
-
/* @__PURE__ */ X(
|
|
16004
|
+
/* @__PURE__ */ X(Ss, {
|
|
15964
16005
|
number: 1,
|
|
15965
16006
|
title: "Create a Firebase project",
|
|
15966
16007
|
worked: "You land on a page with your project's name across the top.",
|
|
@@ -16007,7 +16048,7 @@ function xs({ standalone: e }) {
|
|
|
16007
16048
|
]
|
|
16008
16049
|
})
|
|
16009
16050
|
}),
|
|
16010
|
-
/* @__PURE__ */ X(
|
|
16051
|
+
/* @__PURE__ */ X(Ss, {
|
|
16011
16052
|
number: 2,
|
|
16012
16053
|
title: "Add a web app and copy the six values",
|
|
16013
16054
|
worked: "Six values are on screen under your app's nickname. Leave this tab open — the next step needs them.",
|
|
@@ -16040,7 +16081,7 @@ function xs({ standalone: e }) {
|
|
|
16040
16081
|
]
|
|
16041
16082
|
})
|
|
16042
16083
|
}),
|
|
16043
|
-
/* @__PURE__ */ Z(
|
|
16084
|
+
/* @__PURE__ */ Z(Ss, {
|
|
16044
16085
|
number: 3,
|
|
16045
16086
|
title: "Paste the six values and your email into Secrets",
|
|
16046
16087
|
worked: "Seven secrets are listed in the Secrets tab, with the names spelled exactly as above.",
|
|
@@ -16061,17 +16102,17 @@ function xs({ standalone: e }) {
|
|
|
16061
16102
|
}),
|
|
16062
16103
|
/* @__PURE__ */ Z("div", {
|
|
16063
16104
|
className: "glass-panel border border-[var(--panel-border)] rounded-2xl overflow-hidden bg-background",
|
|
16064
|
-
children: [
|
|
16105
|
+
children: [ds.map(({ key: e, shownAs: t }) => /* @__PURE__ */ X(xs, {
|
|
16065
16106
|
name: e,
|
|
16066
16107
|
shownAs: t
|
|
16067
|
-
}, e)), /* @__PURE__ */ X(
|
|
16108
|
+
}, e)), /* @__PURE__ */ X(xs, {
|
|
16068
16109
|
name: "VITE_ADMIN_EMAILS",
|
|
16069
16110
|
hint: "the email you will sign in with"
|
|
16070
16111
|
})]
|
|
16071
16112
|
})
|
|
16072
16113
|
]
|
|
16073
16114
|
}),
|
|
16074
|
-
/* @__PURE__ */ X(
|
|
16115
|
+
/* @__PURE__ */ X(Ss, {
|
|
16075
16116
|
number: 4,
|
|
16076
16117
|
title: "Turn on sign-in",
|
|
16077
16118
|
worked: "Email/Password shows as Enabled, and your app's address is in the Authorized domains list.",
|
|
@@ -16125,7 +16166,7 @@ function xs({ standalone: e }) {
|
|
|
16125
16166
|
]
|
|
16126
16167
|
})
|
|
16127
16168
|
}),
|
|
16128
|
-
/* @__PURE__ */ Z(
|
|
16169
|
+
/* @__PURE__ */ Z(Ss, {
|
|
16129
16170
|
number: 5,
|
|
16130
16171
|
title: "Create the database and publish its rules",
|
|
16131
16172
|
worked: "The Rules tab shows your text with today's date next to it, and no red error.",
|
|
@@ -16199,13 +16240,13 @@ function xs({ standalone: e }) {
|
|
|
16199
16240
|
})
|
|
16200
16241
|
]
|
|
16201
16242
|
}),
|
|
16202
|
-
a && /* @__PURE__ */ X(
|
|
16243
|
+
a && /* @__PURE__ */ X(bs, {
|
|
16203
16244
|
code: o,
|
|
16204
16245
|
label: "Copy the rules"
|
|
16205
16246
|
})
|
|
16206
16247
|
]
|
|
16207
16248
|
}),
|
|
16208
|
-
/* @__PURE__ */ Z(
|
|
16249
|
+
/* @__PURE__ */ Z(Ss, {
|
|
16209
16250
|
number: 6,
|
|
16210
16251
|
title: "Create storage and publish its rules",
|
|
16211
16252
|
worked: "The Storage Rules tab shows your text with today's date next to it, and no red error.",
|
|
@@ -16240,8 +16281,8 @@ function xs({ standalone: e }) {
|
|
|
16240
16281
|
"."
|
|
16241
16282
|
] })
|
|
16242
16283
|
]
|
|
16243
|
-
}), /* @__PURE__ */ X(
|
|
16244
|
-
code:
|
|
16284
|
+
}), /* @__PURE__ */ X(bs, {
|
|
16285
|
+
code: gs,
|
|
16245
16286
|
label: "Copy the storage rules"
|
|
16246
16287
|
})]
|
|
16247
16288
|
})
|
|
@@ -16261,7 +16302,7 @@ function xs({ standalone: e }) {
|
|
|
16261
16302
|
}
|
|
16262
16303
|
//#endregion
|
|
16263
16304
|
//#region src/pages/Dashboard.tsx
|
|
16264
|
-
function
|
|
16305
|
+
function ws() {
|
|
16265
16306
|
let { user: e } = dn(), t = (e?.displayName || e?.email?.split("@")[0] || "there").split(" ")[0], n = t.charAt(0).toUpperCase() + t.slice(1);
|
|
16266
16307
|
return /* @__PURE__ */ Z("main", {
|
|
16267
16308
|
className: "flex-1 w-full max-w-7xl mx-auto p-4 md:p-8 z-10 flex flex-col pt-12 min-h-screen",
|
|
@@ -16332,7 +16373,7 @@ function Ss() {
|
|
|
16332
16373
|
}
|
|
16333
16374
|
//#endregion
|
|
16334
16375
|
//#region src/App.tsx
|
|
16335
|
-
var
|
|
16376
|
+
var Ts = () => /* @__PURE__ */ Z("div", {
|
|
16336
16377
|
className: "flex-1 flex flex-col items-center justify-center min-h-[60vh] z-10 animate-in fade-in duration-500",
|
|
16337
16378
|
children: [/* @__PURE__ */ X("div", {
|
|
16338
16379
|
className: "w-12 h-12 rounded-xl glass-panel border border-[var(--panel-border)] flex items-center justify-center mb-6 shadow-xl",
|
|
@@ -16345,38 +16386,38 @@ var Cs = () => /* @__PURE__ */ Z("div", {
|
|
|
16345
16386
|
})]
|
|
16346
16387
|
})]
|
|
16347
16388
|
});
|
|
16348
|
-
function
|
|
16389
|
+
function Es({ level: e, children: t }) {
|
|
16349
16390
|
let { user: n, userRole: r, loading: i, userInactive: a, signOut: o, roleResolved: s } = dn(), c = zt();
|
|
16350
|
-
return i || !s ? /* @__PURE__ */ X(
|
|
16391
|
+
return i || !s ? /* @__PURE__ */ X(Ts, {}) : n ? a ? /* @__PURE__ */ X(Yo, {
|
|
16351
16392
|
tone: "red",
|
|
16352
16393
|
title: "Access Denied",
|
|
16353
16394
|
message: "You don't have access to this app. Please contact the administrator.",
|
|
16354
16395
|
action: /* @__PURE__ */ X("button", {
|
|
16355
16396
|
onClick: () => o(),
|
|
16356
|
-
className:
|
|
16397
|
+
className: Xo,
|
|
16357
16398
|
children: "Sign Out"
|
|
16358
16399
|
})
|
|
16359
|
-
}) : n.emailVerified ? e === "admin" && !tn(r) ? /* @__PURE__ */ X(
|
|
16400
|
+
}) : n.emailVerified ? e === "admin" && !tn(r) ? /* @__PURE__ */ X(Qo, {}) : e === "internal" && !nn(r) ? /* @__PURE__ */ X(Zo, {}) : /* @__PURE__ */ X(Y, { children: t }) : /* @__PURE__ */ X(It, { to: "/verify" }) : /* @__PURE__ */ X(It, { to: `/login?from=${encodeURIComponent(c.pathname)}` });
|
|
16360
16401
|
}
|
|
16361
|
-
var
|
|
16402
|
+
var Ds = ({ children: e }) => /* @__PURE__ */ X(Es, {
|
|
16362
16403
|
level: "signed-in",
|
|
16363
16404
|
children: e
|
|
16364
|
-
}),
|
|
16405
|
+
}), Os = ({ children: e }) => /* @__PURE__ */ X(Es, {
|
|
16365
16406
|
level: "admin",
|
|
16366
16407
|
children: e
|
|
16367
16408
|
});
|
|
16368
|
-
function
|
|
16409
|
+
function ks({ children: e }) {
|
|
16369
16410
|
let { user: t, loading: n, roleResolved: r } = dn(), [i] = Ht();
|
|
16370
|
-
return n || !r ? /* @__PURE__ */ X(
|
|
16411
|
+
return n || !r ? /* @__PURE__ */ X(Ts, {}) : t && t.emailVerified ? /* @__PURE__ */ X(It, {
|
|
16371
16412
|
to: i.get("from") || "/dashboard",
|
|
16372
16413
|
replace: !0
|
|
16373
16414
|
}) : /* @__PURE__ */ X(Y, { children: e });
|
|
16374
16415
|
}
|
|
16375
|
-
function
|
|
16416
|
+
function As({ children: e }) {
|
|
16376
16417
|
let { user: t, loading: n, roleResolved: r } = dn();
|
|
16377
|
-
return n || !r ? /* @__PURE__ */ X(
|
|
16418
|
+
return n || !r ? /* @__PURE__ */ X(Ts, {}) : t ? t.emailVerified ? /* @__PURE__ */ X(It, { to: "/dashboard" }) : /* @__PURE__ */ X(Y, { children: e }) : /* @__PURE__ */ X(It, { to: "/" });
|
|
16378
16419
|
}
|
|
16379
|
-
function
|
|
16420
|
+
function js() {
|
|
16380
16421
|
return /* @__PURE__ */ Z("div", {
|
|
16381
16422
|
className: "fixed top-0 left-0 w-full h-full pointer-events-none -z-0 overflow-hidden",
|
|
16382
16423
|
children: [/* @__PURE__ */ X(H.div, {
|
|
@@ -16421,7 +16462,7 @@ function ks() {
|
|
|
16421
16462
|
})]
|
|
16422
16463
|
});
|
|
16423
16464
|
}
|
|
16424
|
-
function
|
|
16465
|
+
function Ms() {
|
|
16425
16466
|
let e = S().components?.Home || Sn;
|
|
16426
16467
|
return /* @__PURE__ */ X(un, { children: /* @__PURE__ */ Z("div", {
|
|
16427
16468
|
className: "min-h-screen relative flex flex-col overflow-x-hidden",
|
|
@@ -16430,23 +16471,23 @@ function As() {
|
|
|
16430
16471
|
/* @__PURE__ */ Z(Rt, { children: [
|
|
16431
16472
|
/* @__PURE__ */ X(Lt, {
|
|
16432
16473
|
path: "/",
|
|
16433
|
-
element: /* @__PURE__ */ X(
|
|
16474
|
+
element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(e, {}) })
|
|
16434
16475
|
}),
|
|
16435
16476
|
/* @__PURE__ */ X(Lt, {
|
|
16436
16477
|
path: "/register",
|
|
16437
|
-
element: /* @__PURE__ */ X(
|
|
16478
|
+
element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(Cn, {}) })
|
|
16438
16479
|
}),
|
|
16439
16480
|
/* @__PURE__ */ X(Lt, {
|
|
16440
16481
|
path: "/login",
|
|
16441
|
-
element: /* @__PURE__ */ X(
|
|
16482
|
+
element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(wn, {}) })
|
|
16442
16483
|
}),
|
|
16443
16484
|
/* @__PURE__ */ X(Lt, {
|
|
16444
16485
|
path: "/reset-password",
|
|
16445
|
-
element: /* @__PURE__ */ X(
|
|
16486
|
+
element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(An, {}) })
|
|
16446
16487
|
}),
|
|
16447
16488
|
/* @__PURE__ */ X(Lt, {
|
|
16448
16489
|
path: "/verify",
|
|
16449
|
-
element: /* @__PURE__ */ X(
|
|
16490
|
+
element: /* @__PURE__ */ X(As, { children: /* @__PURE__ */ X(Tn, {}) })
|
|
16450
16491
|
}),
|
|
16451
16492
|
/* @__PURE__ */ X(Lt, {
|
|
16452
16493
|
path: "/guide",
|
|
@@ -16486,15 +16527,15 @@ function As() {
|
|
|
16486
16527
|
}),
|
|
16487
16528
|
/* @__PURE__ */ X(Lt, {
|
|
16488
16529
|
path: "/users",
|
|
16489
|
-
element: /* @__PURE__ */ X(
|
|
16530
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Nn, {}) })
|
|
16490
16531
|
}),
|
|
16491
16532
|
/* @__PURE__ */ X(Lt, {
|
|
16492
16533
|
path: "/users/:id",
|
|
16493
|
-
element: /* @__PURE__ */ X(
|
|
16534
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Nn, {}) })
|
|
16494
16535
|
}),
|
|
16495
16536
|
/* @__PURE__ */ X(Lt, {
|
|
16496
16537
|
path: "/inbox",
|
|
16497
|
-
element: /* @__PURE__ */ X(
|
|
16538
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(br, {}) })
|
|
16498
16539
|
}),
|
|
16499
16540
|
/* @__PURE__ */ X(Lt, {
|
|
16500
16541
|
path: "/submissions",
|
|
@@ -16512,7 +16553,7 @@ function As() {
|
|
|
16512
16553
|
}),
|
|
16513
16554
|
/* @__PURE__ */ X(Lt, {
|
|
16514
16555
|
path: "/assets",
|
|
16515
|
-
element: /* @__PURE__ */ X(
|
|
16556
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Er, {}) })
|
|
16516
16557
|
}),
|
|
16517
16558
|
/* @__PURE__ */ X(Lt, {
|
|
16518
16559
|
path: "/drive/*",
|
|
@@ -16523,15 +16564,15 @@ function As() {
|
|
|
16523
16564
|
}),
|
|
16524
16565
|
/* @__PURE__ */ X(Lt, {
|
|
16525
16566
|
path: "/theme",
|
|
16526
|
-
element: /* @__PURE__ */ X(
|
|
16567
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Ka, {}) })
|
|
16527
16568
|
}),
|
|
16528
16569
|
/* @__PURE__ */ X(Lt, {
|
|
16529
16570
|
path: "/pages/*",
|
|
16530
|
-
element: /* @__PURE__ */ X(
|
|
16571
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(wo, {}) })
|
|
16531
16572
|
}),
|
|
16532
16573
|
/* @__PURE__ */ X(Lt, {
|
|
16533
16574
|
path: "/forms/*",
|
|
16534
|
-
element: /* @__PURE__ */ X(
|
|
16575
|
+
element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Lo, {}) })
|
|
16535
16576
|
}),
|
|
16536
16577
|
/* @__PURE__ */ X(Lt, {
|
|
16537
16578
|
path: "/settings",
|
|
@@ -16542,42 +16583,42 @@ function As() {
|
|
|
16542
16583
|
}),
|
|
16543
16584
|
/* @__PURE__ */ X(Lt, {
|
|
16544
16585
|
path: "/profile",
|
|
16545
|
-
element: /* @__PURE__ */ X(
|
|
16586
|
+
element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(kn, {}) })
|
|
16546
16587
|
}),
|
|
16547
16588
|
/* @__PURE__ */ X(Lt, {
|
|
16548
16589
|
path: "/dashboard",
|
|
16549
|
-
element: /* @__PURE__ */ X(
|
|
16590
|
+
element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(ws, {}) })
|
|
16550
16591
|
}),
|
|
16551
16592
|
/* @__PURE__ */ X(Lt, {
|
|
16552
16593
|
path: "/setup",
|
|
16553
|
-
element: /* @__PURE__ */ X(
|
|
16594
|
+
element: /* @__PURE__ */ X(Cs, {})
|
|
16554
16595
|
}),
|
|
16555
16596
|
/* @__PURE__ */ X(Lt, {
|
|
16556
16597
|
path: "*",
|
|
16557
|
-
element: /* @__PURE__ */ X(
|
|
16598
|
+
element: /* @__PURE__ */ X(es, {})
|
|
16558
16599
|
})
|
|
16559
16600
|
] }),
|
|
16560
|
-
/* @__PURE__ */ X(
|
|
16601
|
+
/* @__PURE__ */ X(js, {})
|
|
16561
16602
|
]
|
|
16562
16603
|
}) });
|
|
16563
16604
|
}
|
|
16564
|
-
function
|
|
16565
|
-
return /* @__PURE__ */ X(Ft, { children: /* @__PURE__ */ X(
|
|
16605
|
+
function Ns() {
|
|
16606
|
+
return /* @__PURE__ */ X(Ft, { children: /* @__PURE__ */ X(Ps, {}) });
|
|
16566
16607
|
}
|
|
16567
|
-
function
|
|
16608
|
+
function Ps() {
|
|
16568
16609
|
let e = S();
|
|
16569
|
-
return e.firebaseConfig && e.firebaseConfig.apiKey && e.firebaseConfig.apiKey.trim() !== "" && e.firebaseConfig.projectId && e.firebaseConfig.projectId.trim() !== "" ||
|
|
16610
|
+
return e.firebaseConfig && e.firebaseConfig.apiKey && e.firebaseConfig.apiKey.trim() !== "" && e.firebaseConfig.projectId && e.firebaseConfig.projectId.trim() !== "" || hs() ? /* @__PURE__ */ X(Ms, {}) : /* @__PURE__ */ Z("div", {
|
|
16570
16611
|
className: "min-h-screen relative flex flex-col overflow-x-hidden",
|
|
16571
16612
|
children: [
|
|
16572
16613
|
/* @__PURE__ */ X(xn, { standalone: !0 }),
|
|
16573
|
-
/* @__PURE__ */ X(
|
|
16574
|
-
/* @__PURE__ */ X(
|
|
16614
|
+
/* @__PURE__ */ X(Cs, { standalone: !0 }),
|
|
16615
|
+
/* @__PURE__ */ X(js, {})
|
|
16575
16616
|
]
|
|
16576
16617
|
});
|
|
16577
16618
|
}
|
|
16578
16619
|
//#endregion
|
|
16579
16620
|
//#region src/FirebaseOS.tsx
|
|
16580
|
-
function
|
|
16621
|
+
function Fs(e) {
|
|
16581
16622
|
let t = e.firebaseConfig;
|
|
16582
16623
|
return t && t.apiKey && t.apiKey.trim() !== "" && t.projectId && t.projectId.trim() !== "" && f(t), /* @__PURE__ */ X(xe, {
|
|
16583
16624
|
config: {
|
|
@@ -16597,9 +16638,9 @@ function Ns(e) {
|
|
|
16597
16638
|
background: "var(--bg-gradient)",
|
|
16598
16639
|
color: "var(--fg-color)"
|
|
16599
16640
|
},
|
|
16600
|
-
children: /* @__PURE__ */ X(
|
|
16641
|
+
children: /* @__PURE__ */ X(Ns, {})
|
|
16601
16642
|
}) })
|
|
16602
16643
|
});
|
|
16603
16644
|
}
|
|
16604
16645
|
//#endregion
|
|
16605
|
-
export { mn as AdminNotifications, U as Button, Pn as ConfirmModal, Ie as ContactPopup, Oe as CustomSelect, On as DashboardNav,
|
|
16646
|
+
export { mn as AdminNotifications, U as Button, Pn as ConfirmModal, Ie as ContactPopup, Oe as CustomSelect, On as DashboardNav, Fs as FirebaseOS, V as Input, xn as Navbar, Xe as PhoneInputField, w as auth, D as db, g as storage, dn as useAuth, S as useConfig, n as useTheme };
|