your-ai-workflow-firebase-os 1.2.21 → 1.2.23

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.21",
3
+ "version": "1.2.23",
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",
@@ -114,6 +114,21 @@ copyConfig('forms/contactForm.config.ts', 'contactForm.config.ts');
114
114
  copyConfig('forms/supportForm.config.ts', 'supportForm.config.ts');
115
115
  console.log(' ✓ Copied configuration files to your src/your-ai-workflow-firebase-os/configs directory');
116
116
 
117
+ // Copy microcomponents auto-registry
118
+ const microcomponentsSourceDir = path.join(__dirname, '..', 'src', 'microcomponents');
119
+ const microcomponentsDestDir = path.join(fbosDir, 'microcomponents');
120
+ if (fs.existsSync(microcomponentsSourceDir)) {
121
+ if (!fs.existsSync(microcomponentsDestDir)) fs.mkdirSync(microcomponentsDestDir, { recursive: true });
122
+ const files = fs.readdirSync(microcomponentsSourceDir);
123
+ for (const file of files) {
124
+ // Only copy the registry — user creates their own content files
125
+ if (file === 'registry.ts') {
126
+ fs.copyFileSync(path.join(microcomponentsSourceDir, file), path.join(microcomponentsDestDir, file));
127
+ }
128
+ }
129
+ console.log(' ✓ Copied microcomponents auto-registry to your src/your-ai-workflow-firebase-os/microcomponents directory');
130
+ }
131
+
117
132
  // Read and rewrite Home.tsx
118
133
  const sourceHomePath = path.join(__dirname, '..', 'src', 'pages', 'Home.tsx');
