AgendaPro v1.0 - multi-tenant SaaS, mobile calendar, public booking
Multi-tenant scheduling SaaS (AgendaPro-equivalent): - Backend: Node + Express + node:sqlite, multi-tenant (admin/owner/employee), versioned migrations, 4 templates (estetica-spa, barberia, clinica, blank). - Frontend: React 18 + TS + Vite + Tailwind, FullCalendar drag-and-drop, Recharts dashboard, mobile-first (day/list views on mobile). - Features: calendar+services+employees+clients+tickets, dashboard with best employee/service, top tickets, top/frequent clients, commissions, cancellation policy + no-show tracking, public online booking (/b/:slug), cash register (cierre de caja), reminders/notifications center. - Verification: 65/65 e2e API, 58/58 visual (0 overflow, 0 console errors), typecheck clean, vite build OK.
This commit is contained in:
+84
@@ -0,0 +1,84 @@
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { AuthProvider, useAuth } from "./lib/auth";
|
||||
import { LoginPage } from "./pages/LoginPage";
|
||||
import { AppShell } from "./components/AppShell";
|
||||
import { AdminShell } from "./components/AdminShell";
|
||||
import { DashboardPage } from "./pages/DashboardPage";
|
||||
import { CalendarPage } from "./pages/CalendarPage";
|
||||
import { EmployeesPage } from "./pages/EmployeesPage";
|
||||
import { ServicesPage } from "./pages/ServicesPage";
|
||||
import { ClientsPage } from "./pages/ClientsPage";
|
||||
import { TicketsPage } from "./pages/TicketsPage";
|
||||
import { ClientDetailPage } from "./pages/ClientDetailPage";
|
||||
import { CashPage } from "./pages/CashPage";
|
||||
import { NotificationsPage } from "./pages/NotificationsPage";
|
||||
import { SettingsPage } from "./pages/SettingsPage";
|
||||
import { AdminOverviewPage } from "./pages/admin/AdminOverviewPage";
|
||||
import { AdminBusinessesPage } from "./pages/admin/AdminBusinessesPage";
|
||||
import { AdminBusinessDetailPage } from "./pages/admin/AdminBusinessDetailPage";
|
||||
import BookingPage from "./pages/public/BookingPage";
|
||||
|
||||
function Protected() {
|
||||
const { user, loading } = useAuth();
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center bg-[#f6f7fb]">
|
||||
<div className="flex flex-col items-center gap-3 text-slate-500">
|
||||
<div className="h-9 w-9 animate-spin rounded-full border-2 border-brand-500 border-t-transparent" />
|
||||
<span className="text-sm font-medium">Cargando AgendaPro…</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!user) return <LoginPage />;
|
||||
|
||||
const isAdmin = user.role === "admin";
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
{/* Admin routes */}
|
||||
{isAdmin && (
|
||||
<Route element={<AdminShell />}>
|
||||
<Route path="/" element={<Navigate to="/admin" replace />} />
|
||||
<Route path="/admin" element={<AdminOverviewPage />} />
|
||||
<Route path="/admin/businesses" element={<AdminBusinessesPage />} />
|
||||
<Route path="/admin/businesses/:id" element={<AdminBusinessDetailPage />} />
|
||||
<Route path="*" element={<Navigate to="/admin" replace />} />
|
||||
</Route>
|
||||
)}
|
||||
|
||||
{/* Business routes */}
|
||||
{!isAdmin && (
|
||||
<Route element={<AppShell />}>
|
||||
<Route path="/" element={<Navigate to={user.role === "owner" ? "/dashboard" : "/calendar"} replace />} />
|
||||
{user.role === "owner" ? (
|
||||
<Route path="/dashboard" element={<DashboardPage />} />
|
||||
) : (
|
||||
<Route path="/dashboard" element={<Navigate to="/calendar" replace />} />
|
||||
)}
|
||||
<Route path="/calendar" element={<CalendarPage />} />
|
||||
<Route path="/clients" element={<ClientsPage />} />
|
||||
<Route path="/clients/:id" element={<ClientDetailPage />} />
|
||||
{user.role === "owner" && <Route path="/employees" element={<EmployeesPage />} />}
|
||||
{user.role === "owner" && <Route path="/services" element={<ServicesPage />} />}
|
||||
{user.role === "owner" && <Route path="/tickets" element={<TicketsPage />} />}
|
||||
{user.role === "owner" && <Route path="/cash" element={<CashPage />} />}
|
||||
{user.role === "owner" && <Route path="/notifications" element={<NotificationsPage />} />}
|
||||
{user.role === "owner" && <Route path="/settings" element={<SettingsPage />} />}
|
||||
{/* Block business users from admin */}
|
||||
<Route path="/admin/*" element={<Navigate to="/" replace />} />
|
||||
<Route path="*" element={<Navigate to={user.role === "owner" ? "/dashboard" : "/calendar"} replace />} />
|
||||
</Route>
|
||||
)}
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/b/:slug" element={<BookingPage />} />
|
||||
<Route path="*" element={<AuthProvider><Protected /></AuthProvider>} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user