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
- id: "home",
10506
- title: "Home",
10507
- path: "/",
10508
- type: "public",
10509
- isSystem: !0,
10510
- enabled: !0
10511
- }], Qa = (e) => [...e, ...Za.filter((t) => !e.some((e) => e.id === t.id))], $a = {
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 = [`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.`, `Write one file: ${fo(e, t)}, with a default export. That file is what the page shows.`];
10597
- 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 ${xo(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");
10598
- }, go = (e, t) => {
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
- }, _o = (e, t) => {
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
- }, vo = "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", yo = "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", bo = (e) => {
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
- }, xo = (e) => e.replace(/[_-]+/g, " ").replace(/^./, (e) => e.toUpperCase());
10615
- function So({ page: e, config: t, forms: n, saving: r, onSave: i, addressTaken: a, actions: o, footer: s, isNew: c }) {
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: xo(e)
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 Co() {
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 = _o(f[e.id], t);
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.id === "home") return;
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.id !== "home" && !a.some((t) => t.id === e.id)).forEach((e) => a.push({
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 === "home", 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";
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: [!i && !l && /* @__PURE__ */ Z("div", {
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: vo,
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: vo,
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: yo,
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 ${bo(n.path)}` : ""]
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
- !i && !l && /* @__PURE__ */ X(Hn, {
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: "Change the home page",
11400
- 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."
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(So, {
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 && go(f[n.id], _e(n.type)) && /* @__PURE__ */ X(Hn, {
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 wo = new Set(["contact", "support"]), To = [
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
- ], Eo = { age: "Age" }, Do = (e) => To.find((t) => t.value === e)?.label || Eo[e || ""] || "Short text", Oo = (e) => To.some((t) => t.value === e) || !e ? To : [...To, {
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: Do(e)
11575
- }], ko = {
11627
+ label: Oo(e)
11628
+ }], Ao = {
11576
11629
  public: "Anyone",
11577
11630
  private: "Signed-in users"
11578
- }, Ao = (e) => (e || "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, ""), jo = (e, t) => {
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
- }, Mo = (e, t, n) => {
11584
- if (!(!e.name || e.name === Ao(e.label || ""))) return e.name;
11585
- let r = Ao(t);
11586
- return r ? jo(r, n.filter((t) => t !== e).map((e) => e.name)) : "";
11587
- }, No = () => ({
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
- }), Po = {
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 Fo({ choices: e, onChange: t }) {
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 Io() {
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 (!wo.has(e)) {
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 = wo.has(e) || !!t[e] || !!n?.defaultConfigs?.[e] || !!n?.defaultConfigs?.[`${e}Form`];
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: ["· ", Do(n.type)]
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: Mo(n, e, i)
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: Oo(n.type),
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(Fo, {
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 = wo.has(e), i = n.formType || "public", a = n.fields || [], o = p.has(e);
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, No()]),
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, No()]),
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: Po.forPage(n.title || "", e)
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: Po.firstForm
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
- /* @__PURE__ */ X(W, { children: Object.entries(i).map(([e, t]) => {
12156
- let n = C[e] || t, r = h[e], i = l[e], a = (n.fields || []).length;
12157
- return /* @__PURE__ */ Z(H.div, {
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
- opacity: 0,
12160
- y: 12
12272
+ height: 0,
12273
+ opacity: 0
12161
12274
  },
12162
12275
  animate: {
12163
- opacity: 1,
12164
- y: 0
12276
+ height: "auto",
12277
+ opacity: 1
12165
12278
  },
12166
12279
  exit: {
12167
- opacity: 0,
12168
- y: -12
12169
- },
12170
- transition: {
12171
- duration: .35,
12172
- ease: "easeOut"
12280
+ height: 0,
12281
+ opacity: 0
12173
12282
  },
12174
- onClick: () => se(e),
12175
- 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" : ""}`,
12176
- children: [/* @__PURE__ */ Z("div", {
12177
- className: "flex flex-col sm:flex-row items-start sm:items-center justify-between w-full",
12178
- children: [/* @__PURE__ */ Z("div", {
12179
- className: "flex flex-col gap-1 w-full sm:w-auto pr-4",
12180
- children: [/* @__PURE__ */ Z("div", {
12181
- className: "flex items-center gap-2",
12182
- children: [/* @__PURE__ */ X("h3", {
12183
- className: "text-[14px] font-bold text-foreground break-words ml-1 leading-snug",
12184
- children: n.title || "Untitled form"
12185
- }), i && /* @__PURE__ */ X(y, { className: "w-3 h-3 animate-spin text-accent ml-2 shrink-0" })]
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 Lo({ config: e, customContent: t }) {
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 Ro({ config: e, customContent: r }) {
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 zo({ config: e, customContent: r }) {
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 Bo({ config: e, customContent: r }) {
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 Vo = [
15323
+ var Ho = [
15280
15324
  "board",
15281
15325
  "table",
15282
15326
  "calendar",
15283
15327
  "drive"
15284
- ], Ho = ["inline_form", "popup_form"], Uo = (e) => typeof e != "string" || e === "" || e === "none" || e === "custom" || ![...Vo, ...Ho].includes(e), Wo = "src/your-ai-workflow-firebase-os";
15285
- function Go() {
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 Ko(e) {
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 ${Wo}/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.`,
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. ${Wo}/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.`,
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 qo({ tone: e, title: t, message: n, action: r }) {
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 Jo = "w-full block py-2.5 bg-foreground/5 hover:bg-foreground/10 text-foreground text-[14px] font-bold rounded-xl transition-colors", Yo = () => /* @__PURE__ */ X(qo, {
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: Jo,
15380
+ className: Xo,
15337
15381
  children: "Back to dashboard"
15338
15382
  })
15339
- }), Xo = () => /* @__PURE__ */ X(qo, {
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: Jo,
15389
+ className: Xo,
15346
15390
  children: "Back to dashboard"
15347
15391
  })
15348
- }), Zo = (e, t) => {
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 Qo() {
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 Zo(e, e.isPrivate === !0);
15413
+ return $o(e, e.isPrivate === !0);
15370
15414
  } catch {}
15371
15415
  }
15372
- if (e === "/contact") return {
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 = Zo(e.value.data(), t), m = e.value.id) : e.value.docs && e.value.docs.length > 0 && (p = Zo(e.value.docs[0].data(), t), m = e.value.docs[0].id));
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) if (e.pathname === "/contact") 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 = Zo({
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 (!t) return;
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 = Go();
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: Ko(e.pathname)
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(Xo, {}) : /* @__PURE__ */ X(Yo, {});
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 (Uo(c.template) || m && ![
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(Bo, {
15643
+ }, C) : c.isInternal ? /* @__PURE__ */ X(Vo, {
15603
15644
  config: c,
15604
15645
  customContent: x
15605
- }, C) : c.isPrivate ? /* @__PURE__ */ X(Ro, {
15646
+ }, C) : c.isPrivate ? /* @__PURE__ */ X(zo, {
15606
15647
  config: c,
15607
15648
  customContent: x
15608
- }, C) : /* @__PURE__ */ X(Lo, {
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 $o = Qe.createContext({}), es = Object.defineProperty, ts = Object.getOwnPropertySymbols, ns = Object.prototype.hasOwnProperty, rs = Object.prototype.propertyIsEnumerable, is = (e, t, n) => t in e ? es(e, t, {
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, as = (e, t) => {
15678
- for (var n in t ||= {}) ns.call(t, n) && is(e, n, t[n]);
15679
- if (ts) for (var n of ts(t)) rs.call(t, n) && is(e, n, t[n]);
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
- }, os = tt((e, t) => {
15682
- let n = as(as({}, Ze.useContext($o)), e);
15683
- return Ze.createElement("svg", as({
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 us(e) {
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 ds(e, t) {
15785
- let n = us(e);
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 fs(e) {
15801
- let t = [], n = [], r = (t) => ds(t, e);
15802
- for (let { key: e, label: i } of ls) {
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 ps() {
15815
- return fs().allSet;
15855
+ function hs() {
15856
+ return ms().allSet;
15816
15857
  }
15817
- var ms = cs;
15818
- function hs(e) {
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 ss.replace(/(function adminEmails\(\) \{\s*return )\[[^\]]*\];/, `$1[${n}];`);
15861
+ return ls.replace(/(function adminEmails\(\) \{\s*return )\[[^\]]*\];/, `$1[${n}];`);
15821
15862
  }
15822
- function gs(e) {
15863
+ function vs(e) {
15823
15864
  return e.split(/[,\s]+/).map((e) => e.trim()).filter(Boolean);
15824
15865
  }
15825
- var _s = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
15826
- function vs({ code: e, label: t }) {
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 ys({ name: e, shownAs: t, hint: n }) {
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 bs({ number: e, title: t, children: n, worked: r }) {
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 xs({ standalone: e }) {
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 = gs(ds("VITE_ADMIN_EMAILS", t)), i = r.filter((e) => !_s.test(e)), a = r.length > 0 && i.length === 0, o = hs(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(os, { className: "w-4 h-4 text-foreground/80" })
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(bs, {
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(bs, {
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(bs, {
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: [ls.map(({ key: e, shownAs: t }) => /* @__PURE__ */ X(ys, {
16105
+ children: [ds.map(({ key: e, shownAs: t }) => /* @__PURE__ */ X(xs, {
16065
16106
  name: e,
16066
16107
  shownAs: t
16067
- }, e)), /* @__PURE__ */ X(ys, {
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(bs, {
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(bs, {
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(vs, {
16243
+ a && /* @__PURE__ */ X(bs, {
16203
16244
  code: o,
16204
16245
  label: "Copy the rules"
16205
16246
  })
16206
16247
  ]
16207
16248
  }),
16208
- /* @__PURE__ */ Z(bs, {
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(vs, {
16244
- code: ms,
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 Ss() {
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 Cs = () => /* @__PURE__ */ Z("div", {
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 ws({ level: e, children: t }) {
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(Cs, {}) : n ? a ? /* @__PURE__ */ X(qo, {
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: Jo,
16397
+ className: Xo,
16357
16398
  children: "Sign Out"
16358
16399
  })
16359
- }) : n.emailVerified ? e === "admin" && !tn(r) ? /* @__PURE__ */ X(Xo, {}) : e === "internal" && !nn(r) ? /* @__PURE__ */ X(Yo, {}) : /* @__PURE__ */ X(Y, { children: t }) : /* @__PURE__ */ X(It, { to: "/verify" }) : /* @__PURE__ */ X(It, { to: `/login?from=${encodeURIComponent(c.pathname)}` });
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 Ts = ({ children: e }) => /* @__PURE__ */ X(ws, {
16402
+ var Ds = ({ children: e }) => /* @__PURE__ */ X(Es, {
16362
16403
  level: "signed-in",
16363
16404
  children: e
16364
- }), Es = ({ children: e }) => /* @__PURE__ */ X(ws, {
16405
+ }), Os = ({ children: e }) => /* @__PURE__ */ X(Es, {
16365
16406
  level: "admin",
16366
16407
  children: e
16367
16408
  });
16368
- function Ds({ children: e }) {
16409
+ function ks({ children: e }) {
16369
16410
  let { user: t, loading: n, roleResolved: r } = dn(), [i] = Ht();
16370
- return n || !r ? /* @__PURE__ */ X(Cs, {}) : t && t.emailVerified ? /* @__PURE__ */ X(It, {
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 Os({ children: e }) {
16416
+ function As({ children: e }) {
16376
16417
  let { user: t, loading: n, roleResolved: r } = dn();
16377
- return n || !r ? /* @__PURE__ */ X(Cs, {}) : t ? t.emailVerified ? /* @__PURE__ */ X(It, { to: "/dashboard" }) : /* @__PURE__ */ X(Y, { children: e }) : /* @__PURE__ */ X(It, { to: "/" });
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 ks() {
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 As() {
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(Ds, { children: /* @__PURE__ */ X(e, {}) })
16474
+ element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(e, {}) })
16434
16475
  }),
16435
16476
  /* @__PURE__ */ X(Lt, {
16436
16477
  path: "/register",
16437
- element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(Cn, {}) })
16478
+ element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(Cn, {}) })
16438
16479
  }),
16439
16480
  /* @__PURE__ */ X(Lt, {
16440
16481
  path: "/login",
16441
- element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(wn, {}) })
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(Ds, { children: /* @__PURE__ */ X(An, {}) })
16486
+ element: /* @__PURE__ */ X(ks, { children: /* @__PURE__ */ X(An, {}) })
16446
16487
  }),
16447
16488
  /* @__PURE__ */ X(Lt, {
16448
16489
  path: "/verify",
16449
- element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Tn, {}) })
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(Es, { children: /* @__PURE__ */ X(Nn, {}) })
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(Es, { children: /* @__PURE__ */ X(Nn, {}) })
16534
+ element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Nn, {}) })
16494
16535
  }),
16495
16536
  /* @__PURE__ */ X(Lt, {
16496
16537
  path: "/inbox",
16497
- element: /* @__PURE__ */ X(Es, { children: /* @__PURE__ */ X(br, {}) })
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(Es, { children: /* @__PURE__ */ X(Er, {}) })
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(Es, { children: /* @__PURE__ */ X(Ka, {}) })
16567
+ element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(Ka, {}) })
16527
16568
  }),
16528
16569
  /* @__PURE__ */ X(Lt, {
16529
16570
  path: "/pages/*",
16530
- element: /* @__PURE__ */ X(Es, { children: /* @__PURE__ */ X(Co, {}) })
16571
+ element: /* @__PURE__ */ X(Os, { children: /* @__PURE__ */ X(wo, {}) })
16531
16572
  }),
16532
16573
  /* @__PURE__ */ X(Lt, {
16533
16574
  path: "/forms/*",
16534
- element: /* @__PURE__ */ X(Es, { children: /* @__PURE__ */ X(Io, {}) })
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(Ts, { children: /* @__PURE__ */ X(kn, {}) })
16586
+ element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(kn, {}) })
16546
16587
  }),
16547
16588
  /* @__PURE__ */ X(Lt, {
16548
16589
  path: "/dashboard",
16549
- element: /* @__PURE__ */ X(Ts, { children: /* @__PURE__ */ X(Ss, {}) })
16590
+ element: /* @__PURE__ */ X(Ds, { children: /* @__PURE__ */ X(ws, {}) })
16550
16591
  }),
16551
16592
  /* @__PURE__ */ X(Lt, {
16552
16593
  path: "/setup",
16553
- element: /* @__PURE__ */ X(xs, {})
16594
+ element: /* @__PURE__ */ X(Cs, {})
16554
16595
  }),
16555
16596
  /* @__PURE__ */ X(Lt, {
16556
16597
  path: "*",
16557
- element: /* @__PURE__ */ X(Qo, {})
16598
+ element: /* @__PURE__ */ X(es, {})
16558
16599
  })
16559
16600
  ] }),
16560
- /* @__PURE__ */ X(ks, {})
16601
+ /* @__PURE__ */ X(js, {})
16561
16602
  ]
16562
16603
  }) });
16563
16604
  }
16564
- function js() {
16565
- return /* @__PURE__ */ X(Ft, { children: /* @__PURE__ */ X(Ms, {}) });
16605
+ function Ns() {
16606
+ return /* @__PURE__ */ X(Ft, { children: /* @__PURE__ */ X(Ps, {}) });
16566
16607
  }
16567
- function Ms() {
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() !== "" || ps() ? /* @__PURE__ */ X(As, {}) : /* @__PURE__ */ Z("div", {
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(xs, { standalone: !0 }),
16574
- /* @__PURE__ */ X(ks, {})
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 Ns(e) {
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(js, {})
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, Ns 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 };
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 };