119
134
  if (fs.existsSync(sourceHomePath)) {
@@ -135,6 +150,9 @@ const appContent = `import { FirebaseOS } from 'your-ai-workflow-firebase-os';
135
150
  import { Home } from './your-ai-workflow-firebase-os/Home';
136
151
  import { themeConfig } from './your-ai-workflow-firebase-os/configs/theme.config';
137
152
 
153
+ // Auto-discovered microcomponents (zero-config: just create a .tsx file in microcomponents/)
154
+ import { microcomponentRegistry } from './your-ai-workflow-firebase-os/microcomponents/registry';
155
+
138
156
  // Local Configuration Defaults (The Final Boss)
139
157
  import { defaultHomeConfig } from './your-ai-workflow-firebase-os/configs/home.config';
140
158
  import { contactFormConfig } from './your-ai-workflow-firebase-os/configs/contactForm.config';
@@ -161,6 +179,7 @@ export default function App() {
161
179
  firebaseConfig={firebaseConfig}
162
180
  adminEmails={adminEmails}
163
181
  themeConfig={themeConfig}
182
+ microcomponents={microcomponentRegistry}
164
183
  components={{
165
184
  Home: Home
166
185
  }}
@@ -36,6 +36,9 @@ export interface FirebaseOSProps {
36
36
  /** Custom page components mapped by route (e.g. { "/focus": FocusTimerPage }) */
37
37
  customRoutes?: Record<string, React.ComponentType<any>>;
38
38
 
39
+ /** Auto-discovered microcomponents mapped by parsedTabName (e.g. { timer: TimerContent }) */
40
+ microcomponents?: Record<string, React.ComponentType<any>>;
41
+
39
42
  /** Component overrides for the hybrid template architecture */
40
43
  components?: {
41
44
  Home?: React.ComponentType<any>;
@@ -80,6 +83,7 @@ export function FirebaseOS(props: FirebaseOSProps) {
80
83
  themeConfig: props.themeConfig,
81
84
  components: props.components,
82
85
  customRoutes: props.customRoutes,
86
+ microcomponents: props.microcomponents,
83
87
  defaultConfigs: props.defaultConfigs,
84
88
  };
85
89
 
@@ -20,6 +20,8 @@ export interface FirebaseOSConfig {
20
20
  onAuthChange?: (user: any) => void;
21
21
  themeConfig?: any;
22
22
  customRoutes?: Record<string, React.ComponentType<any>>;
23
+ /** Auto-discovered microcomponents mapped by parsedTabName (e.g. { timer: TimerContent }) */
24
+ microcomponents?: Record<string, React.ComponentType<any>>;
23
25
  components?: {
24
26
  Home?: React.ComponentType<any>;
25
27
  TemplateBoard?: React.ComponentType<any>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Microcomponent Auto-Registry
3
+ *
4
+ * Uses Vite's import.meta.glob to auto-discover all .tsx files in this folder.
5
+ * Each file is matched to a tab by convention:
6
+ * - "TimerContent.tsx" → matches tab named "Timer" (parsedTabName: "timer")
7
+ * - "MyDashboardContent.tsx" → matches "My Dashboard" (parsedTabName: "my_dashboard")
8
+ *
9
+ * The AI only needs to create a file here. No routing or App.tsx changes needed.
10
+ */
11
+
12
+ const modules = import.meta.glob('./*.tsx', { eager: true }) as Record<string, { default: React.ComponentType<any> }>;
13
+
14
+ /**
15
+ * Auto-generated registry mapping parsedTabName → React component.
16
+ * e.g. { timer: TimerContent, my_dashboard: MyDashboardContent }
17
+ */
18
+ export const microcomponentRegistry: Record<string, React.ComponentType<any>> = {};
19
+
20
+ for (const filePath in modules) {
21
+ const mod = modules[filePath];
22
+ if (!mod?.default) continue;
23
+
24
+ // Extract component name from path: "./TimerContent.tsx" → "TimerContent"
25
+ const fileName = filePath.replace('./', '').replace('.tsx', '');
26
+
27
+ // Skip the registry file itself
28
+ if (fileName === 'registry') continue;
29
+
30
+ // Convert PascalCase to snake_case tab name:
31
+ // "TimerContent" → strip "Content" → "Timer" → "timer"
32
+ // "MyDashboardContent" → "MyDashboard" → "my_dashboard"
33
+ const baseName = fileName.replace(/Content$/, '');
34
+ const tabName = baseName
35
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
36
+ .toLowerCase();
37
+
38
+ microcomponentRegistry[tabName] = mod.default;
39
+ }
@@ -212,25 +212,36 @@ export function DynamicPage() {
212
212
  return <Navigate to="/login" state={{ from: location }} />;
213
213
  }
214
214
 
215
+ // Explicit customRoutes override (manual registration in App.tsx)
215
216
  const CustomRouteComponent = globalConfig.customRoutes?.[location.pathname] || (config && config.route ? globalConfig.customRoutes?.[config.route] : null);
216
- if (CustomRouteComponent) {
217
- return <CustomRouteComponent config={config} />;
218
- }
219
217
 
220
218
  const parsedPageName = config.pageName?.toLowerCase().replace(/\s+/g, '_') || config.pageId || 'custom_page';
221
219
  const prefix = `pub_${parsedPageName}`;
222
220
 
221
+ // Auto-discovered microcomponent by tab name (zero-config)
222
+ const parsedTabName = (config.tabName || config.pageId || config.pageName || 'custom')
223
+ .toLowerCase().replace(/[^a-z0-9]+/g, '_');
224
+ const AutoComponent = globalConfig.microcomponents?.[parsedTabName];
225
+
226
+ // Priority: explicit customRoute > auto-discovered microcomponent
227
+ const ContentOverride = CustomRouteComponent || AutoComponent || undefined;
228
+
223
229
  if (config.template === 'none') {
224
230
  if (config.isAdmin) {
225
- return <AdminPageTemplate config={config} />;
231
+ return <AdminPageTemplate config={config} customContent={ContentOverride} />;
226
232
  }
227
233
  if (config.isShared) {
228
- return <SharedPageTemplate config={config} />;
234
+ return <SharedPageTemplate config={config} customContent={ContentOverride} />;
229
235
  }
230
236
  if (config.isPrivate) {
231
- return <PrivatePageTemplate config={config} />;
237
+ return <PrivatePageTemplate config={config} customContent={ContentOverride} />;
232
238
  }
233
- return <PublicPageTemplate config={config} />;
239
+ return <PublicPageTemplate config={config} customContent={ContentOverride} />;
240
+ }
241
+
242
+ // For non-'none' templates, customRoutes still replace the full page
243
+ if (CustomRouteComponent) {
244
+ return <CustomRouteComponent config={config} />;
234
245
  }
235
246
 
236
247
  if (config.template === 'board') {
@@ -437,73 +437,81 @@ export function PagesAdmin() {
437
437
  [`user_${parsedTabName}_records`]: { prefix: 'user_files', driveCollection: 'user_files' },
438
438
  };
439
439
 
440
+ const cleanupPromises: Promise<any>[] = [];
441
+
440
442
  for (const col of collectionsClean) {
441
- try {
442
- const recordsSnap = await getDocs(collection(db, col));
443
- const mapping = storageMap[col];
444
-
445
- // Delete files from Storage and Drive for file-type records
446
- for (const d of recordsSnap.docs) {
447
- const data = d.data();
448
- if (data.recordType === 'file' && data.downloadURL && mapping) {
449
- try {
450
- const encodedPrefix = `${mapping.prefix}%2F`;
451
- const fileName = data.downloadURL.split(encodedPrefix)[1]?.split('?')[0];
452
- if (fileName) {
453
- const fileRef = ref(storage, `${mapping.prefix}/${decodeURIComponent(fileName)}`);
454
- await deleteObject(fileRef).catch(() => {});
455
- }
456
- // Remove matching Drive metadata doc by downloadURL
457
- const driveSnap = await getDocs(query(
458
- collection(db, mapping.driveCollection),
459
- where('downloadURL', '==', data.downloadURL)
460
- ));
461
- for (const driveDoc of driveSnap.docs) {
462
- await deleteDoc(driveDoc.ref);
463
- }
464
- } catch (fileErr) {
465
- console.error('Error deleting storage file:', fileErr);
443
+ cleanupPromises.push((async () => {
444
+ try {
445
+ const recordsSnap = await getDocs(collection(db, col));
446
+ const mapping = storageMap[col];
447
+ const filePromises: Promise<any>[] = [];
448
+
449
+ // Delete files from Storage and Drive for file-type records
450
+ for (const d of recordsSnap.docs) {
451
+ const data = d.data();
452
+ if (data.recordType === 'file' && data.downloadURL && mapping) {
453
+ filePromises.push((async () => {
454
+ try {
455
+ const encodedPrefix = `${mapping.prefix}%2F`;
456
+ const fileName = data.downloadURL.split(encodedPrefix)[1]?.split('?')[0];
457
+ if (fileName) {
458
+ const fileRef = ref(storage, `${mapping.prefix}/${decodeURIComponent(fileName)}`);
459
+ await deleteObject(fileRef).catch(() => {});
460
+ }
461
+ // Remove matching Drive metadata doc by downloadURL
462
+ const driveSnap = await getDocs(query(
463
+ collection(db, mapping.driveCollection),
464
+ where('downloadURL', '==', data.downloadURL)
465
+ ));
466
+ await Promise.all(driveSnap.docs.map(driveDoc => deleteDoc(driveDoc.ref)));
467
+ } catch (fileErr) {
468
+ console.error('Error deleting storage file:', fileErr);
469
+ }
470
+ })());
466
471
  }
467
472
  }
468
- }
473
+ await Promise.all(filePromises);
469
474
 
470
- // Delete all record documents in this collection
471
- const deletePromises = recordsSnap.docs.map(d => deleteDoc(d.ref));
472
- await Promise.all(deletePromises);
475
+ // Delete all record documents in this collection
476
+ await Promise.all(recordsSnap.docs.map(d => deleteDoc(d.ref)));
473
477
 
474
- // Clear localStorage cache for this collection
475
- try { localStorage.removeItem(`fbos_records_${col}`); } catch {}
476
- } catch (colErr) {
477
- console.error(`Error cleaning up collection ${col}:`, colErr);
478
- }
478
+ // Clear localStorage cache for this collection
479
+ try { localStorage.removeItem(`fbos_records_${col}`); } catch {}
480
+ } catch (colErr) {
481
+ console.error(`Error cleaning up collection ${col}:`, colErr);
482
+ }
483
+ })());
479
484
  }
480
485
 
481
486
  // Also clean Drive collections by sourceTab (catches any orphaned file docs)
482
487
  const driveCollections = ['admin_files', 'mem_files', 'user_files'];
483
488
  for (const driveColl of driveCollections) {
484
- try {
485
- const orphanedSnap = await getDocs(query(
486
- collection(db, driveColl),
487
- where('sourceTab', '==', parsedTabName)
488
- ));
489
- for (const orphanDoc of orphanedSnap.docs) {
490
- const data = orphanDoc.data();
491
- // Also delete the actual file from Storage if we haven't already
492
- if (data.downloadURL) {
493
- try {
494
- const prefix = driveColl.replace('_files', '_files');
495
- const encodedPrefix = `${prefix}%2F`;
496
- const fName = data.downloadURL.split(encodedPrefix)[1]?.split('?')[0];
497
- if (fName) {
498
- const fRef = ref(storage, `${prefix}/${decodeURIComponent(fName)}`);
499
- await deleteObject(fRef).catch(() => {});
500
- }
501
- } catch {}
502
- }
503
- await deleteDoc(orphanDoc.ref);
504
- }
505
- } catch {}
489
+ cleanupPromises.push((async () => {
490
+ try {
491
+ const orphanedSnap = await getDocs(query(
492
+ collection(db, driveColl),
493
+ where('sourceTab', '==', parsedTabName)
494
+ ));
495
+ await Promise.all(orphanedSnap.docs.map(async (orphanDoc) => {
496
+ const data = orphanDoc.data();
497
+ // Also delete the actual file from Storage if we haven't already
498
+ if (data.downloadURL) {
499
+ try {
500
+ const encodedPrefix = `${driveColl}%2F`;
501
+ const fName = data.downloadURL.split(encodedPrefix)[1]?.split('?')[0];
502
+ if (fName) {
503
+ const fRef = ref(storage, `${driveColl}/${decodeURIComponent(fName)}`);
504
+ await deleteObject(fRef).catch(() => {});
505
+ }
506
+ } catch {}
507
+ }
508
+ await deleteDoc(orphanDoc.ref);
509
+ }));
510
+ } catch {}
511
+ })());
506
512
  }
513
+
514
+ await Promise.all(cleanupPromises);
507
515
  }
508
516
 
509
517
  // Delete the tab/page document itself
@@ -1,44 +1,19 @@
1
1
  export const publicPagePrompt = (config: any, forms: string[] = []) => {
2
2
  const parsedPageName = (config.pageName || config.pageId || 'custom_page')
3
3
  .toLowerCase().replace(/[^a-z0-9]+/g, '_');
4
- const microName = `${parsedPageName.charAt(0).toUpperCase()}${parsedPageName.slice(1)}Content`;
4
+ const pascalName = parsedPageName
5
+ .split('_')
6
+ .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
7
+ .join('');
8
+ const fileName = `${pascalName}Content`;
5
9
 
6
- return `I need your help building the inner content for a newly created Public Page. This page is powered by the \`PublicPageTemplate\` (located at \`src/templates/PublicPageTemplate.tsx\`).
10
+ return `Create file: src/your-ai-workflow-firebase-os/microcomponents/${fileName}.tsx
7
11
 
8
- The page "infrastructure" (routing, header, public accessibility) is already set up and working. Your goal is to design the "Landing Content" that lives inside this page.
12
+ export default function ${fileName}({ config }: { config: any }) {
13
+ return ( <div>your content here</div> );
14
+ }
9
15
 
10
- ### ── Current Page Configuration ──────────────────────────────────────────────
11
- \`\`\`json
12
- ${JSON.stringify({
13
- pageName: config.pageName,
14
- pageTitle: config.pageTitle,
15
- route: config.route,
16
- template: config.template,
17
- }, null, 2)}
18
- \`\`\`
16
+ No page titles, no nav, no routing — already handled. Default export only.
19
17
 
20
- ### ── Available Forms ────────────────────────────────────────────────────────
21
- The following public forms are available in the system. **These are for your information only—do NOT use them unless specifically needed.** Your priority is to listen to the user's specific input and decide if a particular modal, form, or component is actually required for the context of this page:
22
- ${forms.length > 0 ? forms.map(f => `- ${f}`).join('\n') : '- No public forms found'}
23
-
24
- ### ── Your Mission ────────────────────────────────────────────────────────────
25
- 1. **Create the Content Layer**: Design a new micro-component at \`src/microcomponents/${microName}.tsx\`.
26
- 2. **Build the UI**: Inside this component, build the landing page content (hero sections, feature grids, contact sections, etc.).
27
- 3. **Inject into Template**: Once created, import and place \`<${microName} />\` inside the main content area of \`src/templates/PublicPageTemplate.tsx\`, replacing the placeholder robot icon and default text.
28
-
29
- ### ── Guardrails ─────────────────────────────────────────────────────────────
30
- - **DO NOT** modify the outer layout of \`PublicPageTemplate.tsx\` (back buttons, page header, footer, dynamic title).
31
- - **DO NOT** change the routing or page exports.
32
- - Focus exclusively on the inner content box using the micro-component strategy.
33
-
34
- ### ── Aesthetic & Performance (Theme System) ───────────────────────────────
35
- Build with the user's active theme in mind:
36
- - **CSS Variables**: \`var(--bg-color)\`, \`var(--fg-color)\`, \`var(--accent-color)\`, \`var(--panel-bg)\`, \`var(--panel-border)\`.
37
- - **Components**: Re-use \`<Button />\`, \`<Input />\`, \`<ConfirmModal />\`, and \`<ContactPopup />\` natively.
38
- - **Styles**: Use the \`glass-panel\` class for sections and \`text-gradient\` for headers.
39
-
40
- ### ── Important Formatting Rules ──────────────────────────────────────────────
41
- - **NO REDUNDANT HEADERS**: The parent template already injects a large dynamic page title and action buttons. DO NOT add a main \`<h1>\` or \`<h2>\` page title at the top of your micro-component. Start directly with your cards, lists, or sub-sections.
42
-
43
- [WHAT SHOULD I BUILD ON THIS PUBLIC PAGE?]`;
18
+ [WHAT TO BUILD?]`;
44
19
  };
@@ -1,63 +1,21 @@
1
1
  export const adminCrudPrompt = (config: any, recordsCollection: string) => {
2
2
  const parsedTabName = (config.tabName || config.pageId || 'admin_page')
3
3
  .toLowerCase().replace(/[^a-z0-9]+/g, '_');
4
- const microName = `${parsedTabName.charAt(0).toUpperCase()}${parsedTabName.slice(1)}Content`;
4
+ const pascalName = parsedTabName
5
+ .split('_')
6
+ .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
7
+ .join('');
8
+ const fileName = `${pascalName}Content`;
5
9
 
6
- return `I need your help building the inner functionality for an **already existing** Admin View. This view is powered by the \`AdminPageTemplate\` (located at \`src/templates/AdminPageTemplate.tsx\`).
10
+ return `Create file: src/your-ai-workflow-firebase-os/microcomponents/${fileName}.tsx
7
11
 
8
- The "infrastructure" (routing, header, database connection) is entirely set up and functioning. Your goal is to design the "Dashboard Content" that lives inside this specific admin view.
12
+ export default function ${fileName}({ config }: { config: any }) {
13
+ return ( <div>your content here</div> );
14
+ }
9
15
 
10
- ### ── Current View Configuration ──────────────────────────────────────────────
11
- \`\`\`json
12
- ${JSON.stringify({
13
- viewName: config.tabName,
14
- viewTitle: config.tabTitle,
15
- route: config.route,
16
- template: config.template,
17
- storageEnabled: !!config.storage,
18
- showActionButton: !!config.showButton,
19
- buttonStyle: config.buttonAction || 'primary',
20
- }, null, 2)}
21
- \`\`\`
16
+ Firestore collection: \`${recordsCollection}\`
17
+ Firebase imports: \`import { db } from 'your-ai-workflow-firebase-os'\`
18
+ No page titles, no nav, no routing — already handled. Default export only.
22
19
 
23
- ### ── Note on Forms ──────────────────────────────────────────────────────────
24
- **No forms are needed for this view.** Focus on building the administrative dashboard and management tools requested by the user.
25
-
26
- ### ── Data & Storage Architecture ─────────────────────────────────────────────
27
- The app is already configured to handle data for this specific view using these paths:
28
- - **Firestore Collection**: \`${recordsCollection}\` (Use this for all view data).
29
- - **Storage Layer**: ${config.storage ? "Enabled. Uploads are handled via \`admin_files/\` folder in Storage and mirrored to a shared Firestore collection for tracking." : "Disabled for this view."}
30
- - **Access Level**: Full Admin Access. This view is only visible to users with the 'admin' role.
31
-
32
- ### ── Your Mission ────────────────────────────────────────────────────────────
33
- 1. **Create the Page**: Create a full page component at \`src/pages/\${microName}Page.tsx\`. Include \`<DashboardNav />\` from \`your-ai-workflow-firebase-os\` at the top of the content area.
34
- 2. **Handle UX/UI**: Inside this component, build the interface (lists, charts, management forms, or any feature requested).
35
- 3. **Register the Route**: Open \`src/App.tsx\` and map this component to the \`customRoutes\` prop of \`<FirebaseOS>\`:
36
- \`\`\`tsx
37
- import { \${microName}Page } from './pages/\${microName}Page';
38
-
39
- // Inside App.tsx:
40
- <FirebaseOS
41
- customRoutes={{
42
- '\${config.route}': \${microName}Page
43
- }}
44
- // ...
45
- />
46
- \`\`\`
47
-
48
- ### ── Guardrails ─────────────────────────────────────────────────────────────
49
- - **CRITICAL: DO NOT CREATE A NEW ROUTE IN REACT ROUTER.** You are only mapping a component to an existing dynamic route via \\\`customRoutes\\\`.
50
- - **Database Logic**: You must write your own database queries inside the component using the provided collection names.
51
-
52
- ### ── Aesthetic & Performance (Theme System) ───────────────────────────────
53
- Build with the user's active theme in mind:
54
- - **CSS Variables**: \`var(--bg-color)\`, \`var(--fg-color)\`, \`var(--accent-color)\`, \`var(--panel-bg)\`, \`var(--panel-border)\`.
55
- - **Components**: Re-use \`<Button />\`, \`<Input />\`, \`<ConfirmModal />\`, and \`<DashboardNav />\` natively.
56
- - **Styles**: Use \`glass-panel\` and \`text-gradient\` for a premium look.
57
- - **Example**: See \`src/microcomponents/AdminExampleContent.tsx\` for a structural reference.
58
-
59
- ### ── Important Formatting Rules ──────────────────────────────────────────────
60
- - **NO REDUNDANT HEADERS**: The parent template already injects a large dynamic page title and action buttons. DO NOT add a main \`<h1>\` or \`<h2>\` page title at the top of your micro-component. Start directly with your cards, lists, or sub-sections.
61
-
62
- [WHAT FEATURES SHOULD I ADD TO THIS ADMIN VIEW?]`;
20
+ [WHAT TO BUILD?]`;
63
21
  };
@@ -1,65 +1,21 @@
1
- export const privateCrudPrompt = (config: any, recordsCollection: string, forms: string[] = []) => {
1
+ export const privateCrudPrompt = (config: any, recordsCollection: string) => {
2
2
  const parsedTabName = (config.tabName || config.pageId || 'private_page')
3
3
  .toLowerCase().replace(/[^a-z0-9]+/g, '_');
4
- const microName = `${parsedTabName.charAt(0).toUpperCase()}${parsedTabName.slice(1)}Content`;
4
+ const pascalName = parsedTabName
5
+ .split('_')
6
+ .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
7
+ .join('');
8
+ const fileName = `${pascalName}Content`;
5
9
 
6
- return `I need your help building the inner functionality for an **already existing** Private User View. This view is powered by the \`PrivatePageTemplate\` (located at \`src/templates/PrivatePageTemplate.tsx\`).
10
+ return `Create file: src/your-ai-workflow-firebase-os/microcomponents/${fileName}.tsx
7
11
 
8
- The "infrastructure" (auth-guards, dynamic routing, navigation menus, database sync) is entirely set up and functioning. Your task is strictly to design the "Dashboard Content" that lives inside this specific user view.
12
+ export default function ${fileName}({ config }: { config: any }) {
13
+ return ( <div>your content here</div> );
14
+ }
9
15
 
10
- ### ── Current View Configuration ──────────────────────────────────────────────
11
- \`\`\`json
12
- ${JSON.stringify({
13
- viewName: config.tabName,
14
- viewTitle: config.tabTitle,
15
- route: config.route,
16
- template: config.template,
17
- storageEnabled: !!config.storage,
18
- showActionButton: !!config.showButton,
19
- availablePrivateForms: forms,
20
- }, null, 2)}
21
- \`\`\`
16
+ Firestore collection: \`${recordsCollection}\` filter by \`where('uid', '==', user.uid)\`
17
+ Firebase: \`import { db, useAuth } from 'your-ai-workflow-firebase-os'\`
18
+ No page titles, no nav, no routing — already handled. Default export only.
22
19
 
23
- ### ── Available Forms ────────────────────────────────────────────────────────
24
- The following private forms are available in the system. **These are for your information only—do NOT use them unless specifically needed.** Your priority is to listen to the user's specific input and decide if a particular modal, form, or component is actually required for the context of this page:
25
- ${forms.length > 0 ? forms.map(f => `- ${f}`).join('\n') : '- No private forms found'}
26
-
27
- ### ── Data & Storage Architecture ─────────────────────────────────────────────
28
- Every user sees their own unique data on this page. The system is rigged to handle this isolation:
29
- - **Firestore Collection**: \`${recordsCollection}\`. The template already filters this by \`uid == current_user.uid\`. Use this for all record storage.
30
- - **Storage Layer**: ${config.storage ? "Enabled. User files are saved to \`user_files/\` and strictly isolated by user ID." : "Disabled for this view."}
31
- - **Access Level**: Private. Only the owner of the data can view or modify these records.
32
-
33
- ### ── Your Mission ────────────────────────────────────────────────────────────
34
- 1. **Create the Page**: Create a full page component at \`src/pages/\${microName}Page.tsx\`. Include \`<DashboardNav />\` from \`your-ai-workflow-firebase-os\` at the top of the content area.
35
- 2. **Handle UX/UI**: Inside this component, build the interface (lists, charts, forms, etc).
36
- 3. **Register the Route**: Open \`src/App.tsx\` and map this component to the \`customRoutes\` prop of \`<FirebaseOS>\`:
37
- \`\`\`tsx
38
- import { \${microName}Page } from './pages/\${microName}Page';
39
-
40
- // Inside App.tsx:
41
- <FirebaseOS
42
- customRoutes={{
43
- '\${config.route}': \${microName}Page
44
- }}
45
- // ...
46
- />
47
- \`\`\`
48
-
49
- ### ── Guardrails ─────────────────────────────────────────────────────────────
50
- - **CRITICAL: DO NOT CREATE A NEW ROUTE IN REACT ROUTER.** You are only mapping a component to an existing dynamic route via \\\`customRoutes\\\`.
51
- - **Database Logic**: You must write your own database queries inside the component using the provided collection names.
52
- - Re-use the \`ContactPopup\` component with the available private forms if the user needs to submit requests.
53
-
54
- ### ── Aesthetic & Performance (Theme System) ───────────────────────────────
55
- Build with the user's active theme in mind:
56
- - **CSS Variables**: Use \`var(--bg-color)\`, \`var(--fg-color)\`, \`var(--accent-color)\`, \`var(--panel-bg)\`, \`var(--panel-border)\`.
57
- - **Components**: Re-use \`<Button />\`, \`<Input />\`, \`<ConfirmModal />\`, and \`<DashboardNav />\` natively.
58
- - **Styles**: Use \`glass-panel\` and \`text-gradient\` for a premium look.
59
- - **Example**: See \`src/microcomponents/PrivateExampleContent.tsx\` for a structural reference.
60
-
61
- ### ── Important Formatting Rules ──────────────────────────────────────────────
62
- - **NO REDUNDANT HEADERS**: The parent template already injects a large dynamic page title and action buttons. DO NOT add a main \`<h1>\` or \`<h2>\` page title at the top of your micro-component. Start directly with your cards, lists, or sub-sections.
63
-
64
- [WHAT SHOULD I BUILD INSIDE THIS PRIVATE VIEW?]`;
20
+ [WHAT TO BUILD?]`;
65
21
  };
@@ -1,63 +1,21 @@
1
1
  export const sharedCrudPrompt = (config: any, recordsCollection: string) => {
2
2
  const parsedTabName = (config.tabName || config.pageId || 'shared_page')
3
3
  .toLowerCase().replace(/[^a-z0-9]+/g, '_');
4
- const microName = `${parsedTabName.charAt(0).toUpperCase()}${parsedTabName.slice(1)}Content`;
4
+ const pascalName = parsedTabName
5
+ .split('_')
6
+ .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
7
+ .join('');
8
+ const fileName = `${pascalName}Content`;
5
9
 
6
- return `I need your help building the inner functionality for an **already existing** Shared Member View. This view is powered by the \`SharedPageTemplate\` (located at \`src/templates/SharedPageTemplate.tsx\`).
10
+ return `Create file: src/your-ai-workflow-firebase-os/microcomponents/${fileName}.tsx
7
11
 
8
- The "infrastructure" (real-time sync, collaborative routing) is entirely set up and functioning. Your goal is to design the "Community Content" that lives inside this specific shared view.
12
+ export default function ${fileName}({ config }: { config: any }) {
13
+ return ( <div>your content here</div> );
14
+ }
9
15
 
10
- ### ── Current View Configuration ──────────────────────────────────────────────
11
- \`\`\`json
12
- ${JSON.stringify({
13
- viewName: config.tabName,
14
- viewTitle: config.tabTitle,
15
- route: config.route,
16
- template: config.template,
17
- storageEnabled: !!config.storage,
18
- showActionButton: !!config.showButton,
19
- }, null, 2)}
20
- \`\`\`
16
+ Firestore collection: \`${recordsCollection}\` shared between all members.
17
+ Firebase: \`import { db, useAuth } from 'your-ai-workflow-firebase-os'\`
18
+ No page titles, no nav, no routing — already handled. Default export only.
21
19
 
22
- ### ── Note on Forms ──────────────────────────────────────────────────────────
23
- **No forms are needed for this view.** Focus on building the collaborative features and shared content areas requested by the user.
24
-
25
- ### ── Data & Storage Architecture ─────────────────────────────────────────────
26
- All logged-in members share the data on this page. The system is configured for collaboration:
27
- - **Firestore Collection**: \`${recordsCollection}\`. All authenticated users can read and create records here. The template ensures only creators can edit/delete their own items.
28
- - **Storage Layer**: ${config.storage ? "Enabled. Shared files are saved to \`mem_files/\` and are visible to all members." : "Disabled for this view."}
29
- - **Access Level**: Shared Member Access. Requires authentication.
30
-
31
- ### ── Your Mission ────────────────────────────────────────────────────────────
32
- 1. **Create the Page**: Create a full page component at \`src/pages/\${microName}Page.tsx\`. Include \`<DashboardNav />\` from \`your-ai-workflow-firebase-os\` at the top of the content area.
33
- 2. **Handle UX/UI**: Inside this component, build the collaborative interface (group lists, message boards, shared resource trackers).
34
- 3. **Register the Route**: Open \`src/App.tsx\` and map this component to the \`customRoutes\` prop of \`<FirebaseOS>\`:
35
- \`\`\`tsx
36
- import { \${microName}Page } from './pages/\${microName}Page';
37
-
38
- // Inside App.tsx:
39
- <FirebaseOS
40
- customRoutes={{
41
- '\${config.route}': \${microName}Page
42
- }}
43
- // ...
44
- />
45
- \`\`\`
46
-
47
- ### ── Guardrails ─────────────────────────────────────────────────────────────
48
- - **CRITICAL: DO NOT CREATE A NEW ROUTE IN REACT ROUTER.** You are only mapping a component to an existing dynamic route via \\\`customRoutes\\\`.
49
- - **Database Logic**: You must write your own database queries inside the component using the provided collection names.
50
- - **NEVER** expose administrative control settings in this shared view.
51
-
52
- ### ── Aesthetic & Performance (Theme System) ───────────────────────────────
53
- Build with the user's active theme in mind:
54
- - **CSS Variables**: \`var(--bg-color)\`, \`var(--fg-color)\`, \`var(--accent-color)\`, \`var(--panel-bg)\`, \`var(--panel-border)\`.
55
- - **Components**: Re-use \`<Button />\`, \`<Input />\`, \`<ConfirmModal />\`, and \`<DashboardNav />\` natively.
56
- - **Styles**: Use \`glass-panel\` and \`text-gradient\` for a premium look.
57
- - **Example**: See \`src/microcomponents/SharedExampleContent.tsx\` for a structural reference.
58
-
59
- ### ── Important Formatting Rules ──────────────────────────────────────────────
60
- - **NO REDUNDANT HEADERS**: The parent template already injects a large dynamic page title and action buttons. DO NOT add a main \`<h1>\` or \`<h2>\` page title at the top of your micro-component. Start directly with your cards, lists, or sub-sections.
61
-
62
- [WHAT SHARED FEATURE SHOULD I BUILD IN THIS VIEW?]`;
20
+ [WHAT TO BUILD?]`;
63
21
  };