Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import * as React from "react";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import { usePathname } from "next/navigation";
|
|
|
|
|
import {
|
|
|
|
|
LayoutDashboard,
|
|
|
|
|
CreditCard,
|
|
|
|
|
CalendarDays,
|
|
|
|
|
Users,
|
|
|
|
|
BarChart3,
|
2026-04-17 13:17:11 -04:00
|
|
|
ClipboardList,
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
Settings,
|
|
|
|
|
ChevronsLeft,
|
|
|
|
|
ChevronsRight,
|
|
|
|
|
} from "lucide-react";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
2026-04-16 19:08:08 -04:00
|
|
|
import { OrgSwitcher } from "@/components/layout/org-switcher";
|
Normalize role-based permissions across API and UI
Centralizes role/permission enforcement so each role (owner, admin, editor,
reviewer, viewer) behaves consistently in the API and UI.
- Extend src/lib/permissions.ts with an expanded action map (cards.reprocess,
cards.assign, uploads.create, integrations.manage, etc.) plus helper
predicates (isAdminRole, canEditContent).
- Add requireApiAuthWithPermission(action) to src/lib/api-auth.ts with a
narrowed OrgSession return type and PermissionError -> 403 handling.
- Replace hand-rolled role checks in card, org, integration, form-template,
settings, upload, and location routes with the shared helpers so 403s are
uniform and derived from one permission map.
- Close the editor UI gap: the dashboard upload button, row-level mark
reviewed/reprocess/delete, and card detail edit/reprocess/export/assign
now flow from can(role, action) instead of ad-hoc isAdmin checks.
- Gate /settings/* at the middleware layer for non-admins and hide the
Settings entry in the sidebar and top-bar menu when the role cannot
access it.
- Use isAdminRole() in the team members settings page for consistency.
Made-with: Cursor
2026-04-23 13:17:35 -04:00
|
|
|
import { useUserProfile } from "@/lib/user-profile";
|
|
|
|
|
import { isAdminRole } from "@/lib/permissions";
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
|
|
|
|
|
const STORAGE_KEY = "echo-sidebar-collapsed";
|
|
|
|
|
|
|
|
|
|
const navItems = [
|
|
|
|
|
{ href: "/", label: "Dashboard", icon: LayoutDashboard },
|
|
|
|
|
{ href: "/cards", label: "Response Cards", icon: CreditCard },
|
|
|
|
|
{ href: "/events", label: "Collection Days", icon: CalendarDays },
|
|
|
|
|
{ href: "/people", label: "People", icon: Users },
|
2026-04-17 13:17:11 -04:00
|
|
|
{ href: "/surveys", label: "Surveys", icon: ClipboardList },
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
{ href: "/reports", label: "Reports", icon: BarChart3 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const bottomItems = [
|
|
|
|
|
{ href: "/settings", label: "Settings", icon: Settings },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function isActive(pathname: string, href: string) {
|
|
|
|
|
if (href === "/") return pathname === "/";
|
|
|
|
|
return pathname === href || pathname.startsWith(href + "/");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SidebarContextValue = {
|
|
|
|
|
collapsed: boolean;
|
|
|
|
|
setCollapsed: (v: boolean) => void;
|
|
|
|
|
toggle: () => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const SidebarContext = React.createContext<SidebarContextValue>({
|
|
|
|
|
collapsed: false,
|
|
|
|
|
setCollapsed: () => {},
|
|
|
|
|
toggle: () => {},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export function useSidebar() {
|
|
|
|
|
return React.useContext(SidebarContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function SidebarProvider({ children }: { children: React.ReactNode }) {
|
|
|
|
|
const [collapsed, setCollapsedState] = React.useState(false);
|
|
|
|
|
const [mounted, setMounted] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
setMounted(true);
|
|
|
|
|
try {
|
|
|
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
|
|
|
if (stored === "true") setCollapsedState(true);
|
|
|
|
|
} catch {}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const setCollapsed = React.useCallback((v: boolean) => {
|
|
|
|
|
setCollapsedState(v);
|
|
|
|
|
try {
|
|
|
|
|
localStorage.setItem(STORAGE_KEY, String(v));
|
|
|
|
|
} catch {}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const toggle = React.useCallback(() => {
|
|
|
|
|
setCollapsed(!collapsed);
|
|
|
|
|
}, [collapsed, setCollapsed]);
|
|
|
|
|
|
|
|
|
|
const value = React.useMemo(
|
|
|
|
|
() => ({ collapsed: mounted ? collapsed : false, setCollapsed, toggle }),
|
|
|
|
|
[mounted, collapsed, setCollapsed, toggle]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<SidebarContext.Provider value={value}>{children}</SidebarContext.Provider>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function NavItem({
|
|
|
|
|
href,
|
|
|
|
|
label,
|
|
|
|
|
icon: Icon,
|
|
|
|
|
active,
|
|
|
|
|
collapsed,
|
|
|
|
|
}: {
|
|
|
|
|
href: string;
|
|
|
|
|
label: string;
|
|
|
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
|
|
|
active: boolean;
|
|
|
|
|
collapsed: boolean;
|
|
|
|
|
}) {
|
|
|
|
|
const content = (
|
|
|
|
|
<Link
|
|
|
|
|
href={href}
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
Phase 1 - Security & Bug Fixes:
- Add requireApiAuth helper and protect all 25 unprotected API routes
- Add org-tenant scoping to all card, job, stats, and notification queries
- Fix SSRF in ai-test, mask secrets in settings API, fix middleware bypass
- Fix cards pagination routing, stat filter sync, drag-drop file passing
- Add PUT /api/auth/me for profile persistence, stuck job recovery
- Fix email watcher MIME type detection
Phase 2 - Dynamic Fields & Digital Survey:
- Add FormTemplate, FormField, Person, PasswordResetToken models to schema
- Add fieldData, formTemplateId, firstName, lastName, personId to ResponseCard
- Build FormTemplate CRUD API with field management and org scoping
- Build Form Builder UI with field ordering, type config, and section management
- Refactor card detail page to render fields dynamically from templates
- Add dynamic OCR prompt/schema generation from template fields
- Build public survey page at /s/[orgSlug]/[formSlug] with branding
- Add QR code generation API and share section component
Phase 3 - People & Analytics:
- Build People CRUD API with merge and batch auto-link endpoints
- Build People list and detail pages with search, merge dialog
- Add auto-link logic in OCR completion to match/create Person records
- Add /api/stats/trends endpoint with time series and team activity
- Build Reports page with Recharts (area charts, bar charts, pipeline)
- Upgrade dashboard with sparklines and People stat card
Phase 4 - UX Polish:
- Replace silent error handling with toast notifications across all pages
- Add loading skeletons, differentiated empty states
- Add ARIA labels, skip-to-content link, accessible column toggle
- Add forgot password flow, Cmd+K command palette, Collection Days pages
- Unify Echo branding and theme toggle consistency
Made-with: Cursor
2026-04-17 00:29:26 -04:00
|
|
|
aria-current={active ? "page" : undefined}
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
|
|
|
|
active
|
|
|
|
|
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
|
|
|
: "text-sidebar-foreground/70 hover:bg-sidebar-accent/50 hover:text-sidebar-foreground",
|
|
|
|
|
collapsed && "justify-center px-0"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<Icon className="size-4 shrink-0" />
|
|
|
|
|
{!collapsed && <span className="truncate">{label}</span>}
|
|
|
|
|
</Link>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (collapsed) {
|
|
|
|
|
return (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger render={<div />}>{content}</TooltipTrigger>
|
|
|
|
|
<TooltipContent side="right" sideOffset={8}>
|
|
|
|
|
{label}
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function Sidebar() {
|
|
|
|
|
const pathname = usePathname();
|
|
|
|
|
const { collapsed, toggle } = useSidebar();
|
Normalize role-based permissions across API and UI
Centralizes role/permission enforcement so each role (owner, admin, editor,
reviewer, viewer) behaves consistently in the API and UI.
- Extend src/lib/permissions.ts with an expanded action map (cards.reprocess,
cards.assign, uploads.create, integrations.manage, etc.) plus helper
predicates (isAdminRole, canEditContent).
- Add requireApiAuthWithPermission(action) to src/lib/api-auth.ts with a
narrowed OrgSession return type and PermissionError -> 403 handling.
- Replace hand-rolled role checks in card, org, integration, form-template,
settings, upload, and location routes with the shared helpers so 403s are
uniform and derived from one permission map.
- Close the editor UI gap: the dashboard upload button, row-level mark
reviewed/reprocess/delete, and card detail edit/reprocess/export/assign
now flow from can(role, action) instead of ad-hoc isAdmin checks.
- Gate /settings/* at the middleware layer for non-admins and hide the
Settings entry in the sidebar and top-bar menu when the role cannot
access it.
- Use isAdminRole() in the team members settings page for consistency.
Made-with: Cursor
2026-04-23 13:17:35 -04:00
|
|
|
const { role } = useUserProfile();
|
|
|
|
|
const canManageSettings = isAdminRole(role);
|
|
|
|
|
const visibleBottomItems = bottomItems.filter(
|
|
|
|
|
(item) => item.href !== "/settings" || canManageSettings
|
|
|
|
|
);
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<aside
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
Phase 1 - Security & Bug Fixes:
- Add requireApiAuth helper and protect all 25 unprotected API routes
- Add org-tenant scoping to all card, job, stats, and notification queries
- Fix SSRF in ai-test, mask secrets in settings API, fix middleware bypass
- Fix cards pagination routing, stat filter sync, drag-drop file passing
- Add PUT /api/auth/me for profile persistence, stuck job recovery
- Fix email watcher MIME type detection
Phase 2 - Dynamic Fields & Digital Survey:
- Add FormTemplate, FormField, Person, PasswordResetToken models to schema
- Add fieldData, formTemplateId, firstName, lastName, personId to ResponseCard
- Build FormTemplate CRUD API with field management and org scoping
- Build Form Builder UI with field ordering, type config, and section management
- Refactor card detail page to render fields dynamically from templates
- Add dynamic OCR prompt/schema generation from template fields
- Build public survey page at /s/[orgSlug]/[formSlug] with branding
- Add QR code generation API and share section component
Phase 3 - People & Analytics:
- Build People CRUD API with merge and batch auto-link endpoints
- Build People list and detail pages with search, merge dialog
- Add auto-link logic in OCR completion to match/create Person records
- Add /api/stats/trends endpoint with time series and team activity
- Build Reports page with Recharts (area charts, bar charts, pipeline)
- Upgrade dashboard with sparklines and People stat card
Phase 4 - UX Polish:
- Replace silent error handling with toast notifications across all pages
- Add loading skeletons, differentiated empty states
- Add ARIA labels, skip-to-content link, accessible column toggle
- Add forgot password flow, Cmd+K command palette, Collection Days pages
- Unify Echo branding and theme toggle consistency
Made-with: Cursor
2026-04-17 00:29:26 -04:00
|
|
|
aria-label="Main navigation"
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
className={cn(
|
|
|
|
|
"fixed left-0 top-16 z-30 flex h-[calc(100vh-4rem)] flex-col border-r border-sidebar-border bg-sidebar transition-[width] duration-200 ease-in-out",
|
|
|
|
|
collapsed ? "w-16" : "w-60"
|
|
|
|
|
)}
|
|
|
|
|
>
|
2026-04-16 19:08:08 -04:00
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"border-b border-sidebar-border p-3",
|
|
|
|
|
collapsed && "px-2"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<OrgSwitcher collapsed={collapsed} />
|
|
|
|
|
</div>
|
|
|
|
|
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
<ScrollArea className="flex-1">
|
|
|
|
|
<nav className={cn("flex flex-col gap-1 p-3", collapsed && "px-2")}>
|
|
|
|
|
{navItems.map((item) => (
|
|
|
|
|
<NavItem
|
|
|
|
|
key={item.href}
|
|
|
|
|
{...item}
|
|
|
|
|
active={isActive(pathname, item.href)}
|
|
|
|
|
collapsed={collapsed}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
</ScrollArea>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex flex-col gap-1 border-t border-sidebar-border p-3",
|
|
|
|
|
collapsed && "px-2"
|
|
|
|
|
)}
|
|
|
|
|
>
|
Normalize role-based permissions across API and UI
Centralizes role/permission enforcement so each role (owner, admin, editor,
reviewer, viewer) behaves consistently in the API and UI.
- Extend src/lib/permissions.ts with an expanded action map (cards.reprocess,
cards.assign, uploads.create, integrations.manage, etc.) plus helper
predicates (isAdminRole, canEditContent).
- Add requireApiAuthWithPermission(action) to src/lib/api-auth.ts with a
narrowed OrgSession return type and PermissionError -> 403 handling.
- Replace hand-rolled role checks in card, org, integration, form-template,
settings, upload, and location routes with the shared helpers so 403s are
uniform and derived from one permission map.
- Close the editor UI gap: the dashboard upload button, row-level mark
reviewed/reprocess/delete, and card detail edit/reprocess/export/assign
now flow from can(role, action) instead of ad-hoc isAdmin checks.
- Gate /settings/* at the middleware layer for non-admins and hide the
Settings entry in the sidebar and top-bar menu when the role cannot
access it.
- Use isAdminRole() in the team members settings page for consistency.
Made-with: Cursor
2026-04-23 13:17:35 -04:00
|
|
|
{visibleBottomItems.map((item) => (
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
<NavItem
|
|
|
|
|
key={item.href}
|
|
|
|
|
{...item}
|
|
|
|
|
active={isActive(pathname, item.href)}
|
|
|
|
|
collapsed={collapsed}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
<button
|
|
|
|
|
onClick={toggle}
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
Phase 1 - Security & Bug Fixes:
- Add requireApiAuth helper and protect all 25 unprotected API routes
- Add org-tenant scoping to all card, job, stats, and notification queries
- Fix SSRF in ai-test, mask secrets in settings API, fix middleware bypass
- Fix cards pagination routing, stat filter sync, drag-drop file passing
- Add PUT /api/auth/me for profile persistence, stuck job recovery
- Fix email watcher MIME type detection
Phase 2 - Dynamic Fields & Digital Survey:
- Add FormTemplate, FormField, Person, PasswordResetToken models to schema
- Add fieldData, formTemplateId, firstName, lastName, personId to ResponseCard
- Build FormTemplate CRUD API with field management and org scoping
- Build Form Builder UI with field ordering, type config, and section management
- Refactor card detail page to render fields dynamically from templates
- Add dynamic OCR prompt/schema generation from template fields
- Build public survey page at /s/[orgSlug]/[formSlug] with branding
- Add QR code generation API and share section component
Phase 3 - People & Analytics:
- Build People CRUD API with merge and batch auto-link endpoints
- Build People list and detail pages with search, merge dialog
- Add auto-link logic in OCR completion to match/create Person records
- Add /api/stats/trends endpoint with time series and team activity
- Build Reports page with Recharts (area charts, bar charts, pipeline)
- Upgrade dashboard with sparklines and People stat card
Phase 4 - UX Polish:
- Replace silent error handling with toast notifications across all pages
- Add loading skeletons, differentiated empty states
- Add ARIA labels, skip-to-content link, accessible column toggle
- Add forgot password flow, Cmd+K command palette, Collection Days pages
- Unify Echo branding and theme toggle consistency
Made-with: Cursor
2026-04-17 00:29:26 -04:00
|
|
|
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:
- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments
Made-with: Cursor
2026-04-15 00:59:38 -04:00
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-sidebar-foreground/50 transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-foreground",
|
|
|
|
|
collapsed && "justify-center px-0"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{collapsed ? (
|
|
|
|
|
<ChevronsRight className="size-4 shrink-0" />
|
|
|
|
|
) : (
|
|
|
|
|
<ChevronsLeft className="size-4 shrink-0" />
|
|
|
|
|
)}
|
|
|
|
|
{!collapsed && <span>Collapse</span>}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</aside>
|
|
|
|
|
);
|
|
|
|
|
}
|