From 5865ac3b99d60005c4245740369b0e0789c8fbbd Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Mon, 6 Apr 2026 12:59:30 -0500 Subject: [PATCH] feat(notification): base notifcaiton sub and admin compelted can now sub to a notification and user can remove them selfs plus an admin can remove,updates to add new emails are good as well --- README.md | 10 +- .../notificationSub.delete.route.ts | 7 +- .../notification/notificationSub.get.route.ts | 2 +- frontend/src/components/Sidebar/AdminBar.tsx | 12 +- frontend/src/routeTree.gen.ts | 21 ++ .../-components/NotificationsSubCard.tsx | 9 +- .../(auth)/-components/NotificationsTable.tsx | 10 +- frontend/src/routes/admin/notifications.tsx | 316 ++++++++++++++++++ frontend/types/notifications.ts | 10 + 9 files changed, 376 insertions(+), 21 deletions(-) create mode 100644 frontend/src/routes/admin/notifications.tsx create mode 100644 frontend/types/notifications.ts diff --git a/README.md b/README.md index 7702e6d..31730db 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Quick summary of current rewrite/migration goal. - **Phase:** Backend rewrite -- **Last updated:** 2026-04-05 +- **Last updated:** 2026-04-06 --- @@ -16,9 +16,9 @@ Quick summary of current rewrite/migration goal. | Feature | Description | Status | |----------|--------------|--------| | User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress | -| User Profile | Edit profile, upload avatar | 🟨 In Progress | +| User Profile | ~~Edit profile~~, upload avatar | 🟨 In Progress | | User Admin | Edit user, create user, remove user, alplaprod user integration | ⏳ Not Started | -| Notifications | Subscribe, ~~Create~~, Update, Remove, Manual Trigger | 🟨 In Progress | +| Notifications | ~~Subscribe~~, ~~Create~~, ~~Update~~, ~~~~Remove~~, Manual Trigger | 🟨 In Progress | | Datamart | Create, Update, Run, Deactivate | 🔧 In Progress | | Frontend | Analytics and charts | ⏳ Not Started | | Docs | Instructions and trouble shooting | ⏳ Not Started | @@ -44,7 +44,7 @@ _Status legend:_ How to run the current version of the app. ```bash -git clone https://github.com/youruser/yourrepo.git -cd yourrepo +git clone https://git.tuffraid.net/cowch/lst_v3.git +cd lst_v3 npm install npm run dev \ No newline at end of file diff --git a/backend/notification/notificationSub.delete.route.ts b/backend/notification/notificationSub.delete.route.ts index 702aa78..dd71d7b 100644 --- a/backend/notification/notificationSub.delete.route.ts +++ b/backend/notification/notificationSub.delete.route.ts @@ -34,8 +34,11 @@ r.delete("/", async (req, res: Response) => { .delete(notificationSub) .where( and( - //eq(notificationSub.userId, hasPermissions ? validated.userId : req?.user?.id ?? ""), // allows the admin to delete this - eq(notificationSub.userId, req?.user?.id ?? ""), + eq( + notificationSub.userId, + hasPermissions ? validated.userId : (req?.user?.id ?? ""), + ), // allows the admin to delete this + //eq(notificationSub.userId, req?.user?.id ?? ""), eq(notificationSub.notificationId, validated.notificationId), ), ) diff --git a/backend/notification/notificationSub.get.route.ts b/backend/notification/notificationSub.get.route.ts index d601aef..76d4b3d 100644 --- a/backend/notification/notificationSub.get.route.ts +++ b/backend/notification/notificationSub.get.route.ts @@ -21,7 +21,7 @@ r.get("/", async (req, res: Response) => { }, }); - if (userId !== "") { + if (userId) { hasPermissions.success = false; } diff --git a/frontend/src/components/Sidebar/AdminBar.tsx b/frontend/src/components/Sidebar/AdminBar.tsx index d50b210..8032d64 100644 --- a/frontend/src/components/Sidebar/AdminBar.tsx +++ b/frontend/src/components/Sidebar/AdminBar.tsx @@ -1,5 +1,5 @@ import { Link } from "@tanstack/react-router"; -import { Logs } from "lucide-react"; +import { Bell, Logs, Settings } from "lucide-react"; import { SidebarGroup, @@ -24,10 +24,18 @@ import { export default function AdminSidebar({ session }: any) { const { setOpen } = useSidebar(); const items = [ + { + title: "Notifications", + url: "/admin/notifications", + icon: Bell, + role: ["systemAdmin", "admin"], + module: "admin", + active: true, + }, { title: "Settings", url: "/admin/settings", - icon: Logs, + icon: Settings, role: ["systemAdmin"], module: "admin", active: true, diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index f8f4a28..35b7ff1 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' import { Route as AdminSettingsRouteImport } from './routes/admin/settings' +import { Route as AdminNotificationsRouteImport } from './routes/admin/notifications' import { Route as AdminLogsRouteImport } from './routes/admin/logs' import { Route as authLoginRouteImport } from './routes/(auth)/login' import { Route as authUserSignupRouteImport } from './routes/(auth)/user.signup' @@ -33,6 +34,11 @@ const AdminSettingsRoute = AdminSettingsRouteImport.update({ path: '/admin/settings', getParentRoute: () => rootRouteImport, } as any) +const AdminNotificationsRoute = AdminNotificationsRouteImport.update({ + id: '/admin/notifications', + path: '/admin/notifications', + getParentRoute: () => rootRouteImport, +} as any) const AdminLogsRoute = AdminLogsRouteImport.update({ id: '/admin/logs', path: '/admin/logs', @@ -64,6 +70,7 @@ export interface FileRoutesByFullPath { '/about': typeof AboutRoute '/login': typeof authLoginRoute '/admin/logs': typeof AdminLogsRoute + '/admin/notifications': typeof AdminNotificationsRoute '/admin/settings': typeof AdminSettingsRoute '/user/profile': typeof authUserProfileRoute '/user/resetpassword': typeof authUserResetpasswordRoute @@ -74,6 +81,7 @@ export interface FileRoutesByTo { '/about': typeof AboutRoute '/login': typeof authLoginRoute '/admin/logs': typeof AdminLogsRoute + '/admin/notifications': typeof AdminNotificationsRoute '/admin/settings': typeof AdminSettingsRoute '/user/profile': typeof authUserProfileRoute '/user/resetpassword': typeof authUserResetpasswordRoute @@ -85,6 +93,7 @@ export interface FileRoutesById { '/about': typeof AboutRoute '/(auth)/login': typeof authLoginRoute '/admin/logs': typeof AdminLogsRoute + '/admin/notifications': typeof AdminNotificationsRoute '/admin/settings': typeof AdminSettingsRoute '/(auth)/user/profile': typeof authUserProfileRoute '/(auth)/user/resetpassword': typeof authUserResetpasswordRoute @@ -97,6 +106,7 @@ export interface FileRouteTypes { | '/about' | '/login' | '/admin/logs' + | '/admin/notifications' | '/admin/settings' | '/user/profile' | '/user/resetpassword' @@ -107,6 +117,7 @@ export interface FileRouteTypes { | '/about' | '/login' | '/admin/logs' + | '/admin/notifications' | '/admin/settings' | '/user/profile' | '/user/resetpassword' @@ -117,6 +128,7 @@ export interface FileRouteTypes { | '/about' | '/(auth)/login' | '/admin/logs' + | '/admin/notifications' | '/admin/settings' | '/(auth)/user/profile' | '/(auth)/user/resetpassword' @@ -128,6 +140,7 @@ export interface RootRouteChildren { AboutRoute: typeof AboutRoute authLoginRoute: typeof authLoginRoute AdminLogsRoute: typeof AdminLogsRoute + AdminNotificationsRoute: typeof AdminNotificationsRoute AdminSettingsRoute: typeof AdminSettingsRoute authUserProfileRoute: typeof authUserProfileRoute authUserResetpasswordRoute: typeof authUserResetpasswordRoute @@ -157,6 +170,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AdminSettingsRouteImport parentRoute: typeof rootRouteImport } + '/admin/notifications': { + id: '/admin/notifications' + path: '/admin/notifications' + fullPath: '/admin/notifications' + preLoaderRoute: typeof AdminNotificationsRouteImport + parentRoute: typeof rootRouteImport + } '/admin/logs': { id: '/admin/logs' path: '/admin/logs' @@ -200,6 +220,7 @@ const rootRouteChildren: RootRouteChildren = { AboutRoute: AboutRoute, authLoginRoute: authLoginRoute, AdminLogsRoute: AdminLogsRoute, + AdminNotificationsRoute: AdminNotificationsRoute, AdminSettingsRoute: AdminSettingsRoute, authUserProfileRoute: authUserProfileRoute, authUserResetpasswordRoute: authUserResetpasswordRoute, diff --git a/frontend/src/routes/(auth)/-components/NotificationsSubCard.tsx b/frontend/src/routes/(auth)/-components/NotificationsSubCard.tsx index ffe4b78..6010a58 100644 --- a/frontend/src/routes/(auth)/-components/NotificationsSubCard.tsx +++ b/frontend/src/routes/(auth)/-components/NotificationsSubCard.tsx @@ -14,7 +14,7 @@ import { notifications } from "../../../lib/queries/notifications"; export default function NotificationsSubCard({ user }: any) { const { data } = useSuspenseQuery(notifications()); - const { data: ns, refetch } = useSuspenseQuery(notificationSubs(user.id)); + const { refetch } = useSuspenseQuery(notificationSubs(user.id)); const form = useAppForm({ defaultValues: { notificationId: "", @@ -32,8 +32,11 @@ export default function NotificationsSubCard({ user }: any) { withCredentials: true, }); - refetch(); - form.reset(); + if (res.status === 200) { + toast.success("Notification Subbed"); + refetch(); + form.reset(); + } } catch (error) { console.error(error); } diff --git a/frontend/src/routes/(auth)/-components/NotificationsTable.tsx b/frontend/src/routes/(auth)/-components/NotificationsTable.tsx index 16e3a70..dcc65f7 100644 --- a/frontend/src/routes/(auth)/-components/NotificationsTable.tsx +++ b/frontend/src/routes/(auth)/-components/NotificationsTable.tsx @@ -3,6 +3,7 @@ import { createColumnHelper } from "@tanstack/react-table"; import axios from "axios"; import { Trash } from "lucide-react"; import { toast } from "sonner"; +import type { Notifications } from "../../../../types/notifications"; import { Button } from "../../../components/ui/button"; import { Card, CardContent, CardHeader } from "../../../components/ui/card"; import { @@ -15,14 +16,6 @@ import { notifications } from "../../../lib/queries/notifications"; import LstTable from "../../../lib/tableStuff/LstTable"; import SearchableHeader from "../../../lib/tableStuff/SearchableHeader"; -type Notifications = { - id: string; - name: string; - emails: string; - description: string; - remove: unknown; -}; - export default function NotificationsTable({ userId }: any) { const { data: subs, refetch } = useSuspenseQuery(notificationSubs(userId)); const { data: note } = useSuspenseQuery(notifications()); @@ -101,6 +94,7 @@ export default function NotificationsTable({ userId }: any) { return ( + ); + }, + }), + ]; + + return ( +
+
+

Settings

+

+ Manage you settings and related data. +

+
+ + + + System Settings + + + + + Notifications + Subscriptions + + }> + + + + + + + + + + +
+ ); +} diff --git a/frontend/types/notifications.ts b/frontend/types/notifications.ts new file mode 100644 index 0000000..eb076fd --- /dev/null +++ b/frontend/types/notifications.ts @@ -0,0 +1,10 @@ +export type Notifications = { + id: string; + name: string; + emails: string; + description: string; + remove?: unknown; + active?: boolean; + interval: number; + options: unknown[]; +};