Files
TX/artifacts/tx-os/src/App.tsx
T
riyadhafraa 242f63ab26 Task #389: Notification sounds + vibration with per-user customization
- DB: 7 new user prefs (sound per slot, per-type toggles, vibration, mute, volume).
- API: PATCH /auth/me/notification-preferences with whitelisted partial update,
  integer guard for volume, and hydrated AuthUser response. AuthUser now exposes
  the 7 new fields.
- Frontend: Web Audio synth library (8 sounds), settings popover with global
  mute/vibration/volume/per-type toggles, slot tabs, sound preview buttons,
  shift+click on bell for quick mute. Concurrency-safe optimistic updates with
  monotonic seq counter. RTL-correct toggle knob transforms.
- Socket hook plays sound on notification_created (orders/meetings only) when
  tab is hidden, respecting global mute and per-type toggles.
- Audio unlock hook mounted globally to satisfy autoplay restrictions.
- AR/EN translations added.

Pre-existing TS errors in executive-meetings.ts (font_settings) and pre-existing
test failures in test workflow are unrelated to this task.
2026-05-05 08:13:38 +00:00

78 lines
2.7 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 { useAudioUnlock } from "@/hooks/use-audio-unlock";
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();
useAudioUnlock();
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;