your-ai-workflow-firebase-os 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "your-ai-workflow-firebase-os",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "A complete Firebase-powered admin OS — one React component.",
5
5
  "type": "module",
6
6
  "main": "dist/your-ai-workflow-firebase-os.cjs.js",
@@ -32,7 +32,8 @@
32
32
  "build:lib": "vite build",
33
33
  "lint": "eslint .",
34
34
  "preview": "vite preview",
35
- "postinstall": "node scripts/postinstall.js"
35
+ "postinstall": "node scripts/postinstall.js",
36
+ "release": "npm version patch && npm run build:lib && npm pack"
36
37
  },
37
38
  "dependencies": {
38
39
  "@eslint/js": "^9.39.4",
@@ -78,4 +79,4 @@
78
79
  "react-dom": ">=18",
79
80
  "react-router-dom": ">=6"
80
81
  }
81
- }
82
+ }
@@ -9,8 +9,6 @@ import { doc, onSnapshot, setDoc, collection, getDocs, getDoc } from 'firebase/f
9
9
  import { ConfirmModal } from '../components/ConfirmModal';
10
10
  import { JsonNode } from './ThemeAdmin';
11
11
  import { defaultHomeConfig } from '../configs/pages/home.config';
12
- import { defaultContactConfig } from '../configs/pages/contact.config';
13
- import { defaultSupportConfig } from '../configs/pages/support.config';
14
12
  import { defaultPublicConfig } from '../configs/pages/pub.config';
15
13
  import { userPageConfig as defaultPrivateConfig } from '../configs/pages/user.config';
16
14
  import { adminPageConfig as defaultAdminConfig } from '../configs/pages/admin.config';
