refactor(notification): blocking added

This commit is contained in:
2026-04-10 21:33:26 -05:00
parent dcb3f2dd13
commit 9a0ef8e51a
10 changed files with 309 additions and 2 deletions

View File

@@ -65,6 +65,7 @@
"onnotice",
"opendock",
"opendocks",
"palletizer",
"ppoo",
"preseed",
"prodlabels",

View File

@@ -10,11 +10,16 @@ import { delay } from "../utils/delay.utils.js";
import { returnFunc } from "../utils/returnHelper.utils.js";
import { sendEmail } from "../utils/sendEmail.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";
import { v1QueryRun } from "../utils/pgConnectToLst.utils.js";
import { v2QueryRun } from "../utils/pgConnectToLst.utils.js";
let shutoffv1 = false
const func = async (data: any, emails: string) => {
// TODO: remove this disable once all 17 plants are on this new lst
v1QueryRun(`update public.notifications set active = false where name = '${data.name}'`)
if (!shutoffv1){
v2QueryRun(`update public.notifications set active = false where name = '${data.name}'`)
shutoffv1 = true
}
const { data: l, error: le } = (await tryCatch(
db.select().from(notifications).where(eq(notifications.id, data.id)),

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,105 @@
import { Link, useRouterState } from "@tanstack/react-router";
import { ChevronRight } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../ui/collapsible";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
useSidebar,
} from "../ui/sidebar";
const docs = [
{
title: "Notifications",
url: "/intro",
//icon,
isActive: window.location.pathname.includes("notifications") ?? false,
items: [
{
title: "Reprints",
url: "/reprints",
},
{
title: "New Blocking order",
url: "/qualityBlocking",
},
],
},
];
export default function DocBar() {
const { setOpen } = useSidebar();
const pathname = useRouterState({
select: (s) => s.location.pathname,
});
const isNotifications = pathname.includes("notifications");
return (
<SidebarGroup>
<SidebarGroupLabel>Docs</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem key={"docs"}>
<SidebarMenuButton asChild>
<Link to={"/docs"} onClick={() => setOpen(false)}>
{/* <item.icon /> */}
<span>{"Intro"}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
<SidebarMenu>
{docs.map((item) => (
<Collapsible
key={item.title}
asChild
defaultOpen={isNotifications}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
<Link
to={"/docs/$"}
params={{ _splat: `notifications${item.url}` }}
>
{item.title}
</Link>
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<Link
to={"/docs/$"}
params={{ _splat: `notifications${subItem.url}` }}
>
{subItem.title}
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -7,6 +7,7 @@ import {
} from "@/components/ui/sidebar";
import { useSession } from "@/lib/auth-client";
import AdminSidebar from "./AdminBar";
import DocBar from "./DocBar";
export function AppSidebar() {
const { data: session } = useSession();
@@ -21,6 +22,7 @@ export function AppSidebar() {
<SidebarMenu>
<SidebarMenuItem>
<SidebarContent>
<DocBar/>
{session &&
(session.user.role === "admin" ||
session.user.role === "systemAdmin") && (

View File

@@ -0,0 +1,76 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default: "bg-card text-card-foreground",
destructive:
"bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
},
},
defaultVariants: {
variant: "default",
},
}
)
function Alert({
className,
variant,
...props
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
return (
<div
data-slot="alert"
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
)
}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-title"
className={cn(
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
className
)}
{...props}
/>
)
}
function AlertDescription({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-description"
className={cn(
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
className
)}
{...props}
/>
)
}
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-action"
className={cn("absolute top-2 right-2", className)}
{...props}
/>
)
}
export { Alert, AlertTitle, AlertDescription, AlertAction }

View File

@@ -0,0 +1,31 @@
import { Collapsible as CollapsiblePrimitive } from "radix-ui"
function Collapsible({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}
function CollapsibleTrigger({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot="collapsible-trigger"
{...props}
/>
)
}
function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
<CollapsiblePrimitive.CollapsibleContent
data-slot="collapsible-content"
{...props}
/>
)
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent }

View File

@@ -0,0 +1,19 @@
export default function reprints() {
return (
<div className="mx-auto w-full max-w-4xl px-6 py-8">
<h1 className="text-3xl underline p-2">Quality Blocking</h1>
<p className="p-2">
When a new blocking order is created a new alert will be sent out to all
users subscribed. if there are multiple blocking orders created between
checks you can expect to get multiple emails. below you will see an
example of a blocking email that is sent out
</p>
<img
src="/lst/app/imgs/docs/notifications/lt_qualityBlocking.png"
alt="Reprint notification example"
className="m-2 rounded-lg border-2"
/>
</div>
);
}

26
frontend/src/lib/docs.ts Normal file
View File

@@ -0,0 +1,26 @@
import type { ComponentType } from "react";
const modules = import.meta.glob("../docs/**/*.tsx", {
eager: true,
});
type DocModule = {
default: ComponentType;
};
const docsMap: Record<string, ComponentType> = {};
for (const path in modules) {
const mod = modules[path] as DocModule;
const slug = path
.replace("../docs/", "")
.replace(".tsx", "");
// "notifications/intro"
docsMap[slug] = mod.default;
}
export function getDoc(slug: string) {
return docsMap[slug];
}

View File

@@ -11,6 +11,8 @@
import { Route as rootRouteImport } from './routes/__root'
import { Route as AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index'
import { Route as DocsIndexRouteImport } from './routes/docs/index'
import { Route as DocsSplatRouteImport } from './routes/docs/$'
import { Route as AdminSettingsRouteImport } from './routes/admin/settings'
import { Route as AdminNotificationsRouteImport } from './routes/admin/notifications'
import { Route as AdminLogsRouteImport } from './routes/admin/logs'
@@ -29,6 +31,16 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const DocsIndexRoute = DocsIndexRouteImport.update({
id: '/docs/',
path: '/docs/',
getParentRoute: () => rootRouteImport,
} as any)
const DocsSplatRoute = DocsSplatRouteImport.update({
id: '/docs/$',
path: '/docs/$',
getParentRoute: () => rootRouteImport,
} as any)
const AdminSettingsRoute = AdminSettingsRouteImport.update({
id: '/admin/settings',
path: '/admin/settings',
@@ -72,6 +84,8 @@ export interface FileRoutesByFullPath {
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
'/admin/settings': typeof AdminSettingsRoute
'/docs/$': typeof DocsSplatRoute
'/docs/': typeof DocsIndexRoute
'/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute
@@ -83,6 +97,8 @@ export interface FileRoutesByTo {
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
'/admin/settings': typeof AdminSettingsRoute
'/docs/$': typeof DocsSplatRoute
'/docs': typeof DocsIndexRoute
'/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute
@@ -95,6 +111,8 @@ export interface FileRoutesById {
'/admin/logs': typeof AdminLogsRoute
'/admin/notifications': typeof AdminNotificationsRoute
'/admin/settings': typeof AdminSettingsRoute
'/docs/$': typeof DocsSplatRoute
'/docs/': typeof DocsIndexRoute
'/(auth)/user/profile': typeof authUserProfileRoute
'/(auth)/user/resetpassword': typeof authUserResetpasswordRoute
'/(auth)/user/signup': typeof authUserSignupRoute
@@ -108,6 +126,8 @@ export interface FileRouteTypes {
| '/admin/logs'
| '/admin/notifications'
| '/admin/settings'
| '/docs/$'
| '/docs/'
| '/user/profile'
| '/user/resetpassword'
| '/user/signup'
@@ -119,6 +139,8 @@ export interface FileRouteTypes {
| '/admin/logs'
| '/admin/notifications'
| '/admin/settings'
| '/docs/$'
| '/docs'
| '/user/profile'
| '/user/resetpassword'
| '/user/signup'
@@ -130,6 +152,8 @@ export interface FileRouteTypes {
| '/admin/logs'
| '/admin/notifications'
| '/admin/settings'
| '/docs/$'
| '/docs/'
| '/(auth)/user/profile'
| '/(auth)/user/resetpassword'
| '/(auth)/user/signup'
@@ -142,6 +166,8 @@ export interface RootRouteChildren {
AdminLogsRoute: typeof AdminLogsRoute
AdminNotificationsRoute: typeof AdminNotificationsRoute
AdminSettingsRoute: typeof AdminSettingsRoute
DocsSplatRoute: typeof DocsSplatRoute
DocsIndexRoute: typeof DocsIndexRoute
authUserProfileRoute: typeof authUserProfileRoute
authUserResetpasswordRoute: typeof authUserResetpasswordRoute
authUserSignupRoute: typeof authUserSignupRoute
@@ -163,6 +189,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/docs/': {
id: '/docs/'
path: '/docs'
fullPath: '/docs/'
preLoaderRoute: typeof DocsIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/docs/$': {
id: '/docs/$'
path: '/docs/$'
fullPath: '/docs/$'
preLoaderRoute: typeof DocsSplatRouteImport
parentRoute: typeof rootRouteImport
}
'/admin/settings': {
id: '/admin/settings'
path: '/admin/settings'
@@ -222,6 +262,8 @@ const rootRouteChildren: RootRouteChildren = {
AdminLogsRoute: AdminLogsRoute,
AdminNotificationsRoute: AdminNotificationsRoute,
AdminSettingsRoute: AdminSettingsRoute,
DocsSplatRoute: DocsSplatRoute,
DocsIndexRoute: DocsIndexRoute,
authUserProfileRoute: authUserProfileRoute,
authUserResetpasswordRoute: authUserResetpasswordRoute,
authUserSignupRoute: authUserSignupRoute,