diff --git a/CLAUDE.md b/CLAUDE.md
index 498fd933..9c7ba454 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -357,18 +357,22 @@ src/
│ ├── admin/ # Admin-specific components
│ ├── operator/ # Operator terminal components
│ ├── ui/ # shadcn/ui base components
-│ └── shared/ # Cross-cutting components
-├── pages/ # Route pages
+│ └── Layout.tsx # Role-based layout router
+├── pages/ # Route pages (with barrel exports)
│ ├── admin/ # Admin pages
-│ ├── operator/ # Operator pages
-│ └── common/ # Shared pages (auth, etc.)
+│ │ ├── config/ # Config pages (Stages, Users, etc.)
+│ │ └── analytics/ # Analytics pages (OEE, QRM, etc.)
+│ ├── auth/ # Auth pages (Auth, AcceptInvitation)
+│ ├── operator/ # Operator pages (WorkQueue, etc.)
+│ └── common/ # Shared pages (MyPlan, Pricing, Help, etc.)
├── hooks/ # Custom React hooks
├── lib/ # Utility libraries
├── integrations/ # Supabase client
├── i18n/ # Localization
-│ └── locales/ # Translation files
+│ └── locales/ # Translation files (en, nl, de)
├── styles/ # Global styles
│ └── design-system.css
+├── routes.ts # Centralized route definitions
└── theme/ # Theme provider
```
@@ -549,20 +553,67 @@ function JobCard({ job, onEdit, onDelete }: JobCardProps) {
|------|---------|
| `docs/DESIGN_SYSTEM.md` | Design guidelines (READ FIRST for UI work) |
| `src/styles/design-system.css` | CSS tokens and classes |
+| `src/routes.ts` | Centralized route definitions |
| `src/integrations/supabase/client.ts` | Supabase client |
| `src/integrations/supabase/types.ts` | Database types |
| `src/i18n/locales/*/translation.json` | Translations (EN/NL/DE) |
| `src/components/ui/*` | shadcn/ui components |
| `src/theme/ThemeProvider.tsx` | Theme (dark/light/auto) |
+### Page Organization
+
+Pages are organized by role with barrel exports (`index.ts`):
+
+```
+src/pages/
+├── admin/
+│ ├── config/ # Config pages
+│ │ ├── index.ts # Barrel export
+│ │ ├── Stages.tsx
+│ │ ├── Materials.tsx
+│ │ └── Users.tsx # etc.
+│ ├── analytics/ # Analytics pages
+│ │ ├── index.ts # Barrel export
+│ │ └── OEEAnalytics.tsx # etc.
+│ └── Dashboard.tsx # Other admin pages
+├── auth/
+│ ├── index.ts # Barrel export
+│ ├── Auth.tsx
+│ └── AcceptInvitation.tsx
+├── operator/
+│ ├── index.ts # Barrel export
+│ └── WorkQueue.tsx # etc.
+└── common/
+ ├── index.ts # Barrel export
+ ├── MyPlan.tsx
+ ├── Pricing.tsx
+ └── Help.tsx # etc.
+```
+
### Adding New Files
Follow existing patterns:
-- **Page**: `src/pages/admin/NewFeaturePage.tsx`
+- **Admin page**: `src/pages/admin/NewFeature.tsx`
+- **Config page**: `src/pages/admin/config/NewConfig.tsx` (add to barrel export)
+- **Common page**: `src/pages/common/NewPage.tsx` (add to barrel export)
- **Component**: `src/components/admin/NewFeatureComponent.tsx`
- **Hook**: `src/hooks/useNewFeature.ts`
- **Type**: Add to `src/integrations/supabase/types.ts`
+### Import Pattern
+
+Use barrel exports for cleaner imports:
+
+```tsx
+// Good - use barrel exports
+import { Auth, AcceptInvitation } from "./pages/auth";
+import { ApiKeys, Materials, Users } from "./pages/admin/config";
+import { MyPlan, Pricing, Help } from "./pages/common";
+
+// Avoid - direct file imports (unless not in barrel)
+import Auth from "./pages/auth/Auth";
+```
+
---
## Common Patterns
diff --git a/src/App.tsx b/src/App.tsx
index a82d0484..9b7c057f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,26 +7,18 @@ import { AuthProvider, useAuth } from "./contexts/AuthContext";
import { ThemeProvider } from "./theme/ThemeProvider";
import { NotificationToastProvider } from "./components/NotificationToastProvider";
import { McpActivityToasts } from "./components/admin/McpActivityToasts";
-import Auth from "./pages/Auth";
-import AcceptInvitation from "./pages/AcceptInvitation";
-import WorkQueue from "./pages/operator/WorkQueue";
-import MyActivity from "./pages/operator/MyActivity";
-import MyIssues from "./pages/operator/MyIssues";
-import OperatorTerminal from "./pages/operator/OperatorTerminal";
+// Auth pages
+import { Auth, AcceptInvitation } from "./pages/auth";
+
+// Operator pages
+import { WorkQueue, MyActivity, MyIssues, OperatorTerminal, OperatorView } from "./pages/operator";
+
+// Admin pages
import Dashboard from "./pages/admin/Dashboard";
import IssueQueue from "./pages/admin/IssueQueue";
-import ConfigStages from "./pages/admin/ConfigStages";
import FactoryCalendar from "./pages/admin/FactoryCalendar";
-import ConfigMaterials from "./pages/admin/ConfigMaterials";
-import ConfigResources from "./pages/admin/ConfigResources";
-import ConfigUsers from "./pages/admin/ConfigUsers";
-import ConfigScrapReasons from "./pages/admin/ConfigScrapReasons";
import OrganizationSettings from "./pages/admin/OrganizationSettings";
import Assignments from "./pages/admin/Assignments";
-import ConfigApiKeys from "./pages/admin/ConfigApiKeys";
-import ConfigMcpKeys from "./pages/admin/ConfigMcpKeys";
-import ConfigWebhooks from "./pages/admin/ConfigWebhooks";
-import ConfigMqttPublishers from "./pages/admin/ConfigMqttPublishers";
import McpServerSettings from "./pages/admin/McpServerSettings";
import DataExport from "./pages/admin/DataExport";
import DataImport from "./pages/admin/DataImport";
@@ -41,19 +33,43 @@ import IntegrationsMarketplace from "./pages/admin/IntegrationsMarketplace";
import Shipments from "./pages/admin/Shipments";
import StepsTemplatesView from "./pages/admin/StepsTemplatesView";
import AnalyticsDashboard from "./pages/admin/Analytics";
-import OEEAnalytics from "./pages/admin/analytics/OEEAnalytics";
-import ReliabilityAnalytics from "./pages/admin/analytics/ReliabilityAnalytics";
-import QRMAnalytics from "./pages/admin/analytics/QRMAnalytics";
-import QRMDashboard from "./pages/admin/analytics/QRMDashboard";
-import JobsAnalytics from "./pages/admin/analytics/JobsAnalytics";
-import QualityAnalytics from "./pages/admin/analytics/QualityAnalytics";
-import ApiDocs from "./pages/ApiDocs";
-import Pricing from "./pages/Pricing";
-import { MyPlan } from "./pages/MyPlan";
-import Help from "./pages/common/Help";
-import About from "./pages/About";
-import PrivacyPolicy from "./pages/common/PrivacyPolicy";
-import TermsOfService from "./pages/common/TermsOfService";
+
+// Admin config pages
+import {
+ ApiKeys as ConfigApiKeys,
+ Materials as ConfigMaterials,
+ McpKeys as ConfigMcpKeys,
+ MqttPublishers as ConfigMqttPublishers,
+ Resources as ConfigResources,
+ ScrapReasons as ConfigScrapReasons,
+ Stages as ConfigStages,
+ Users as ConfigUsers,
+ Webhooks as ConfigWebhooks,
+} from "./pages/admin/config";
+
+// Admin analytics pages
+import {
+ OEEAnalytics,
+ ReliabilityAnalytics,
+ QRMAnalytics,
+ QRMDashboard,
+ JobsAnalytics,
+ QualityAnalytics,
+} from "./pages/admin/analytics";
+
+// Common pages
+import {
+ ApiDocs,
+ Pricing,
+ MyPlan,
+ Help,
+ About,
+ PrivacyPolicy,
+ TermsOfService,
+ SubscriptionBlocked,
+} from "./pages/common";
+
+// Other pages
import NotFound from "./pages/NotFound";
import { OnboardingWizard } from "./components/onboarding";
import Layout from "./components/Layout";
diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx
deleted file mode 100644
index 1d0e3831..00000000
--- a/src/layouts/AdminLayout.tsx
+++ /dev/null
@@ -1,626 +0,0 @@
-import { useAuth } from "@/contexts/AuthContext";
-import { Button } from "@/components/ui/button";
-import { ScrollArea } from "@/components/ui/scroll-area";
-import { Separator } from "@/components/ui/separator";
-import { Badge } from "@/components/ui/badge";
-import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
-import {
- LogOut,
- LayoutDashboard,
- ListChecks,
- Settings,
- AlertCircle,
- UserCheck,
- BookOpen,
- Briefcase,
- Package,
- Plug,
- ChevronLeft,
- ChevronRight,
- ChevronDown,
- Menu,
- Store,
- Clock,
- Layers,
- Database,
- Wrench,
- FileText,
- DollarSign,
- Users,
- Key,
- Webhook,
- FileDown,
- HelpCircle,
- CreditCard,
- Code,
- Eye,
- ListTodo,
- Activity,
- Flag,
- Info,
- Truck,
- Trash2,
- Radio,
-} from "lucide-react";
-import { Link, useLocation } from "react-router-dom";
-import { useState } from "react";
-import { cn } from "@/lib/utils";
-import { usePendingIssuesCount } from "@/hooks/usePendingIssuesCount";
-import { LanguageSwitcher } from "@/components/LanguageSwitcher";
-import { ROUTES } from "@/routes";
-import { useTranslation } from "react-i18next";
-import AnimatedBackground from "@/components/AnimatedBackground";
-import { BrandLogo } from "@/components/ui/brand-logo";
-import SessionTrackingBar from "@/components/SessionTrackingBar";
-
-interface AdminLayoutProps {
- children: React.ReactNode;
-}
-
-export default function AdminLayout({ children }: AdminLayoutProps) {
- const { t } = useTranslation();
- const { profile, tenant, signOut } = useAuth();
- const location = useLocation();
- const [collapsed, setCollapsed] = useState(false);
- const [mobileOpen, setMobileOpen] = useState(false);
- const [operatorViewsOpen, setOperatorViewsOpen] = useState(false);
- const [configOpen, setConfigOpen] = useState(false);
- const [integrationsOpen, setIntegrationsOpen] = useState(false);
- const [accountOpen, setAccountOpen] = useState(false);
- const { count: pendingIssuesCount } = usePendingIssuesCount();
-
- const isActive = (path: string) => location.pathname === path;
- const isActiveGroup = (...paths: string[]) => paths.some(path => location.pathname.startsWith(path));
-
- // Main navigation - Daily operations (always visible)
- const mainNavItems = [
- {
- path: ROUTES.ADMIN.DASHBOARD,
- label: t("navigation.dashboard"),
- icon: LayoutDashboard,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.JOBS,
- label: t("navigation.jobs"),
- icon: Briefcase,
- activePaths: [ROUTES.ADMIN.JOBS],
- },
- {
- path: ROUTES.ADMIN.PARTS,
- label: t("navigation.parts"),
- icon: Package,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.OPERATIONS,
- label: t("navigation.operations"),
- icon: Layers,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.ASSIGNMENTS,
- label: t("navigation.assignments"),
- icon: UserCheck,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.ISSUES,
- label: t("navigation.issues"),
- icon: AlertCircle,
- exact: true,
- badge: pendingIssuesCount,
- },
- {
- path: ROUTES.ADMIN.ACTIVITY,
- label: t("navigation.activityMonitor"),
- icon: Clock,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.SHIPPING,
- label: t("navigation.shipping"),
- icon: Truck,
- exact: true,
- },
- ];
-
- // Operator views - Admin can see what operators see
- const operatorViewItems = [
- {
- path: ROUTES.OPERATOR.WORK_QUEUE,
- label: t("navigation.workQueue"),
- icon: ListTodo,
- exact: true,
- },
- {
- path: ROUTES.OPERATOR.VIEW,
- label: t("navigation.operatorView"),
- icon: Eye,
- exact: true,
- },
- {
- path: ROUTES.OPERATOR.MY_ACTIVITY,
- label: t("navigation.myActivity"),
- icon: Activity,
- exact: true,
- },
- {
- path: ROUTES.OPERATOR.MY_ISSUES,
- label: t("navigation.myIssues"),
- icon: Flag,
- exact: true,
- },
- ];
-
- // Configuration - Setup tasks (rarely changed)
- const configNavItems = [
- {
- path: ROUTES.ADMIN.CONFIG.STAGES,
- label: t("navigation.stages"),
- icon: Database,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.MATERIALS,
- label: t("navigation.materials"),
- icon: Wrench,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.RESOURCES,
- label: t("navigation.resources"),
- icon: Wrench,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.USERS,
- label: t("navigation.users"),
- icon: Users,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.STEPS_TEMPLATES,
- label: t("navigation.templates"),
- icon: FileText,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.SCRAP_REASONS,
- label: t("navigation.scrapReasons", "Scrap Reasons"),
- icon: Trash2,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.API_KEYS,
- label: t("navigation.apiKeys"),
- icon: Key,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.WEBHOOKS,
- label: t("navigation.webhooks"),
- icon: Webhook,
- exact: true,
- },
- ];
-
- // Integrations - Developer tools
- const integrationsNavItems = [
- {
- path: ROUTES.ADMIN.INTEGRATIONS,
- label: t("navigation.integrations"),
- icon: Store,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.CONFIG.MQTT_PUBLISHERS,
- label: t("navigation.mqttPublishers"),
- icon: Radio,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.DATA_EXPORT,
- label: t("navigation.dataExport"),
- icon: FileDown,
- exact: true,
- },
- {
- path: ROUTES.COMMON.API_DOCS,
- label: t("apiDocumentation"),
- icon: FileText,
- exact: true,
- },
- ];
-
- // Account & Support
- const accountNavItems = [
- {
- path: ROUTES.COMMON.MY_PLAN,
- label: t("myPlan"),
- icon: CreditCard,
- exact: true,
- },
- {
- path: ROUTES.COMMON.PRICING,
- label: t("navigation.pricing"),
- icon: DollarSign,
- exact: true,
- },
- {
- path: ROUTES.ADMIN.SETTINGS,
- label: t("navigation.settings"),
- icon: Settings,
- exact: true,
- },
- {
- path: ROUTES.COMMON.HELP,
- label: t("help"),
- icon: HelpCircle,
- exact: true,
- },
- {
- path: ROUTES.COMMON.ABOUT,
- label: t("about"),
- icon: Info,
- exact: true,
- },
- ];
-
- const SidebarContent = () => (
-
- {/* Logo/Brand */}
-
-
-
-
- {/* Navigation */}
-
- {/* Main Navigation - Daily Operations */}
-
- {mainNavItems.map((item) => {
- const isItemActive = item.exact
- ? isActive(item.path)
- : item.activePaths
- ? isActiveGroup(...item.activePaths)
- : location.pathname.startsWith(item.path);
-
- return (
-
setMobileOpen(false)}>
-
-
- {!collapsed && (
- <>
- {item.label}
- {item.badge !== undefined && item.badge > 0 && (
-
- {item.badge}
-
- )}
- >
- )}
- {collapsed && item.badge !== undefined && item.badge > 0 && (
-
- )}
-
-
- );
- })}
-
-
- {!collapsed && }
-
- {/* Operator Views Section - Collapsible */}
- {!collapsed && (
-
-
-
-
- {t("navigation.operatorViews")}
-
-
-
-
- {operatorViewItems.map((item) => {
- const isItemActive = item.exact
- ? isActive(item.path)
- : location.pathname.startsWith(item.path);
-
- return (
- setMobileOpen(false)}>
-
-
- {item.label}
-
-
- );
- })}
-
-
- )}
-
- {!collapsed && }
-
- {/* Configuration Section - Collapsible */}
- {!collapsed && (
-
-
-
-
- {t("navigation.configuration")}
-
-
-
-
- {configNavItems.map((item) => {
- const isItemActive = item.exact
- ? isActive(item.path)
- : location.pathname.startsWith(item.path);
-
- return (
- setMobileOpen(false)}>
-
-
- {item.label}
-
-
- );
- })}
-
-
- )}
-
- {!collapsed && }
-
- {/* Integrations Section - Collapsible */}
- {!collapsed && (
-
-
-
-
- {t("navigation.integrations")}
-
-
-
-
- {integrationsNavItems.map((item) => {
- const isItemActive = item.exact
- ? isActive(item.path)
- : location.pathname.startsWith(item.path);
-
- return (
- setMobileOpen(false)}>
-
-
- {item.label}
-
-
- );
- })}
-
-
- )}
-
- {!collapsed && }
-
- {/* Account & Support Section - Collapsible */}
- {!collapsed && (
-
-
-
-
- {t("navigation.accountAndSupport")}
-
-
-
-
- {accountNavItems.map((item) => {
- const isItemActive = item.exact
- ? isActive(item.path)
- : location.pathname.startsWith(item.path);
-
- return (
- setMobileOpen(false)}>
-
-
- {item.label}
-
-
- );
- })}
-
-
- )}
-
-
- {/* User Profile & Sign Out */}
-
- {!collapsed && tenant && (
-
-
Tenant
-
{tenant.company_name || tenant.name}
-
-
- {tenant.plan}
-
-
- {tenant.status}
-
-
-
- )}
- {!collapsed && profile && (
-
-
{profile.full_name}
-
- {profile.role}
-
-
- )}
-
-
-
-
-
- {!collapsed && t("auth.signOut")}
-
-
-
- {/* Collapse Toggle (Desktop) */}
- {!mobileOpen && (
-
setCollapsed(!collapsed)}
- className="absolute -right-3 top-20 hidden h-6 w-6 rounded-full border bg-background p-0 lg:flex"
- >
- {collapsed ? (
-
- ) : (
-
- )}
-
- )}
-
- );
-
- return (
- <>
-
-
- {/* Mobile Menu Button */}
-
setMobileOpen(!mobileOpen)}
- >
-
-
-
- {/* Mobile Sidebar Overlay */}
- {mobileOpen && (
-
setMobileOpen(false)}
- />
- )}
-
- {/* Sidebar - Mobile */}
-
-
- {/* Sidebar - Desktop */}
-
-
- {/* Main Content */}
-
-
- {children}
-
-
-
- {/* Global Session Tracking Bar */}
-
-
- >
- );
-}
diff --git a/src/layouts/Layout.tsx b/src/layouts/Layout.tsx
deleted file mode 100644
index 55645d9b..00000000
--- a/src/layouts/Layout.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-// Role-based layout routing
-import { useAuth } from "@/contexts/AuthContext";
-import AdminLayout from "@/layouts/AdminLayout";
-import { OperatorLayout } from "@/layouts/OperatorLayout";
-
-interface LayoutProps {
- children: React.ReactNode;
-}
-
-// SECURITY NOTE: Layout selection based on role is for UI convenience only.
-// Server-side RLS policies enforce actual data access permissions.
-export default function Layout({ children }: LayoutProps) {
- const { profile, loading } = useAuth();
-
- // Show nothing while loading to prevent layout flicker
- if (loading || !profile) {
- return null;
- }
-
- // UI-only: Route to appropriate layout based on user role
- // This provides better UX but provides ZERO security
- if (profile.role === "admin") {
- return
{children} ;
- }
-
- // Default to operator layout for operator role
- return
{children} ;
-}
diff --git a/src/layouts/OperatorLayout.tsx b/src/layouts/OperatorLayout.tsx
deleted file mode 100644
index 6774cc03..00000000
--- a/src/layouts/OperatorLayout.tsx
+++ /dev/null
@@ -1,265 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { useTranslation } from "react-i18next";
-import { useLocation, useNavigate } from "react-router-dom";
-import {
- List,
- Clock,
- AlertTriangle,
- LogOut,
- Sun,
- Moon,
- HelpCircle,
- Building2,
- Gauge,
- ChevronDown,
-} from "lucide-react";
-import { Button } from "@/components/ui/button";
-import { Avatar, AvatarFallback } from "@/components/ui/avatar";
-import { Badge } from "@/components/ui/badge";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { cn } from "@/lib/utils";
-import { useAuth } from "@/contexts/AuthContext";
-import { useThemeMode } from "@/theme/ThemeProvider";
-import CurrentlyTimingWidget from "@/components/operator/CurrentlyTimingWidget";
-import { LanguageSwitcher } from "@/components/LanguageSwitcher";
-import { AppTour } from "@/components/onboarding";
-import { ROUTES } from "@/routes";
-import { BrandLogo } from "@/components/ui/brand-logo";
-import SessionTrackingBar from "@/components/SessionTrackingBar";
-
-interface OperatorLayoutProps {
- children: React.ReactNode;
-}
-
-export const OperatorLayout: React.FC
= ({ children }) => {
- const { t } = useTranslation();
- const { profile, tenant, signOut } = useAuth();
- const { mode, toggleTheme } = useThemeMode();
- const location = useLocation();
- const navigate = useNavigate();
-
- const navItems = [
- { path: ROUTES.OPERATOR.WORK_QUEUE, label: t("navigation.workQueue"), icon: List },
- { path: ROUTES.OPERATOR.VIEW, label: t("navigation.operatorView"), icon: Gauge },
- { path: ROUTES.OPERATOR.MY_ACTIVITY, label: t("navigation.myActivity"), icon: Clock },
- { path: ROUTES.OPERATOR.MY_ISSUES, label: t("navigation.myIssues"), icon: AlertTriangle },
- ];
-
- const isActive = (path: string) => location.pathname === path;
-
- return (
-
- {/* Top App Bar - Glassmorphism Header */}
-
-
- {/* Desktop Navigation Tabs - Hidden on mobile */}
-
-
- {navItems.map((item) => {
- const Icon = item.icon;
- return (
- navigate(item.path)}
- className={cn(
- "flex items-center gap-2 px-4 py-2.5 rounded-lg",
- "text-sm font-medium transition-all duration-200",
- isActive(item.path)
- ? "bg-primary/15 text-primary"
- : "text-muted-foreground hover:text-foreground hover:bg-white/5"
- )}
- >
-
- {item.label}
-
- );
- })}
-
-
-
- {/* Currently Timing Widget - Sticky below header */}
-
-
- {/* Main Content */}
-
- {children}
-
-
- {/* Bottom Navigation - Mobile Only */}
-
-
- {navItems.map((item) => {
- const Icon = item.icon;
- const active = isActive(item.path);
- return (
- navigate(item.path)}
- className={cn(
- "flex flex-col items-center justify-center gap-1",
- "min-w-[64px] py-2 px-3",
- "transition-colors duration-200",
- active ? "text-primary" : "text-muted-foreground"
- )}
- >
-
- {item.label}
-
- );
- })}
-
-
-
- {/* Global Session Tracking Bar */}
-
-
- {/* Onboarding Tour - only show if not completed */}
- {profile && !(profile as any).tour_completed &&
}
-
- );
-};
diff --git a/src/pages/About.tsx b/src/pages/About.tsx
deleted file mode 100644
index f4dea377..00000000
--- a/src/pages/About.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Card, CardContent } from "@/components/ui/card";
-import { Separator } from "@/components/ui/separator";
-
-export default function About() {
- return (
-
-
About
-
-
-
- Eryxon MES
-
- Manufacturing execution system for metals fabrication. Track jobs, parts, and operations through production.
-
-
- Made by{" "}
-
- Sheet Metal Connect
-
-
-
-
-
-
-
-
-
-
- Privacy Policy · Terms of Service
-
-
-
- );
-}
diff --git a/src/pages/ApiDocs.tsx b/src/pages/ApiDocs.tsx
deleted file mode 100644
index bb75ca3b..00000000
--- a/src/pages/ApiDocs.tsx
+++ /dev/null
@@ -1,856 +0,0 @@
-import { useEffect, useState } from "react";
-import { Link } from "react-router-dom";
-import SwaggerUI from "swagger-ui-react";
-import "swagger-ui-react/swagger-ui.css";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Separator } from "@/components/ui/separator";
-import { Badge } from "@/components/ui/badge";
-import {
- BookOpen,
- Code2,
- KeyRound,
- Rocket,
- CheckCircle2,
- Copy,
- ExternalLink,
- Terminal,
- Zap,
- FileJson,
- PlayCircle,
- Download,
- FileCode,
- Link as LinkIcon,
- Package,
- FileUp,
- HelpCircle
-} from "lucide-react";
-import { useToast } from "@/hooks/use-toast";
-
-export default function ApiDocs() {
- const { toast } = useToast();
- const [apiKey, setApiKey] = useState("");
- const baseUrl = import.meta.env.VITE_SUPABASE_URL?.replace('/supabase', '') || "https://vatgianzotsurljznsry.supabase.co";
- const apiBaseUrl = `${baseUrl}/functions/v1`;
-
- const copyToClipboard = (text: string, label: string) => {
- navigator.clipboard.writeText(text);
- toast({
- title: "Copied!",
- description: `${label} copied to clipboard`,
- });
- };
-
- const downloadSpec = async (format: 'json' | 'yaml') => {
- try {
- const response = await fetch('/openapi.json');
- const spec = await response.json();
-
- let content: string;
- let filename: string;
- let mimeType: string;
-
- if (format === 'json') {
- content = JSON.stringify(spec, null, 2);
- filename = 'eryxon-flow-openapi.json';
- mimeType = 'application/json';
- } else {
- // Convert JSON to YAML (simple conversion)
- content = jsonToYaml(spec);
- filename = 'eryxon-flow-openapi.yaml';
- mimeType = 'application/x-yaml';
- }
-
- const blob = new Blob([content], { type: mimeType });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
-
- toast({
- title: "Downloaded!",
- description: `OpenAPI spec downloaded as ${filename}`,
- });
- } catch (error) {
- toast({
- title: "Error",
- description: "Failed to download spec",
- variant: "destructive",
- });
- }
- };
-
- // Simple JSON to YAML converter (handles common cases)
- const jsonToYaml = (obj: any, indent = 0): string => {
- const spaces = ' '.repeat(indent);
- let result = '';
-
- if (Array.isArray(obj)) {
- for (const item of obj) {
- if (typeof item === 'object' && item !== null) {
- result += `${spaces}-\n${jsonToYaml(item, indent + 1).replace(/^ /, '')}`;
- } else {
- result += `${spaces}- ${formatYamlValue(item)}\n`;
- }
- }
- } else if (typeof obj === 'object' && obj !== null) {
- for (const [key, value] of Object.entries(obj)) {
- if (typeof value === 'object' && value !== null) {
- if (Array.isArray(value) && value.length === 0) {
- result += `${spaces}${key}: []\n`;
- } else if (typeof value === 'object' && Object.keys(value).length === 0) {
- result += `${spaces}${key}: {}\n`;
- } else {
- result += `${spaces}${key}:\n${jsonToYaml(value, indent + 1)}`;
- }
- } else {
- result += `${spaces}${key}: ${formatYamlValue(value)}\n`;
- }
- }
- }
-
- return result;
- };
-
- const formatYamlValue = (value: any): string => {
- if (value === null) return 'null';
- if (value === undefined) return '';
- if (typeof value === 'boolean') return value ? 'true' : 'false';
- if (typeof value === 'number') return String(value);
- if (typeof value === 'string') {
- if (value.includes('\n') || value.includes(':') || value.includes('#') ||
- value.includes('"') || value.includes("'") || value.startsWith(' ') ||
- value.endsWith(' ') || /^[\d.]+$/.test(value) || value === '') {
- return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
- }
- return value;
- }
- return String(value);
- };
-
- const openInSwaggerEditor = () => {
- const specUrl = encodeURIComponent(`${window.location.origin}/openapi.json`);
- window.open(`https://editor.swagger.io/?url=${specUrl}`, '_blank');
- };
-
- const codeExamples = {
- curl: `# Test your API connection
-curl ${apiBaseUrl}/api-stages \\
- -H "Authorization: Bearer YOUR_API_KEY" \\
- -H "Content-Type: application/json"
-
-# Create a new job
-curl -X POST ${apiBaseUrl}/api-jobs \\
- -H "Authorization: Bearer YOUR_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "job_number": "JOB-2024-001",
- "customer": "Acme Corp",
- "due_date": "2024-12-31",
- "parts": [{
- "part_number": "BRACKET-001",
- "material": "Steel 304",
- "quantity": 10
- }]
- }'`,
-
- javascript: `// Using fetch API
-const apiKey = 'YOUR_API_KEY';
-const baseUrl = '${apiBaseUrl}';
-
-// Fetch stages
-async function getStages() {
- const response = await fetch(\`\${baseUrl}/api-stages\`, {
- headers: {
- 'Authorization': \`Bearer \${apiKey}\`,
- 'Content-Type': 'application/json'
- }
- });
-
- const data = await response.json();
- return data;
-}
-
-// Create a job
-async function createJob(jobData) {
- const response = await fetch(\`\${baseUrl}/api-jobs\`, {
- method: 'POST',
- headers: {
- 'Authorization': \`Bearer \${apiKey}\`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(jobData)
- });
-
- const data = await response.json();
- return data;
-}
-
-// Example usage
-const newJob = {
- job_number: 'JOB-2024-001',
- customer: 'Acme Corp',
- due_date: '2024-12-31',
- parts: [{
- part_number: 'BRACKET-001',
- material: 'Steel 304',
- quantity: 10
- }]
-};
-
-createJob(newJob).then(console.log);`,
-
- python: `import requests
-import json
-
-API_KEY = 'YOUR_API_KEY'
-BASE_URL = '${apiBaseUrl}'
-
-headers = {
- 'Authorization': f'Bearer {API_KEY}',
- 'Content-Type': 'application/json'
-}
-
-# Test connection
-response = requests.get(f'{BASE_URL}/api-stages', headers=headers)
-print(response.json())
-
-# Create a job
-job_data = {
- 'job_number': 'JOB-2024-001',
- 'customer': 'Acme Corp',
- 'due_date': '2024-12-31',
- 'parts': [{
- 'part_number': 'BRACKET-001',
- 'material': 'Steel 304',
- 'quantity': 10
- }]
-}
-
-response = requests.post(
- f'{BASE_URL}/api-jobs',
- headers=headers,
- json=job_data
-)
-
-print(response.json())`,
-
- nodejs: `// Using axios (npm install axios)
-const axios = require('axios');
-
-const apiClient = axios.create({
- baseURL: '${apiBaseUrl}',
- headers: {
- 'Authorization': 'Bearer YOUR_API_KEY',
- 'Content-Type': 'application/json'
- }
-});
-
-// Get stages
-apiClient.get('/api-stages')
- .then(response => console.log(response.data))
- .catch(error => console.error(error));
-
-// Create a job
-const jobData = {
- job_number: 'JOB-2024-001',
- customer: 'Acme Corp',
- due_date: '2024-12-31',
- parts: [{
- part_number: 'BRACKET-001',
- material: 'Steel 304',
- quantity: 10
- }]
-};
-
-apiClient.post('/api-jobs', jobData)
- .then(response => console.log(response.data))
- .catch(error => console.error(error));`
- };
-
- // Custom Swagger UI plugin to inject API key
- const ApiKeyPlugin = () => {
- return {
- wrapComponents: {
- authorizeBtn: (Original: any) => (props: any) => {
- return null; // Hide default authorize button
- }
- }
- };
- };
-
- return (
- <>
-
- {/* Hero Section */}
-
-
-
-
-
-
- Eryxon Flow API
-
-
- Production workflow management API for sheet metal manufacturing.
- Build integrations, automate workflows, and manage your production data programmatically.
-
-
-
- v1.0.0
-
-
-
-
-
-
-
-
-
REST API
-
JSON-based RESTful API
-
-
-
-
-
-
API Key Auth
-
Secure bearer token authentication
-
-
-
-
-
-
Interactive Docs
-
Test endpoints directly in browser
-
-
-
-
-
-
- {/* Download & Tools Section */}
-
-
-
-
- OpenAPI Specification
-
-
- Download the spec to use with Swagger, Postman, or generate client SDKs
-
-
-
-
- {/* Download JSON */}
-
downloadSpec('json')}
- >
-
-
-
Download JSON
-
OpenAPI 3.0 spec
-
-
-
- {/* Download YAML */}
-
downloadSpec('yaml')}
- >
-
-
-
Download YAML
-
Human-readable format
-
-
-
- {/* Open in Swagger Editor */}
-
-
-
-
Swagger Editor
-
Edit & validate online
-
-
-
- {/* Copy Spec URL */}
-
copyToClipboard(`${window.location.origin}/openapi.json`, "Spec URL")}
- >
-
-
-
Copy Spec URL
-
Direct link to JSON
-
-
-
-
- {/* Postman Import Instructions */}
-
-
-
-
-
Import to Postman
-
- Open Postman → Import → Link → Paste: {window.location.origin}/openapi.json
-
-
copyToClipboard(`${window.location.origin}/openapi.json`, "Postman import URL")}
- >
-
- Copy URL
-
-
-
-
-
- {/* Related Resources */}
-
-
-
-
- CSV Import Wizard
-
-
-
-
-
- Help & Guides
-
-
-
-
-
-
-
-
-
-
- Quick Start
-
-
-
- Code Examples
-
-
-
- Try It Out
-
-
-
- Full Reference
-
-
-
- {/* Quick Start Tab */}
-
-
-
-
-
- Getting Started in 3 Steps
-
-
- Start making API calls in minutes with this beginner-friendly guide
-
-
-
- {/* Step 1 */}
-
-
-
- 1
-
-
Generate an API Key
-
-
-
- Navigate to Admin > API Keys in the web interface and generate a new API key.
- Your key will start with ery_live_ or ery_test_
-
-
-
- Keep your API key secure!
-
- Your API key is shown only once. Store it securely and never commit it to version control.
-
-
-
window.location.href = '/admin/config/api-keys'}
- className="mt-2"
- >
-
- Go to API Keys
-
-
-
-
-
-
-
- {/* Step 2 */}
-
-
-
- 2
-
-
Test Your Connection
-
-
-
- Make your first API call to verify your key works. We recommend starting with the read-only /api-stages endpoint.
-
-
-
- Base URL:
- copyToClipboard(apiBaseUrl, "Base URL")}
- >
-
-
-
-
- {apiBaseUrl}
-
-
-
-
- cURL Example:
- copyToClipboard(
- `curl ${apiBaseUrl}/api-stages -H "Authorization: Bearer YOUR_API_KEY"`,
- "cURL command"
- )}
- >
-
-
-
-
- {`curl ${apiBaseUrl}/api-stages \\
- -H "Authorization: Bearer YOUR_API_KEY"`}
-
-
-
-
- Expected Response
-
-
- {`{
- "success": true,
- "data": {
- "stages": [...]
- }
-}`}
-
-
-
-
-
-
-
-
- {/* Step 3 */}
-
-
-
- 3
-
-
Start Building
-
-
-
- Explore the API Reference below to see all available endpoints and try them interactively.
-
-
-
- Common Use Cases
-
- Create jobs from ERP systems
- Track production progress
- Update task completion
- Query job status and metrics
-
-
-
- Best Practices
-
- Use pagination for large datasets
- Implement exponential backoff
- Handle rate limit headers
- Validate responses
-
-
-
-
-
-
-
-
-
- {/* Code Examples Tab */}
-
-
-
-
-
- Code Examples
-
-
- Ready-to-use code snippets in multiple programming languages
-
-
-
-
-
- cURL
- JavaScript
- Node.js
- Python
-
-
- {Object.entries(codeExamples).map(([lang, code]) => (
-
-
-
copyToClipboard(code, `${lang} code`)}
- >
-
-
-
- {code}
-
-
-
- ))}
-
-
-
-
- {/* Authentication Card */}
-
-
-
-
- Authentication
-
-
-
-
- All API requests require authentication using an API key in the Authorization header:
-
-
-
- Header Format:
- copyToClipboard("Authorization: Bearer YOUR_API_KEY", "Header")}
- >
-
-
-
-
- Authorization: Bearer ery_live_your_api_key_here
-
-
-
-
- Rate Limiting
-
- API requests are rate-limited. Check the X-RateLimit-* headers in responses
- for current limits and usage.
-
-
-
-
-
-
- {/* Try It Out Tab - Interactive Testing */}
-
-
-
-
-
- Interactive API Testing
-
-
- Test API endpoints directly in your browser. No additional tools required!
-
-
-
-
-
- How to Test
-
-
- Click the "Authorize" button below (green lock icon)
- Enter your API key: ery_live_xxxxx
- Expand any endpoint and click "Try it out"
- Fill in parameters and click "Execute"
-
-
-
-
- {/* Quick Test Endpoints */}
-
-
-
- GET
- /api-stages
-
- Best endpoint to test your API key. Returns all production stages.
-
-
-
- GET
- /api-jobs
-
- List all jobs with filtering. Test pagination with limit/offset.
-
-
-
- POST
- /api-jobs
-
- Create a new job. Includes example request body.
-
-
-
- PUT
- /api-jobs/sync
-
- ERP sync endpoint. Upsert by external_id.
-
-
-
-
-
-
-
-
-
-
-
-
- {/* API Reference Tab - Full Spec */}
-
-
-
-
-
- Complete API Reference
-
-
- Full OpenAPI specification with all endpoints, schemas, and examples.
-
-
-
-
- downloadSpec('json')}
- >
-
- Download JSON
-
- downloadSpec('yaml')}
- >
-
- Download YAML
-
-
-
- Open in Swagger Editor
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/pages/MyPlan.tsx b/src/pages/MyPlan.tsx
deleted file mode 100644
index 59f721dd..00000000
--- a/src/pages/MyPlan.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from 'react';
-import { Card, CardContent } from '@/components/ui/card';
-import { Button } from '@/components/ui/button';
-import { Alert, AlertDescription } from '@/components/ui/alert';
-import {
- Info,
- Mail,
- Clock,
- Loader2,
-} from 'lucide-react';
-import { useSubscription } from '../hooks/useSubscription';
-import { useTranslation } from 'react-i18next';
-
-export const MyPlan: React.FC = () => {
- const { t } = useTranslation();
- const { loading, error } = useSubscription();
-
- const handleContactUs = () => {
- window.location.href = `mailto:office@sheetmetalconnect.com?subject=${encodeURIComponent('Subscription Inquiry')}`;
- };
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- if (error) {
- return (
-
- {error}
-
- );
- }
-
- return (
-
- {/* Header */}
-
-
{t('myPlan.title')}
-
{t('myPlan.comingSoonSubtitle')}
-
-
- {/* Coming Soon */}
-
-
-
- {t('myPlan.comingSoonTitle')}
-
- {t('myPlan.comingSoonDescription')}
-
-
-
- {t('myPlan.contactUs')}
-
-
-
-
- {/* Info Alert */}
-
-
-
- {t('myPlan.currentlyFree')}
-
- {t('myPlan.currentlyFreeDescription')}
-
-
-
-
- );
-};
diff --git a/src/pages/Pricing.tsx b/src/pages/Pricing.tsx
deleted file mode 100644
index f05d1f14..00000000
--- a/src/pages/Pricing.tsx
+++ /dev/null
@@ -1,199 +0,0 @@
-import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { Badge } from "@/components/ui/badge";
-import { Check, X, Mail, Users, Zap, Star, ArrowRight } from "lucide-react";
-import { useSubscription } from "@/hooks/useSubscription";
-import { Link } from "react-router-dom";
-import { useTranslation } from "react-i18next";
-
-// Main hosted tiers
-const hostedTiers = [
- {
- id: "free",
- nameKey: "pricing.free.name",
- descriptionKey: "pricing.free.description",
- priceKey: "pricing.free.price",
- featuresKey: "pricing.free.features",
- ctaKey: "pricing.currentPlan",
- icon: Users,
- popular: false,
- gradient: false,
- },
- {
- id: "pro",
- nameKey: "pricing.pro.name",
- descriptionKey: "pricing.pro.description",
- priceKey: "pricing.pro.price",
- featuresKey: "pricing.pro.features",
- ctaKey: "pricing.upgrade",
- icon: Zap,
- popular: true,
- gradient: false,
- },
- {
- id: "premium",
- nameKey: "pricing.premium.name",
- descriptionKey: "pricing.premium.description",
- priceKey: "pricing.premium.price",
- featuresKey: "pricing.premium.features",
- ctaKey: "pricing.contactSales",
- icon: Star,
- popular: false,
- gradient: true,
- },
-];
-
-
-export default function Pricing() {
- const { t } = useTranslation();
- const { subscription, getPlanDisplayName } = useSubscription();
- const currentPlan = subscription?.plan || 'free';
-
- const handleUpgradeRequest = (tierName: string) => {
- const subject = `Upgrade Request: ${tierName} Plan`;
- const body = `Hello,
-
-I would like to request an upgrade to the ${tierName} plan.
-
-Current Plan: ${getPlanDisplayName(currentPlan)}
-Tenant ID: ${subscription?.tenant_id || 'N/A'}
-
-Please provide me with more information about the upgrade process.
-
-Thank you!`;
-
- window.location.href = `mailto:office@sheetmetalconnect.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
- };
-
- const isCurrentPlan = (tierId: string) => tierId === currentPlan;
-
- return (
-
- {/* Header */}
-
-
{t("pricing.title")}
-
- {t("pricing.subtitle")}
-
- {subscription && (
-
-
- {t("pricing.currentPlan")}: {getPlanDisplayName(currentPlan)}
-
-
-
- {t("navigation.myPlan")}
-
-
-
- )}
-
-
- {/* Section 1: Hosted Plans */}
-
-
-
{t("pricing.hostedPlans")}
-
- {t("pricing.hostedPlansDescription")}
-
-
-
-
- {hostedTiers.map((tier) => {
- const Icon = tier.icon;
- const isCurrent = isCurrentPlan(tier.id);
-
- return (
-
- {isCurrent && (
-
- {t("pricing.currentPlan")}
-
- )}
- {tier.popular && !isCurrent && (
-
- {t("onboarding.mostPopular")}
-
- )}
-
-
-
-
-
-
-
- {t(tier.nameKey)}
- {t(tier.descriptionKey)}
-
-
-
-
-
- {t(tier.priceKey)}
- {t("pricing.perMonth")}
-
-
-
-
-
-
- {(t(tier.featuresKey, { returnObjects: true }) as string[]).map((feature, index) => {
- const isNegative = feature.toLowerCase().startsWith('no ') ||
- feature.toLowerCase().startsWith('geen ') ||
- feature.toLowerCase().startsWith('kein');
- return (
-
- {isNegative ? (
-
- ) : (
-
- )}
- {feature}
-
- );
- })}
-
-
-
-
- {isCurrent ? (
-
-
-
- {t("pricing.managePlan")}
-
-
- ) : (
- handleUpgradeRequest(t(tier.nameKey))}
- >
-
- {t(tier.ctaKey)}
-
- )}
-
-
- );
- })}
-
-
-
-
- );
-}
diff --git a/src/pages/admin/analytics/index.ts b/src/pages/admin/analytics/index.ts
new file mode 100644
index 00000000..187f7d45
--- /dev/null
+++ b/src/pages/admin/analytics/index.ts
@@ -0,0 +1,7 @@
+// Analytics pages barrel export
+export { default as JobsAnalytics } from './JobsAnalytics';
+export { default as OEEAnalytics } from './OEEAnalytics';
+export { default as QRMAnalytics } from './QRMAnalytics';
+export { default as QRMDashboard } from './QRMDashboard';
+export { default as QualityAnalytics } from './QualityAnalytics';
+export { default as ReliabilityAnalytics } from './ReliabilityAnalytics';
diff --git a/src/pages/admin/ConfigApiKeys.tsx b/src/pages/admin/config/ApiKeys.tsx
similarity index 100%
rename from src/pages/admin/ConfigApiKeys.tsx
rename to src/pages/admin/config/ApiKeys.tsx
diff --git a/src/pages/admin/ConfigMaterials.tsx b/src/pages/admin/config/Materials.tsx
similarity index 100%
rename from src/pages/admin/ConfigMaterials.tsx
rename to src/pages/admin/config/Materials.tsx
diff --git a/src/pages/admin/ConfigMcpKeys.tsx b/src/pages/admin/config/McpKeys.tsx
similarity index 100%
rename from src/pages/admin/ConfigMcpKeys.tsx
rename to src/pages/admin/config/McpKeys.tsx
diff --git a/src/pages/admin/ConfigMqttPublishers.tsx b/src/pages/admin/config/MqttPublishers.tsx
similarity index 100%
rename from src/pages/admin/ConfigMqttPublishers.tsx
rename to src/pages/admin/config/MqttPublishers.tsx
diff --git a/src/pages/admin/ConfigResources.tsx b/src/pages/admin/config/Resources.tsx
similarity index 100%
rename from src/pages/admin/ConfigResources.tsx
rename to src/pages/admin/config/Resources.tsx
diff --git a/src/pages/admin/ConfigScrapReasons.tsx b/src/pages/admin/config/ScrapReasons.tsx
similarity index 100%
rename from src/pages/admin/ConfigScrapReasons.tsx
rename to src/pages/admin/config/ScrapReasons.tsx
diff --git a/src/pages/admin/ConfigStages.tsx b/src/pages/admin/config/Stages.tsx
similarity index 100%
rename from src/pages/admin/ConfigStages.tsx
rename to src/pages/admin/config/Stages.tsx
diff --git a/src/pages/admin/ConfigUsers.tsx b/src/pages/admin/config/Users.tsx
similarity index 100%
rename from src/pages/admin/ConfigUsers.tsx
rename to src/pages/admin/config/Users.tsx
diff --git a/src/pages/admin/ConfigWebhooks.tsx b/src/pages/admin/config/Webhooks.tsx
similarity index 100%
rename from src/pages/admin/ConfigWebhooks.tsx
rename to src/pages/admin/config/Webhooks.tsx
diff --git a/src/pages/admin/config/index.ts b/src/pages/admin/config/index.ts
new file mode 100644
index 00000000..0f965dc2
--- /dev/null
+++ b/src/pages/admin/config/index.ts
@@ -0,0 +1,10 @@
+// Config pages barrel export
+export { default as ApiKeys } from './ApiKeys';
+export { default as Materials } from './Materials';
+export { default as McpKeys } from './McpKeys';
+export { default as MqttPublishers } from './MqttPublishers';
+export { default as Resources } from './Resources';
+export { default as ScrapReasons } from './ScrapReasons';
+export { default as Stages } from './Stages';
+export { default as Users } from './Users';
+export { default as Webhooks } from './Webhooks';
diff --git a/src/pages/AcceptInvitation.tsx b/src/pages/auth/AcceptInvitation.tsx
similarity index 100%
rename from src/pages/AcceptInvitation.tsx
rename to src/pages/auth/AcceptInvitation.tsx
diff --git a/src/pages/Auth.tsx b/src/pages/auth/Auth.tsx
similarity index 100%
rename from src/pages/Auth.tsx
rename to src/pages/auth/Auth.tsx
diff --git a/src/pages/auth/index.ts b/src/pages/auth/index.ts
new file mode 100644
index 00000000..424e6d35
--- /dev/null
+++ b/src/pages/auth/index.ts
@@ -0,0 +1,3 @@
+// Auth pages barrel export
+export { default as Auth } from './Auth';
+export { default as AcceptInvitation } from './AcceptInvitation';
diff --git a/src/pages/SubscriptionBlocked.tsx b/src/pages/common/SubscriptionBlocked.tsx
similarity index 100%
rename from src/pages/SubscriptionBlocked.tsx
rename to src/pages/common/SubscriptionBlocked.tsx
diff --git a/src/pages/common/index.ts b/src/pages/common/index.ts
new file mode 100644
index 00000000..15bfc818
--- /dev/null
+++ b/src/pages/common/index.ts
@@ -0,0 +1,9 @@
+// Common pages barrel export
+export { default as About } from './About';
+export { default as ApiDocs } from './ApiDocs';
+export { default as Help } from './Help';
+export { MyPlan } from './MyPlan';
+export { default as Pricing } from './Pricing';
+export { default as PrivacyPolicy } from './PrivacyPolicy';
+export { default as SubscriptionBlocked } from './SubscriptionBlocked';
+export { default as TermsOfService } from './TermsOfService';
diff --git a/src/pages/operator/index.ts b/src/pages/operator/index.ts
new file mode 100644
index 00000000..63483cd5
--- /dev/null
+++ b/src/pages/operator/index.ts
@@ -0,0 +1,6 @@
+// Operator pages barrel export
+export { default as MyActivity } from './MyActivity';
+export { default as MyIssues } from './MyIssues';
+export { default as OperatorTerminal } from './OperatorTerminal';
+export { default as OperatorView } from './OperatorView';
+export { default as WorkQueue } from './WorkQueue';
diff --git a/src/routes.ts b/src/routes.ts
index 4e36a87e..ea5a6cb0 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -50,28 +50,12 @@ export const ROUTES = {
},
COMMON: {
- API_DOCS: "/api-docs",
- PRICING: "/pricing",
- MY_PLAN: "/my-plan",
- BILLING_COMING_SOON: "/billing/coming-soon",
- HELP: "/help",
- ABOUT: "/about",
+ API_DOCS: "/admin/api-docs",
+ PRICING: "/admin/pricing",
+ MY_PLAN: "/admin/my-plan",
+ HELP: "/admin/help",
+ ABOUT: "/admin/about",
+ PRIVACY_POLICY: "/privacy-policy",
+ TERMS_OF_SERVICE: "/terms-of-service",
},
-
- // Legacy redirects
- LEGACY: {
- WORK_QUEUE: "/work-queue",
- MY_ACTIVITY: "/my-activity",
- MY_ISSUES: "/my-issues",
- OPERATOR_VIEW: "/operator-view",
- DASHBOARD: "/dashboard",
- STAGES: "/admin/stages",
- MATERIALS: "/admin/materials",
- RESOURCES: "/admin/resources",
- USERS: "/admin/users",
- API_DOCS: "/api-docs",
- PRICING: "/pricing",
- MY_PLAN: "/my-plan",
- HELP: "/help",
- }
} as const;