@@ -31,10 +29,6 @@ interface PageItem {
31
29
  const initialPages: PageItem[] = [
32
30
  // Public
33
31
  { id: 'home', title: 'Home Page', path: '/', type: 'public', isSystem: true, enabled: true },
34
- { id: 'contact', title: 'Contact', path: '/contact', type: 'public', isSystem: true, enabled: true },
35
-
36
- // Private
37
- { id: 'support', title: 'Request Support', path: '/support', type: 'private', isSystem: true, enabled: true },
38
32
  ];
39
33
 
40
34
  const Toggle = ({ enabled, onChange, disabled, className = '' }: { enabled: boolean, onChange: (val: boolean) => void, disabled?: boolean, className?: string }) => (
@@ -76,14 +70,6 @@ export function PagesAdmin() {
76
70
  const [homeConfig, setHomeConfig] = useState<any>(null);
77
71
  const [savingHome, setSavingHome] = useState(false);
78
72
  const [resettingHome, setResettingHome] = useState(false);
79
-
80
- const [contactConfig, setContactConfig] = useState<any>(null);
81
- const [savingContact, setSavingContact] = useState(false);
82
- const [resettingContact, setResettingContact] = useState(false);
83
-
84
- const [supportConfig, setSupportConfig] = useState<any>(null);
85
- const [savingSupport, setSavingSupport] = useState(false);
86
- const [resettingSupport, setResettingSupport] = useState(false);
87
73
  // ── Order-based visibility (single source of truth) ─────────────────────
88
74
  const [pageHiddenIds, setPageHiddenIds] = useState<Set<string>>(new Set());
89
75
  const [tabHiddenIds, setTabHiddenIds] = useState<Set<string>>(new Set());
@@ -126,13 +112,7 @@ export function PagesAdmin() {
126
112
  const unsubHome = onSnapshot(doc(db, 'sys_pages', 'home'), (docSnap) => {
127
113
  setHomeConfig(docSnap.exists() ? mergeConfig(defaultHomeConfig, docSnap.data()) : defaultHomeConfig);
128
114
  });
129
- const unsubContact = onSnapshot(doc(db, 'sys_pages', 'contact'), (docSnap) => {
130
- setContactConfig(docSnap.exists() ? mergeConfig(defaultContactConfig, docSnap.data()) : defaultContactConfig);
131
- });
132
- const unsubSupport = onSnapshot(doc(db, 'sys_tabs', 'support'), (docSnap) => {
133
- setSupportConfig(docSnap.exists() ? mergeConfig(defaultSupportConfig, docSnap.data()) : defaultSupportConfig);
134
- });
135
- return () => { unsubHome(); unsubContact(); unsubSupport(); };
115
+ return () => { unsubHome(); };
136
116
  }, []);
137
117
 
138
118
  useEffect(() => {
@@ -218,14 +198,12 @@ export function PagesAdmin() {
218
198
  val.pageType = pageType;
219
199
  }
220
200
 
221
- const isSystemPage = ['home', 'contact', 'support'].includes(id);
201
+ const isSystemPage = ['home'].includes(id);
222
202
  const targetId = isSystemPage
223
203
  ? id
224
204
  : (val.route ? val.route.replace(/^\/+/, '').replace(/[^a-zA-Z0-9_\-]/g, '_').toLowerCase() : id);
225
205
 
226
206
  if (id === 'home') setSavingHome(true);
227
- else if (id === 'contact') setSavingContact(true);
228
- else if (id === 'support') setSavingSupport(true);
229
207
  else setSavingConfigMap(p => ({ ...p, [id]: true, [targetId]: true }));
230
208
 
231
209
  try {
@@ -267,8 +245,6 @@ export function PagesAdmin() {
267
245
  } else {
268
246
  await setDoc(doc(db, collectionName, id), val);
269
247
  if (id === 'home') setHomeConfig(val);
270
- else if (id === 'contact') setContactConfig(val);
271
- else if (id === 'support') setSupportConfig(val);
272
248
  else {
273
249
  setDraftConfigs(p => ({ ...p, [id]: val }));
274
250
  setPages(prev => prev.map(p => p.id === id ? { ...p, path: val.route || p.path, title: val.pageName || val.tabName || val.title || p.title } : p));
@@ -279,8 +255,6 @@ export function PagesAdmin() {
279
255
  } finally {
280
256
  setTimeout(() => {
281
257
  if (id === 'home') setSavingHome(false);
282
- else if (id === 'contact') setSavingContact(false);
283
- else if (id === 'support') setSavingSupport(false);
284
258
  else setSavingConfigMap(p => ({ ...p, [id]: false, [targetId]: false }));
285
259
  }, 500);
286
260
  }
@@ -292,17 +266,6 @@ export function PagesAdmin() {
292
266
  setTimeout(() => setResettingHome(false), 500);
293
267
  };
294
268
 
295
- const handleResetContact = async (e: React.MouseEvent) => {
296
- e.stopPropagation(); setResettingContact(true);
297
- await handleSaveConfig('contact', defaultContactConfig);
298
- setTimeout(() => setResettingContact(false), 500);
299
- };
300
-
301
- const handleResetSupport = async (e: React.MouseEvent) => {
302
- e.stopPropagation(); setResettingSupport(true);
303
- await handleSaveConfig('support', defaultSupportConfig);
304
- setTimeout(() => setResettingSupport(false), 500);
305
- };
306
269
 
307
270
  const handleResetPage = async (e: React.MouseEvent, pageId: string, defConfig: any) => {
308
271
  e.stopPropagation(); setResettingMap(p => ({ ...p, [pageId]: true }));
@@ -428,12 +391,11 @@ export function PagesAdmin() {
428
391
  setPages(prev => [...prev, newPage]);
429
392
 
430
393
  let sourceConfig = draftConfigs[originalPage.id];
431
- if (!sourceConfig) {
432
- if (originalPage.id === 'contact') sourceConfig = contactConfig || defaultContactConfig;
433
- if (originalPage.id === 'support') sourceConfig = supportConfig || defaultSupportConfig;
394
+ if (!sourceConfig && originalPage.id === 'home') {
395
+ sourceConfig = homeConfig;
434
396
  }
435
397
 
436
- const newConfig = { ...sourceConfig, route: newPage.path, pageName: newPage.title };
398
+ const newConfig = { ...(sourceConfig || {}), route: newPage.path, pageName: newPage.title };
437
399
  if (newPage.type !== 'public') {
438
400
  newConfig.tabName = newPage.title;
439
401
  newConfig.pageType = newPage.type;
@@ -483,13 +445,9 @@ export function PagesAdmin() {
483
445
  try {
484
446
  const { deleteDoc } = await import('firebase/firestore');
485
447
  await Promise.all([
486
- deleteDoc(doc(db, 'sys_pages', 'home')),
487
- deleteDoc(doc(db, 'sys_pages', 'contact')),
488
- deleteDoc(doc(db, 'sys_tabs', 'support'))
448
+ deleteDoc(doc(db, 'sys_pages', 'home'))
489
449
  ]);
490
450
  setHomeConfig(defaultHomeConfig);
491
- setContactConfig(defaultContactConfig);
492
- setSupportConfig(defaultSupportConfig);
493
451
  } catch (e) {
494
452
  console.error("Error resetting defaults:", e);
495
453
  }
@@ -699,10 +657,8 @@ export function PagesAdmin() {
699
657
 
700
658
  const renderPage = (page: PageItem, pIdx: number, isHidden: boolean) => {
701
659
  const isHome = page.id === 'home';
702
- const isContact = page.id === 'contact';
703
- const isSupport = page.id === 'support';
704
660
  const isExpanded = expandedPageId === page.id;
705
- const isConfigurable = isHome || isContact || isSupport || !page.isSystem;
661
+ const isConfigurable = isHome || !page.isSystem;
706
662
 
707
663
  return (
708
664
  <div
@@ -721,7 +677,7 @@ export function PagesAdmin() {
721
677
  <div className="flex items-center gap-1 w-full flex-1">
722
678
  <div className="flex items-center gap-2">
723
679
  <h3 className="text-[14px] font-bold text-foreground truncate">{page.title}</h3>
724
- {((isHome && savingHome) || (isContact && savingContact) || (isSupport && savingSupport)) && <Loader2 className="w-3 h-3 animate-spin text-accent ml-2" />}
680
+ {isHome && savingHome && <Loader2 className="w-3 h-3 animate-spin text-accent ml-2" />}
725
681
  </div>
726
682
  <div className={`text-[11px] font-medium font-mono px-2 py-0.5 rounded w-fit ml-6 border ${isHidden ? 'text-foreground/50 border-foreground/10 bg-foreground/5' : 'text-accent/70 bg-accent/5 border-accent/10'}`}>
727
683
  {window.location.host}{page.path}
@@ -795,77 +751,7 @@ export function PagesAdmin() {
795
751
  </div>
796
752
  </motion.div>
797
753
  )}
798
- {isExpanded && isContact && contactConfig && (
799
- <motion.div
800
- initial={{ height: 0, opacity: 0 }}
801
- animate={{ height: "auto", opacity: 1 }}
802
- exit={{ height: 0, opacity: 0 }}
803
- className="overflow-hidden mt-4 pt-4 border-t border-[var(--panel-border)]/50 pb-4"
804
- onClick={e => e.stopPropagation()}
805
- >
806
- <div className="flex flex-col font-mono text-[13px] leading-relaxed relative bg-background/50 p-4 py-6 rounded-xl border border-[var(--panel-border)] shadow-inner">
807
754
 
808
- <div className="absolute top-4 right-4 flex items-center gap-1 z-20">
809
- <button onClick={handleResetContact} className="w-8 h-8 p-0 border-transparent bg-transparent hover:bg-red-500/10 rounded-lg flex items-center justify-center shrink-0 transition-colors group" title="Reset to Defaults">
810
- {resettingContact ? <Loader2 className="w-3.5 h-3.5 text-red-500 animate-spin" /> : <RotateCcw className="w-3.5 h-3.5 text-foreground/40 group-hover:text-red-500 transition-colors" />}
811
- </button>
812
- <button onClick={(e) => { e.stopPropagation(); handleCopyBtn('contact', contactConfig); }} className="w-8 h-8 p-0 border-transparent bg-transparent hover:bg-foreground/10 rounded-lg flex items-center justify-center shrink-0 transition-colors" title="Copy JSON">
813
- {copyStateId === 'contact' ? <CheckCircle2 className="w-3.5 h-3.5 text-emerald-500" /> : <Copy className="w-3.5 h-3.5 text-foreground/40 hover:text-foreground transition-colors" />}
814
- </button>
815
- </div>
816
- <JsonNode
817
- value={contactConfig}
818
- keysToRender={contactConfig.showButton !== false ? ['pageName', 'route', 'template', 'showButton', 'form'] : ['pageName', 'route', 'template', 'showButton']}
819
- disabledKeys={['route']}
820
- onChange={(val: any) => handleSaveConfig('contact', val)}
821
- isLast={true}
822
- isGlobalSaving={savingContact}
823
- customEnums={{ form: publicForms, template: ['popup_form', 'inline_form'] }}
824
- customExplanations={{
825
- pageName: "This defines the text that actually appears globally in the Navigation menus.",
826
- template: "This just simply tells our app when a user clicks on the contact page item what type of form do we load? is it a page or popup? this doesn't define form content! this setting just defines page layout.",
827
- form: "This is the form that will be used. Go to /forms to edit it."
828
- }}
829
- />
830
- </div>
831
- </motion.div>
832
- )}
833
- {isExpanded && isSupport && supportConfig && (
834
- <motion.div
835
- initial={{ height: 0, opacity: 0 }}
836
- animate={{ height: "auto", opacity: 1 }}
837
- exit={{ height: 0, opacity: 0 }}
838
- className="overflow-hidden mt-4 pt-4 border-t border-[var(--panel-border)]/50 pb-4"
839
- onClick={e => e.stopPropagation()}
840
- >
841
- <div className="flex flex-col font-mono text-[13px] leading-relaxed relative bg-background/50 p-4 py-6 rounded-xl border border-[var(--panel-border)] shadow-inner">
842
-
843
- <div className="absolute top-4 right-4 flex items-center gap-1 z-20">
844
- <button onClick={handleResetSupport} className="w-8 h-8 p-0 border-transparent bg-transparent hover:bg-red-500/10 rounded-lg flex items-center justify-center shrink-0 transition-colors group" title="Reset to Defaults">
845
- {resettingSupport ? <Loader2 className="w-3.5 h-3.5 text-red-500 animate-spin" /> : <RotateCcw className="w-3.5 h-3.5 text-foreground/40 group-hover:text-red-500 transition-colors" />}
846
- </button>
847
- <button onClick={(e) => { e.stopPropagation(); handleCopyBtn('support', supportConfig); }} className="w-8 h-8 p-0 border-transparent bg-transparent hover:bg-foreground/10 rounded-lg flex items-center justify-center shrink-0 transition-colors" title="Copy JSON">
848
- {copyStateId === 'support' ? <CheckCircle2 className="w-3.5 h-3.5 text-emerald-500" /> : <Copy className="w-3.5 h-3.5 text-foreground/40 hover:text-foreground transition-colors" />}
849
- </button>
850
- </div>
851
- <JsonNode
852
- value={supportConfig}
853
- keysToRender={supportConfig.showButton !== false ? ['pageName', 'iconName', 'route', 'template', 'showButton', 'form'] : ['pageName', 'iconName', 'route', 'template', 'showButton']}
854
- disabledKeys={['route']}
855
- onChange={(val: any) => handleSaveConfig('support', val)}
856
- isLast={true}
857
- isGlobalSaving={savingSupport}
858
- customEnums={{ form: privateForms, template: ['popup_form', 'inline_form'] }}
859
- customExplanations={{
860
- pageName: "Defines the textual title label rendering in internal sidebars.",
861
- iconName: "To modify this icon, visit https://iconoir.com/, find an icon, and copy its exact name (e.g. 'learning', 'server'). Then paste it here.",
862
- template: "This just simply tells our app when a user clicks on the support page item what type of form do we load? is it a page or popup? this doesn't define form content! this setting just defines page layout.",
863
- form: "This is the form that will be used. Go to /forms to edit it."
864
- }}
865
- />
866
- </div>
867
- </motion.div>
868
- )}
869
755
  {isExpanded && !page.isSystem && draftConfigs[page.id] && (
870
756
  <motion.div
871
757
  initial={{ height: 0, opacity: 0 }}