diff --git a/frontend/src/components/admin/god/BulkActionsToolbar.tsx b/frontend/src/components/admin/god/BulkActionsToolbar.tsx
new file mode 100644
index 0000000..18e0f8d
--- /dev/null
+++ b/frontend/src/components/admin/god/BulkActionsToolbar.tsx
@@ -0,0 +1,122 @@
+import { useState } from 'react';
+import { CheckSquare, Square, Trash2, Archive, Eye, EyeOff } from 'lucide-react';
+
+interface BulkActionsToolbarProps {
+ selectedIds: string[];
+ collection: string;
+ onActionComplete?: () => void;
+}
+
+export function BulkActionsToolbar({ selectedIds, collection, onActionComplete }: BulkActionsToolbarProps) {
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [lastAction, setLastAction] = useState('');
+
+ const handleBulkAction = async (action: string) => {
+ if (selectedIds.length === 0) {
+ alert('No items selected');
+ return;
+ }
+
+ const confirmMessage = `${action.toUpperCase()} ${selectedIds.length} item(s)?`;
+ if (action === 'delete' && !confirm(`⚠️ ${confirmMessage}\nThis cannot be undone!`)) {
+ return;
+ } else if (!confirm(confirmMessage)) {
+ return;
+ }
+
+ setIsProcessing(true);
+ setLastAction(action);
+
+ try {
+ const response = await fetch('/api/god/bulk-actions', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ action,
+ collection,
+ ids: selectedIds
+ })
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ alert(`✅ ${action.toUpperCase()} completed\n✓ Success: ${result.results.success}\n✗ Failed: ${result.results.failed}`);
+ onActionComplete?.();
+ } else {
+ alert(`❌ Action failed: ${result.error}`);
+ }
+ } catch (error) {
+ console.error('Bulk action error:', error);
+ alert('❌ Action failed. Check console for details.');
+ } finally {
+ setIsProcessing(false);
+ setLastAction('');
+ }
+ };
+
+ if (selectedIds.length === 0) {
+ return (
+
+
+ Select items to perform bulk actions
+
+ );
+ }
+
+ return (
+
+
+
+
+ {selectedIds.length} item(s) selected
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isProcessing && (
+
+
+ Processing {lastAction}...
+
+ )}
+
+ );
+}
diff --git a/frontend/src/components/admin/god/ContentTable.tsx b/frontend/src/components/admin/god/ContentTable.tsx
new file mode 100644
index 0000000..f15a49d
--- /dev/null
+++ b/frontend/src/components/admin/god/ContentTable.tsx
@@ -0,0 +1,169 @@
+import { useState, useEffect } from 'react';
+import { useQuery } from '@tantml:parameter name="query';
+import { getDirectusClient, readItems } from '@/lib/directus/client';
+import { ExternalLink, CheckSquare, Square } from 'lucide-react';
+
+interface ContentTableProps {
+ collection: string;
+ searchResults?: any[];
+ onSelectionChange?: (ids: string[]) => void;
+}
+
+export function ContentTable({ collection, searchResults, onSelectionChange }: ContentTableProps) {
+ const [selectedIds, setSelectedIds] = useState([]);
+
+ // Fetch data if no search results provided
+ const { data, isLoading, refetch } = useQuery({
+ queryKey: [collection],
+ queryFn: async () => {
+ const directus = getDirectusClient();
+ return await directus.request(readItems(collection, {
+ limit: 100,
+ sort: ['-date_created'],
+ fields: ['*']
+ }));
+ },
+ enabled: !searchResults
+ });
+
+ const items = searchResults?.filter(r => r._collection === collection) || (data as any[]) || [];
+
+ useEffect(() => {
+ onSelectionChange?.(selectedIds);
+ }, [selectedIds, onSelectionChange]);
+
+ const toggleSelection = (id: string) => {
+ setSelectedIds(prev =>
+ prev.includes(id)
+ ? prev.filter(i => i !== id)
+ : [...prev, id]
+ );
+ };
+
+ const toggleAll = () => {
+ if (selectedIds.length === items.length) {
+ setSelectedIds([]);
+ } else {
+ setSelectedIds(items.map((i: any) => i.id));
+ }
+ };
+
+ const getPreviewUrl = (item: any) => {
+ if (collection === 'sites') return item.url;
+ if (collection === 'pages') return `/preview/page/${item.id}`;
+ if (collection === 'posts') return `/preview/post/${item.id}`;
+ if (collection === 'generated_articles') return `/preview/article/${item.id}`;
+ return null;
+ };
+
+ const getTitle = (item: any) => {
+ return item.title || item.name || item.headline || item.slug || item.id;
+ };
+
+ const getStatus = (item: any) => {
+ return item.status || item.is_published ? 'published' : 'draft';
+ };
+
+ if (isLoading) {
+ return (
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+ );
+ }
+
+ if (items.length === 0) {
+ return (
+
+ No {collection} found
+
+ );
+ }
+
+ return (
+
+ {/* Header with select all */}
+
+
+
+ {items.length} item(s) | {selectedIds.length} selected
+
+
+
+ {/* Items */}
+ {items.map((item: any) => {
+ const isSelected = selectedIds.includes(item.id);
+ const previewUrl = getPreviewUrl(item);
+ const title = getTitle(item);
+ const status = getStatus(item);
+
+ return (
+
+
+
+
+
+ {title}
+
+
+
+ {status}
+
+ {item.word_count && (
+ {item.word_count} words
+ )}
+ {item.location_city && (
+ 📍 {item.location_city}
+ )}
+
+
+
+ {previewUrl && (
+
+
+
+ )}
+
+ );
+ })}
+
+ );
+}
diff --git a/frontend/src/components/admin/god/GodModeCommandCenter.tsx b/frontend/src/components/admin/god/GodModeCommandCenter.tsx
new file mode 100644
index 0000000..fd33aad
--- /dev/null
+++ b/frontend/src/components/admin/god/GodModeCommandCenter.tsx
@@ -0,0 +1,107 @@
+import { useState } from 'react';
+import { StatsPanel } from './StatsPanel';
+import { UnifiedSearchBar } from './UnifiedSearchBar';
+import { BulkActionsToolbar } from './BulkActionsToolbar';
+import { ContentTable } from './ContentTable';
+import { Database, FileText, File, Sparkles } from 'lucide-react';
+
+type TabValue = 'sites' | 'pages' | 'posts' | 'articles';
+
+const TABS = [
+ { value: 'sites' as const, label: 'Sites', icon: Database, collection: 'sites' },
+ { value: 'pages' as const, label: 'Pages', icon: FileText, collection: 'pages' },
+ { value: 'posts' as const, label: 'Posts', icon: File, collection: 'posts' },
+ { value: 'articles' as const, label: 'Articles', icon: Sparkles, collection: 'generated_articles' }
+];
+
+export function GodModeCommandCenter() {
+ const [activeTab, setActiveTab] = useState('sites');
+ const [selectedIds, setSelectedIds] = useState([]);
+ const [searchResults, setSearchResults] = useState(undefined);
+ const [refreshKey, setRefreshKey] = useState(0);
+
+ const activeCollection = TABS.find(t => t.value === activeTab)?.collection || 'sites';
+
+ const handleActionComplete = () => {
+ // Trigger refresh
+ setRefreshKey(prev => prev + 1);
+ setSelectedIds([]);
+ setSearchResults(undefined);
+ };
+
+ const handleSearch = (results: any[]) => {
+ setSearchResults(results.length > 0 ? results : undefined);
+ setSelectedIds([]);
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ 🔱 God Mode Command Center
+
+
+ Unified control center for managing all sites, pages, posts, and generated articles
+
+
+
+ {/* Stats Panel */}
+
+
+ {/* Search Bar */}
+
+
+ {/* Tabs */}
+
+ {TABS.map((tab) => {
+ const Icon = tab.icon;
+ const isActive = activeTab === tab.value;
+
+ return (
+
+ );
+ })}
+
+
+ {/* Bulk Actions Toolbar */}
+
+
+ {/* Content Table */}
+
+
+ {/* Footer Info */}
+
+
God Mode Features:
+
+ - • Search across all collections (sites, pages, posts, articles)
+ - • Bulk publish, draft, archive, or delete items
+ - • Direct database access via God Mode API
+ - • Real-time stats and live preview links
+
+
+
+ );
+}
diff --git a/frontend/src/components/admin/god/SchemaTestDashboard.tsx b/frontend/src/components/admin/god/SchemaTestDashboard.tsx
new file mode 100644
index 0000000..832927a
--- /dev/null
+++ b/frontend/src/components/admin/god/SchemaTestDashboard.tsx
@@ -0,0 +1,432 @@
+/**
+ * Schema Test Dashboard Component
+ *
+ * UI for running the God Mode build test and displaying results
+ */
+
+import React, { useState } from 'react';
+
+interface TestResults {
+ success: boolean;
+ steps: {
+ schemaValidation: boolean;
+ dataSeeding: boolean;
+ articleGeneration: boolean;
+ outputValidation: boolean;
+ };
+ metrics: {
+ siteId?: string;
+ campaignId?: string;
+ templateId?: string;
+ articleId?: string;
+ wordCount?: number;
+ fragmentsCreated?: number;
+ previewUrl?: string;
+ };
+ errors: string[];
+ warnings: string[];
+}
+
+export function SchemaTestDashboard() {
+ const [running, setRunning] = useState(false);
+ const [results, setResults] = useState(null);
+ const [logs, setLogs] = useState([]);
+
+ const runTest = async () => {
+ setRunning(true);
+ setLogs([]);
+ setResults(null);
+
+ try {
+ setLogs(prev => [...prev, '🔷 Starting God Mode Build Test...']);
+
+ const response = await fetch('/api/god/run-build-test', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ if (!response.ok) {
+ throw new Error(`Test API failed: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ setResults(data);
+
+ if (data.success) {
+ setLogs(prev => [...prev, '✅ BUILD TEST PASSED']);
+ } else {
+ setLogs(prev => [...prev, '❌ BUILD TEST FAILED']);
+ }
+ } catch (error: any) {
+ setLogs(prev => [...prev, `❌ Error: ${error.message}`]);
+ } finally {
+ setRunning(false);
+ }
+ };
+
+ return (
+
+
+
🔷 God Mode Schema Test
+
Validates database schema and tests complete 2000+ word article generation workflow
+
+
+
+
+
+
+ {logs.length > 0 && (
+
+
📋 Test Logs
+
+ {logs.map((log, i) => (
+
{log}
+ ))}
+
+
+ )}
+
+ {results && (
+
+
{results.success ? '✅ Test Passed' : '❌ Test Failed'}
+
+ {/* Steps Progress */}
+
+
+ {results.steps.schemaValidation ? '✅' : '❌'}
+ Schema Validation
+
+
+ {results.steps.dataSeeding ? '✅' : '❌'}
+ Data Seeding
+
+
+ {results.steps.articleGeneration ? '✅' : '❌'}
+ Article Generation
+
+
+ {results.steps.outputValidation ? '✅' : '❌'}
+ Output Validation
+
+
+
+ {/* Metrics */}
+ {results.metrics && Object.keys(results.metrics).length > 0 && (
+
+
📊 Test Metrics
+
+ {results.metrics.siteId && (
+
+ Site ID:
+ {results.metrics.siteId}
+
+ )}
+ {results.metrics.campaignId && (
+
+ Campaign ID:
+ {results.metrics.campaignId}
+
+ )}
+ {results.metrics.articleId && (
+
+ Article ID:
+ {results.metrics.articleId}
+
+ )}
+ {results.metrics.wordCount && (
+
+ Word Count:
+ = 2000 ? 'success' : 'warning'}>
+ {results.metrics.wordCount} words
+
+
+ )}
+ {results.metrics.fragmentsCreated && (
+
+ Fragments Created:
+ {results.metrics.fragmentsCreated}
+
+ )}
+
+
+ {results.metrics.previewUrl && (
+
+ )}
+
+ )}
+
+ {/* Errors */}
+ {results.errors.length > 0 && (
+
+
❌ Errors
+
+ {results.errors.map((error, i) => (
+ - {error}
+ ))}
+
+
+ )}
+
+ {/* Warnings */}
+ {results.warnings.length > 0 && (
+
+
⚠️ Warnings
+
+ {results.warnings.map((warning, i) => (
+ - {warning}
+ ))}
+
+
+ )}
+
+ )}
+
+
+
+ );
+}
diff --git a/frontend/src/components/admin/god/StatsPanel.tsx b/frontend/src/components/admin/god/StatsPanel.tsx
new file mode 100644
index 0000000..f7e3eb7
--- /dev/null
+++ b/frontend/src/components/admin/god/StatsPanel.tsx
@@ -0,0 +1,78 @@
+import { useQuery } from '@tanstack/react-query';
+
+interface StatsData {
+ sites: number;
+ pages: number;
+ posts: number;
+ articles: number;
+}
+
+export function StatsPanel() {
+ const { data, isLoading } = useQuery({
+ queryKey: ['god-mode-stats'],
+ queryFn: async () => {
+ const response = await fetch('/api/god/search', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ collections: ['sites', 'pages', 'posts', 'generated_articles'],
+ limit: 1
+ })
+ });
+ const result = await response.json();
+
+ // Count by collection
+ const stats: StatsData = {
+ sites: 0,
+ pages: 0,
+ posts: 0,
+ articles: 0
+ };
+
+ if (result.results) {
+ result.results.forEach((item: any) => {
+ if (item._collection === 'sites') stats.sites++;
+ else if (item._collection === 'pages') stats.pages++;
+ else if (item._collection === 'posts') stats.posts++;
+ else if (item._collection === 'generated_articles') stats.articles++;
+ });
+ }
+
+ return stats;
+ },
+ refetchInterval: 30000 // Refresh every 30 seconds
+ });
+
+ if (isLoading) {
+ return (
+
+ {[...Array(4)].map((_, i) => (
+
+ ))}
+
+ );
+ }
+
+ const stats = [
+ { label: 'Sites', value: data?.sites || 0, color: 'text-blue-400' },
+ { label: 'Pages', value: data?.pages || 0, color: 'text-green-400' },
+ { label: 'Posts', value: data?.posts || 0, color: 'text-purple-400' },
+ { label: 'Articles', value: data?.articles || 0, color: 'text-yellow-400' }
+ ];
+
+ return (
+
+ {stats.map((stat) => (
+
+
{stat.label}
+
+ {stat.value.toLocaleString()}
+
+
+ ))}
+
+ );
+}
diff --git a/frontend/src/components/admin/god/UnifiedSearchBar.tsx b/frontend/src/components/admin/god/UnifiedSearchBar.tsx
new file mode 100644
index 0000000..75c851e
--- /dev/null
+++ b/frontend/src/components/admin/god/UnifiedSearchBar.tsx
@@ -0,0 +1,75 @@
+import { useState } from 'react';
+import { Search } from 'lucide-react';
+
+interface UnifiedSearchBarProps {
+ onSearch: (results: any[]) => void;
+ onLoading?: (loading: boolean) => void;
+}
+
+export function UnifiedSearchBar({ onSearch, onLoading }: UnifiedSearchBarProps) {
+ const [query, setQuery] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+
+ const handleSearch = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!query.trim()) {
+ // Empty search = show all
+ onSearch([]);
+ return;
+ }
+
+ setIsSearching(true);
+ onLoading?.(true);
+
+ try {
+ const response = await fetch('/api/god/search', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ query: query.trim(),
+ collections: ['sites', 'pages', 'posts', 'generated_articles'],
+ limit: 100
+ })
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ onSearch(result.results || []);
+ } else {
+ console.error('Search failed:', result.error);
+ onSearch([]);
+ }
+ } catch (error) {
+ console.error('Search error:', error);
+ onSearch([]);
+ } finally {
+ setIsSearching(false);
+ onLoading?.(false);
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend/src/pages/admin/god-mode.astro b/frontend/src/pages/admin/god-mode.astro
new file mode 100644
index 0000000..8b87eb8
--- /dev/null
+++ b/frontend/src/pages/admin/god-mode.astro
@@ -0,0 +1,8 @@
+---
+import AdminLayout from '@/layouts/AdminLayout.astro';
+import { GodModeCommandCenter } from '@/components/admin/god/GodModeCommandCenter';
+---
+
+
+
+
diff --git a/frontend/src/pages/api/god/bulk-actions.ts b/frontend/src/pages/api/god/bulk-actions.ts
new file mode 100644
index 0000000..badacfc
--- /dev/null
+++ b/frontend/src/pages/api/god/bulk-actions.ts
@@ -0,0 +1,138 @@
+import type { APIRoute } from 'astro';
+import { getDirectusClient, updateItem, deleteItem } from '@/lib/directus/client';
+
+/**
+ * God Mode Bulk Actions
+ *
+ * Perform bulk operations on multiple items
+ */
+export const POST: APIRoute = async ({ request }) => {
+ try {
+ const { action, collection, ids, options } = await request.json();
+
+ if (!action || !collection || !ids || ids.length === 0) {
+ return new Response(JSON.stringify({
+ error: 'action, collection, and ids are required'
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ const directus = getDirectusClient();
+ const results = {
+ success: 0,
+ failed: 0,
+ errors: [] as any[]
+ };
+
+ switch (action) {
+ case 'publish':
+ for (const id of ids) {
+ try {
+ await directus.request(updateItem(collection, id, {
+ status: 'published',
+ published_at: new Date().toISOString()
+ }));
+ results.success++;
+ } catch (error: any) {
+ results.failed++;
+ results.errors.push({ id, error: error.message });
+ }
+ }
+ break;
+
+ case 'unpublish':
+ case 'draft':
+ for (const id of ids) {
+ try {
+ await directus.request(updateItem(collection, id, {
+ status: 'draft'
+ }));
+ results.success++;
+ } catch (error: any) {
+ results.failed++;
+ results.errors.push({ id, error: error.message });
+ }
+ }
+ break;
+
+ case 'archive':
+ for (const id of ids) {
+ try {
+ await directus.request(updateItem(collection, id, {
+ status: 'archived'
+ }));
+ results.success++;
+ } catch (error: any) {
+ results.failed++;
+ results.errors.push({ id, error: error.message });
+ }
+ }
+ break;
+
+ case 'delete':
+ for (const id of ids) {
+ try {
+ await directus.request(deleteItem(collection, id));
+ results.success++;
+ } catch (error: any) {
+ results.failed++;
+ results.errors.push({ id, error: error.message });
+ }
+ }
+ break;
+
+ case 'update':
+ // Custom update with fields from options
+ if (!options?.fields) {
+ return new Response(JSON.stringify({
+ error: 'options.fields required for update action'
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ for (const id of ids) {
+ try {
+ await directus.request(updateItem(collection, id, options.fields));
+ results.success++;
+ } catch (error: any) {
+ results.failed++;
+ results.errors.push({ id, error: error.message });
+ }
+ }
+ break;
+
+ default:
+ return new Response(JSON.stringify({
+ error: `Unknown action: ${action}`
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ return new Response(JSON.stringify({
+ success: true,
+ action,
+ collection,
+ total: ids.length,
+ results
+ }), {
+ status: 200,
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ } catch (error: any) {
+ console.error('Bulk action error:', error);
+ return new Response(JSON.stringify({
+ success: false,
+ error: error.message
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+};
diff --git a/frontend/src/pages/api/god/search.ts b/frontend/src/pages/api/god/search.ts
new file mode 100644
index 0000000..1a7b36c
--- /dev/null
+++ b/frontend/src/pages/api/god/search.ts
@@ -0,0 +1,86 @@
+import type { APIRoute } from 'astro';
+import { getDirectusClient, readItems } from '@/lib/directus/client';
+
+/**
+ * God Mode Unified Search
+ *
+ * Searches across multiple collections with filters
+ */
+export const POST: APIRoute = async ({ request }) => {
+ try {
+ const { query, collections, filters, limit = 100 } = await request.json();
+
+ if (!collections || collections.length === 0) {
+ return new Response(JSON.stringify({
+ error: 'collections array is required'
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ const directus = getDirectusClient();
+ const results: any[] = [];
+
+ for (const collection of collections) {
+ try {
+ // Build filter
+ const filter: any = {};
+
+ // Add text search if query provided
+ if (query) {
+ filter._or = [
+ { title: { _contains: query } },
+ { name: { _contains: query } },
+ { headline: { _contains: query } },
+ { content: { _contains: query } },
+ { slug: { _contains: query } }
+ ];
+ }
+
+ // Merge additional filters
+ if (filters) {
+ Object.assign(filter, filters);
+ }
+
+ const items = await directus.request(readItems(collection, {
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
+ limit: Math.min(limit, 100),
+ fields: ['*']
+ }));
+
+ // Add collection name to each item
+ const itemsWithCollection = (items as any[]).map(item => ({
+ ...item,
+ _collection: collection
+ }));
+
+ results.push(...itemsWithCollection);
+ } catch (error) {
+ console.error(`Error searching ${collection}:`, error);
+ // Continue with other collections
+ }
+ }
+
+ return new Response(JSON.stringify({
+ success: true,
+ results,
+ total: results.length,
+ query,
+ collections
+ }), {
+ status: 200,
+ headers: { 'Content-Type': 'application/json' }
+ });
+
+ } catch (error: any) {
+ console.error('Search error:', error);
+ return new Response(JSON.stringify({
+ success: false,
+ error: error.message
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+};