a72c00fe19
- New `executive_meeting_alert_state` table (per-user, per-meeting) tracking `dismissed`/`acknowledged`. Migration via `pnpm --filter @workspace/db run push-force`. - New API routes in artifacts/api-server/src/routes/executive-meetings.ts: GET /alert-state, POST /:id/alert-state, POST /:id/postpone-minutes, POST /:id/reschedule, POST /:id/cancel. All three mutation routes acquire a row lock with SELECT ... FOR UPDATE inside the transaction, compute oldValue from the locked snapshot, run conflict detection in the same tx, and write the audit row before commit. Cancel is idempotent. Postpone-minutes rejects ranges that would cross midnight (use reschedule for cross-day moves). - Alert-state route uses race-safe onConflictDoNothing upsert + a conditional UPDATE ... RETURNING so transition audits never duplicate. - New i18n keys `executiveMeetings.alert.*` in en.json + ar.json, including the cancel-confirm prompt. - New component artifacts/tx-os/src/components/executive-meetings/ upcoming-meeting-alert.tsx — globally mounted in App.tsx inside AuthProvider. Draggable with localStorage position persistence, RTL-aware, polls every 30s, shows start–end window, postpone-by- minutes chips [5,10,15,30,45,60], full reschedule sub-form, and a Cancel-meeting flow that requires an explicit confirm step before the destructive call fires (gated on confirmCancel state). - Playwright spec executive-meetings-upcoming-alert.spec.mjs with 6 scenarios: appear+Done, postpone-10 shifts times, cancel-with- confirm, dismiss audit, postpone-chip+conflict-warning toast, AR/RTL render. All 6 pass. - replit.md updated with the new table, routes, and migration step. Pre-existing tsc errors at lines 489, 605, and 2039 of executive-meetings.ts are not touched by this task.
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import { Switch, Route, Router as WouterRouter } from "wouter";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { Toaster } from "@/components/ui/toaster";
|
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
import { AuthProvider } from "@/contexts/AuthContext";
|
|
import { useNotificationsSocket } from "@/hooks/use-notifications-socket";
|
|
import { UpcomingMeetingAlert } from "@/components/executive-meetings/upcoming-meeting-alert";
|
|
import NotFound from "@/pages/not-found";
|
|
import LoginPage from "@/pages/login";
|
|
import RegisterPage from "@/pages/register";
|
|
import ForgotPasswordPage from "@/pages/forgot-password";
|
|
import ResetPasswordPage from "@/pages/reset-password";
|
|
import HomePage from "@/pages/home";
|
|
import ServicesPage from "@/pages/services";
|
|
import ChatPage from "@/pages/chat";
|
|
import NotificationsPage from "@/pages/notifications";
|
|
import AdminPage from "@/pages/admin";
|
|
import NotesPage from "@/pages/notes";
|
|
import MyOrdersPage from "@/pages/my-orders";
|
|
import OrdersIncomingPage from "@/pages/orders-incoming";
|
|
import ExecutiveMeetingsPage from "@/pages/executive-meetings";
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: false,
|
|
staleTime: 30_000,
|
|
},
|
|
},
|
|
});
|
|
|
|
function NotificationsSocketBridge() {
|
|
useNotificationsSocket();
|
|
return null;
|
|
}
|
|
|
|
function Router() {
|
|
return (
|
|
<AuthProvider>
|
|
<NotificationsSocketBridge />
|
|
<UpcomingMeetingAlert />
|
|
<Switch>
|
|
<Route path="/login" component={LoginPage} />
|
|
<Route path="/register" component={RegisterPage} />
|
|
<Route path="/forgot-password" component={ForgotPasswordPage} />
|
|
<Route path="/reset-password" component={ResetPasswordPage} />
|
|
<Route path="/services" component={ServicesPage} />
|
|
<Route path="/chat" component={ChatPage} />
|
|
<Route path="/notifications" component={NotificationsPage} />
|
|
<Route path="/admin" component={AdminPage} />
|
|
<Route path="/notes" component={NotesPage} />
|
|
<Route path="/my-orders" component={MyOrdersPage} />
|
|
<Route path="/orders/incoming" component={OrdersIncomingPage} />
|
|
<Route path="/executive-meetings" component={ExecutiveMeetingsPage} />
|
|
<Route path="/" component={HomePage} />
|
|
<Route component={NotFound} />
|
|
</Switch>
|
|
</AuthProvider>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<TooltipProvider>
|
|
<WouterRouter base={import.meta.env.BASE_URL.replace(/\/$/, "")}>
|
|
<Router />
|
|
</WouterRouter>
|
|
<Toaster />
|
|
</TooltipProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|