diff --git a/frontend/src/components/layout/lst-sidebar.tsx b/frontend/src/components/layout/lst-sidebar.tsx index 611cd6c..45926fd 100644 --- a/frontend/src/components/layout/lst-sidebar.tsx +++ b/frontend/src/components/layout/lst-sidebar.tsx @@ -1,19 +1,24 @@ -import {Sidebar, SidebarContent, SidebarFooter, SidebarTrigger} from "../ui/sidebar"; -import {ProductionSideBar} from "./side-components/production"; -import {Header} from "./side-components/header"; -import {LogisticsSideBar} from "./side-components/logistics"; -import {QualitySideBar} from "./side-components/quality"; -import {ForkliftSideBar} from "./side-components/forklift"; -import {EomSideBar} from "./side-components/eom"; -import {AdminSideBar} from "./side-components/admin"; -import {useSessionStore} from "../../lib/store/sessionStore"; -import {hasAccess} from "../../utils/userAccess"; -import {moduleActive} from "../../utils/moduleActive"; -import {useModuleStore} from "../../lib/store/useModuleStore"; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarTrigger, +} from "../ui/sidebar"; +import { ProductionSideBar } from "./side-components/production"; +import { Header } from "./side-components/header"; +import { LogisticsSideBar } from "./side-components/logistics"; +import { QualitySideBar } from "./side-components/quality"; +import { ForkliftSideBar } from "./side-components/forklift"; +import { EomSideBar } from "./side-components/eom"; +import { AdminSideBar } from "./side-components/admin"; +import { useSessionStore } from "../../lib/store/sessionStore"; +import { hasAccess } from "../../utils/userAccess"; +import { moduleActive } from "../../utils/moduleActive"; +import { useModuleStore } from "../../lib/store/useModuleStore"; export function AppSidebar() { - const {user} = useSessionStore(); - const {modules} = useModuleStore(); + const { user } = useSessionStore(); + const { modules } = useModuleStore(); return ( @@ -22,19 +27,31 @@ export function AppSidebar() { {moduleActive("production") && ( n.name === "production")[0].module_id as string} + moduleID={ + modules.filter((n) => n.name === "production")[0] + .module_id as string + } /> )} {moduleActive("logistics") && ( n.name === "logistics")[0].module_id as string} + moduleID={ + modules.filter((n) => n.name === "logistics")[0] + .module_id as string + } /> )} - {moduleActive("forklift") && hasAccess(user, "forklift", modules) && } - {moduleActive("eom") && hasAccess(user, "eom", modules) && } - {moduleActive("quality") && hasAccess(user, "quality", modules) && } - {moduleActive("admin") && hasAccess(user, "admin", modules) && } + {moduleActive("forklift") && + hasAccess(user, "forklift", modules) && } + {moduleActive("eom") && hasAccess(user, "eom", modules) && ( + + )} + {moduleActive("quality") && + hasAccess(user, "quality", modules) && } + {moduleActive("admin") && hasAccess(user, "admin", modules) && ( + + )} diff --git a/frontend/src/components/logistics/siloAdjustments/ChartData.tsx b/frontend/src/components/logistics/siloAdjustments/ChartData.tsx new file mode 100644 index 0000000..5ac116d --- /dev/null +++ b/frontend/src/components/logistics/siloAdjustments/ChartData.tsx @@ -0,0 +1,113 @@ +import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"; + +import { CardContent } from "@/components/ui/card"; + +import { + ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { useQuery } from "@tanstack/react-query"; +import { getAdjustments } from "@/utils/querys/logistics/siloAdjustments/getAdjustments"; +import { LstCard } from "@/components/extendedUI/LstCard"; +import { format } from "date-fns"; + +export default function ChartData(props: any) { + const { data, isError, isLoading } = useQuery(getAdjustments()); + const chartConfig = { + stock: { + label: "Stock", + color: "rgb(255, 99, 132)", + }, + actual: { + label: "Actual", + color: "rgb(53, 162, 235)", + }, + } satisfies ChartConfig; + + if (isLoading) return
Loading chart data
; + if (isError) return
Error in loading chart data
; + + let adjustments: any = data.filter( + (l: any) => l.locationID === props.laneId + ); + adjustments = adjustments.splice(0, 10).map((s: any) => { + return { + date: format(s.dateAdjusted.replace("Z", ""), "M/d/yyyy hh:mm"), + stock: s.currentStockLevel, + actual: s.newLevel, + }; + }); + + return ( + + + {adjustments.length === 0 ? ( + No silo data has been entered for this silo. + ) : ( + + + + + format(value, "M/d/yyyy") + } + /> + + } + /> + + + + + )} + + {/* +
+
+
+ Trending up by 5.2% this month{" "} + +
+
+ January - June 2024 +
+
+
+
*/} +
+ ); +} diff --git a/frontend/src/components/logistics/siloAdjustments/HistoricalData.tsx b/frontend/src/components/logistics/siloAdjustments/HistoricalData.tsx index e69de29..163aad4 100644 --- a/frontend/src/components/logistics/siloAdjustments/HistoricalData.tsx +++ b/frontend/src/components/logistics/siloAdjustments/HistoricalData.tsx @@ -0,0 +1,28 @@ +import { getAdjustments } from "@/utils/querys/logistics/siloAdjustments/getAdjustments"; +import { columns } from "@/utils/tableData/siloAdjustmentHist/siloAdjHist"; +import { DataTable } from "@/utils/tableData/siloAdjustmentHist/siloDate"; +import { useQuery } from "@tanstack/react-query"; + +export default function HistoricalData(props: any) { + const { data, isError, isLoading } = useQuery(getAdjustments()); + + if (isLoading) return
Loading adjustmnet data...
; + if (isError) { + return ( +
+

There was an error getting the adjustments.

+
+ ); + } + //console.log(data[0].locationID, parseInt(props.laneId)); + const adjustments: any = data.filter( + (l: any) => l.locationID === parseInt(props.laneId) + ); + + console.log(adjustments); + return ( +
+ +
+ ); +} diff --git a/frontend/src/components/logistics/siloAdjustments/SiloCard.tsx b/frontend/src/components/logistics/siloAdjustments/SiloCard.tsx index 817bb6a..8a191b9 100644 --- a/frontend/src/components/logistics/siloAdjustments/SiloCard.tsx +++ b/frontend/src/components/logistics/siloAdjustments/SiloCard.tsx @@ -12,11 +12,13 @@ import { import { getStockSilo } from "@/utils/querys/logistics/siloAdjustments/getStockSilo"; import { useForm } from "@tanstack/react-form"; import { useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; import axios from "axios"; import { format } from "date-fns"; import { CircleAlert } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; +import ChartData from "./ChartData"; export default function SiloCard(data: any) { const token = localStorage.getItem("auth_token"); @@ -183,9 +185,17 @@ export default function SiloCard(data: any) { )} -
- charts go here - extra options here +
+ + +
+ + Historical Data + +
diff --git a/frontend/src/routes/(logistics)/siloAdjustments/$hist.tsx b/frontend/src/routes/(logistics)/siloAdjustments/$hist.tsx index 1f848b3..16b572c 100644 --- a/frontend/src/routes/(logistics)/siloAdjustments/$hist.tsx +++ b/frontend/src/routes/(logistics)/siloAdjustments/$hist.tsx @@ -1,3 +1,4 @@ +import HistoricalData from "@/components/logistics/siloAdjustments/HistoricalData"; import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/(logistics)/siloAdjustments/$hist")({ @@ -22,10 +23,10 @@ export const Route = createFileRoute("/(logistics)/siloAdjustments/$hist")({ }); function RouteComponent() { + const { hist } = Route.useParams(); return (
- Hello "/(logistics)/siloAdjustments/$hist"! where the historical - data will be shown in a table alone with a graph +
); } diff --git a/frontend/src/utils/querys/logistics/siloAdjustments/getAdjustments.tsx b/frontend/src/utils/querys/logistics/siloAdjustments/getAdjustments.tsx index c95c54c..300af06 100644 --- a/frontend/src/utils/querys/logistics/siloAdjustments/getAdjustments.tsx +++ b/frontend/src/utils/querys/logistics/siloAdjustments/getAdjustments.tsx @@ -1,14 +1,14 @@ import { queryOptions } from "@tanstack/react-query"; import axios from "axios"; -export function getStockSilo() { +export function getAdjustments() { const token = localStorage.getItem("auth_token"); return queryOptions({ - queryKey: ["getUsers"], + queryKey: ["getAdjustments"], queryFn: () => fetchStockSilo(token), enabled: !!token, // Prevents query if token is null staleTime: 1000, - //refetchInterval: 2 * 2000, + refetchInterval: 2 * 2000, refetchOnWindowFocus: true, }); } diff --git a/frontend/src/utils/tableData/siloAdjustmentHist/siloAdjHist.tsx b/frontend/src/utils/tableData/siloAdjustmentHist/siloAdjHist.tsx new file mode 100644 index 0000000..eb981b1 --- /dev/null +++ b/frontend/src/utils/tableData/siloAdjustmentHist/siloAdjHist.tsx @@ -0,0 +1,84 @@ +import { ColumnDef } from "@tanstack/react-table"; +import { format } from "date-fns"; + +// This type is used to define the shape of our data. +// You can use a Zod schema here if you want. +export type Adjustmnets = { + siloAdjust_id: string; + currentStockLevel: string; + newLevel: number; + dateAdjusted: string; + lastDateAdjusted: string; + comment: string; + commentAddedBy: string; + commentDate: string; + add_user: string; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "currentStockLevel", + header: () =>
Stock At Post
, + }, + { + accessorKey: "newLevel", + header: "Level Entered", + }, + { + accessorKey: "dateAdjusted", + header: "Adjustmnet", + cell: ({ row }) => { + if (row.getValue("dateAdjusted")) { + const correctDate = format( + row.original.dateAdjusted?.replace("Z", ""), + "M/d/yyyy hh:mm" + ); + return ( +
{correctDate}
+ ); + } + }, + }, + { + accessorKey: "lastDateAdjusted", + header: "Last Adjusted", + cell: ({ row }) => { + if (row.getValue("lastDateAdjusted")) { + const correctDate = format( + row.original.lastDateAdjusted?.replace("Z", ""), + "M/d/yyyy hh:mm" + ); + return ( +
{correctDate}
+ ); + } + }, + }, + { + accessorKey: "comment", + header: "Comment", + }, + { + accessorKey: "commentAddedBy", + header: "Commenter ", + }, + { + accessorKey: "commentDate", + header: "Comment Date ", + cell: ({ row }) => { + if (row.getValue("commentDate")) { + const correctDate = format( + row.original.commentDate?.replace("Z", ""), + "M/d/yyyy hh:mm" + ); + return ( +
{correctDate}
+ ); + } + }, + }, + { + accessorKey: "add_user", + header: "Creator", + }, +]; diff --git a/frontend/src/utils/tableData/siloAdjustmentHist/siloDate.tsx b/frontend/src/utils/tableData/siloAdjustmentHist/siloDate.tsx new file mode 100644 index 0000000..2426ad7 --- /dev/null +++ b/frontend/src/utils/tableData/siloAdjustmentHist/siloDate.tsx @@ -0,0 +1,110 @@ +import { + ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, + getPaginationRowModel, +} from "@tanstack/react-table"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Button } from "@/components/ui/button"; + +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; +} + +export function DataTable({ + columns, + data, +}: DataTableProps) { + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + }); + //console.log(data); + return ( +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef + .header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+ + +
+
+ ); +} diff --git a/server/services/logistics/controller/siloAdjustments/migrateAdjustments.ts b/server/services/logistics/controller/siloAdjustments/migrateAdjustments.ts index 51154dc..2060884 100644 --- a/server/services/logistics/controller/siloAdjustments/migrateAdjustments.ts +++ b/server/services/logistics/controller/siloAdjustments/migrateAdjustments.ts @@ -56,17 +56,17 @@ export const migrateAdjustments = async () => { /** * Migrate all the silo adjustments :D */ - const silo: any = s?.data; + const silo: any = s?.data.data; createLog("info", "silo", "logistics", "Starting migration."); for (let i = 0; i < silo.length; i++) { const migrate = await db.insert(siloAdjustments).values({ - warehouseID: silo[0].warehouseID, - locationID: silo[0].locationID, - currentStockLevel: silo[0].currentStockLevel, - newLevel: silo[0].newLevel, - dateAdjusted: new Date(silo[0].dateAdjusted), - lastDateAdjusted: new Date(silo[0].lastDateAdjusted), - add_user: silo[0].add_user, + warehouseID: silo[i].warehouseID, + locationID: silo[i].locationID, + currentStockLevel: silo[i].currentStockLevel, + newLevel: silo[i].newLevel, + dateAdjusted: new Date(silo[i].dateAdjusted), + lastDateAdjusted: new Date(silo[i].lastDateAdjusted), + add_user: silo[i].add_user, }); createLog( "info", @@ -87,5 +87,3 @@ export const migrateAdjustments = async () => { .where(eq(settings.name, "siloAdjMigrations")); createLog("info", "silo", "logistics", "Migration completed."); }; - -migrateAdjustments(); diff --git a/server/services/logistics/logisticsService.ts b/server/services/logistics/logisticsService.ts index 5cc9149..7f96445 100644 --- a/server/services/logistics/logisticsService.ts +++ b/server/services/logistics/logisticsService.ts @@ -7,6 +7,8 @@ import postComment from "./route/siloAdjustments/postComment.js"; import getStockSilo from "./route/siloAdjustments/getStockData.js"; import { migrateAdjustments } from "./controller/siloAdjustments/migrateAdjustments.js"; import getSiloAdjustments from "./route/siloAdjustments/getSiloAdjustments.js"; +import { getLanesToCycleCount } from "./controller/warehouse/cycleCountChecks/cyclecountCheck.js"; +import getCycleCountCheck from "./route/getCycleCountChecks.js"; const app = new OpenAPIHono(); @@ -19,6 +21,8 @@ const routes = [ postComment, getStockSilo, getSiloAdjustments, + //lanes + getCycleCountCheck, ] as const; // app.route("/server", modules); @@ -28,6 +32,17 @@ const appRoutes = routes.forEach((route) => { setTimeout(() => { migrateAdjustments(); -}, 10 * 1000); +}, 120 * 1000); + +/** + * Start the cycle count check + */ + +setTimeout(() => { + getLanesToCycleCount(); +}, 5 * 1000); +setInterval(async () => { + getLanesToCycleCount(); +}, 15 * 60 * 1000); export default app; diff --git a/server/services/server/route/modules/getModules.ts b/server/services/server/route/modules/getModules.ts index dbad3dd..a2426d0 100644 --- a/server/services/server/route/modules/getModules.ts +++ b/server/services/server/route/modules/getModules.ts @@ -1,6 +1,7 @@ -import {z, createRoute, OpenAPIHono} from "@hono/zod-openapi"; -import {modules} from "../../../../../database/schema/modules.js"; -import {db} from "../../../../../database/dbclient.js"; +import { z, createRoute, OpenAPIHono } from "@hono/zod-openapi"; +import { modules } from "../../../../../database/schema/modules.js"; +import { db } from "../../../../../database/dbclient.js"; +import { desc } from "drizzle-orm"; // Define the request body schema const requestSchema = z.object({ @@ -13,10 +14,16 @@ const requestSchema = z.object({ // Define the response schema const responseSchema = z.object({ message: z.string().optional(), - module_id: z.string().openapi({example: "6c922c6c-7de3-4ec4-acb0-f068abdc"}).optional(), - name: z.string().openapi({example: "Production"}).optional(), - active: z.boolean().openapi({example: true}).optional(), - roles: z.string().openapi({example: `["viewer","technician"]`}).optional(), + module_id: z + .string() + .openapi({ example: "6c922c6c-7de3-4ec4-acb0-f068abdc" }) + .optional(), + name: z.string().openapi({ example: "Production" }).optional(), + active: z.boolean().openapi({ example: true }).optional(), + roles: z + .string() + .openapi({ example: `["viewer","technician"]` }) + .optional(), }); const app = new OpenAPIHono(); @@ -30,7 +37,7 @@ app.openapi( responses: { 200: { content: { - "application/json": {schema: responseSchema}, + "application/json": { schema: responseSchema }, }, description: "Response message", }, @@ -40,7 +47,7 @@ app.openapi( //console.log("system modules"); let module: any = []; try { - module = await db.select().from(modules); // .where(eq(modules.active, true)); + module = await db.select().from(modules).orderBy(modules.name); // .where(eq(modules.active, true)); } catch (error) { console.log(error); module = []; @@ -49,7 +56,7 @@ app.openapi( // parse the roles const updateModules = module.map((m: any) => { if (m.roles) { - return {...m, roles: m?.roles}; + return { ...m, roles: m?.roles }; } return m; }); //JSON.parse(module[0]?.roles); diff --git a/server/services/server/route/modules/getSubModules.ts b/server/services/server/route/modules/getSubModules.ts index 831e796..efb3041 100644 --- a/server/services/server/route/modules/getSubModules.ts +++ b/server/services/server/route/modules/getSubModules.ts @@ -47,7 +47,10 @@ app.openapi( //console.log("system modules"); let module: any = []; try { - module = await db.select().from(subModules); // .where(eq(modules.active, true)); + module = await db + .select() + .from(subModules) + .orderBy(subModules.name); // .where(eq(modules.active, true)); } catch (error) { console.log(error); module = []; diff --git a/server/services/server/utils/moduleCheck.ts b/server/services/server/utils/moduleCheck.ts index 79005af..292930d 100644 --- a/server/services/server/utils/moduleCheck.ts +++ b/server/services/server/utils/moduleCheck.ts @@ -3,42 +3,69 @@ * this will only run on a server start up */ -import {db} from "../../../../database/dbclient.js"; -import {modules} from "../../../../database/schema/modules.js"; -import {createLog} from "../../logger/logger.js"; +import { db } from "../../../../database/dbclient.js"; +import { modules } from "../../../../database/schema/modules.js"; +import { createLog } from "../../logger/logger.js"; // "view", "technician", "supervisor","manager", "admin", "systemAdmin" -const newModules = [ - {name: "production", active: true, roles: ["viewer", "tester", "systemAdmin"]}, - {name: "logistics", active: false, roles: ["viewer", "tester", "systemAdmin"]}, - {name: "quality", active: false, roles: ["viewer", "manager", "tester", "systemAdmin"]}, - {name: "forklift", active: false, roles: ["manager", "admin", "tester", "systemAdmin"]}, - {name: "eom", active: false, roles: ["manager", "admin", "tester", "systemAdmin"]}, - {name: "admin", active: true, roles: ["admin", "systemAdmin"]}, - {name: "ocp", active: false, roles: ["viewer", "admin", "tester", "systemAdmin"]}, +const newModules: any = [ + { + name: "production", + active: true, + roles: ["viewer", "tester", "systemAdmin"], + }, + { + name: "logistics", + active: false, + roles: ["viewer", "manager", "supervisor", "tester", "systemAdmin"], + }, + { + name: "quality", + active: false, + roles: ["viewer", "manager", "tester", "systemAdmin"], + }, + { + name: "forklift", + active: false, + roles: ["manager", "admin", "tester", "systemAdmin"], + }, + { + name: "eom", + active: false, + roles: ["manager", "admin", "tester", "systemAdmin"], + }, + { name: "admin", active: true, roles: ["admin", "systemAdmin"] }, + { + name: "ocp", + active: false, + roles: ["viewer", "admin", "tester", "systemAdmin"], + }, ]; export const areModulesIn = async () => { // get the roles - try { - const moduleCheck = await db.select().from(modules); - - if (moduleCheck.length !== newModules.length) { - try { - const newRole = await db + for (let i = 0; i < newModules.length; i++) { + try { + const newRole = await db .insert(modules) - .values(newModules) - .onConflictDoNothing() // this will only update the ones that are new :D - .returning({name: modules.name}); - createLog("info", "lst", "server", "Roles were just added due to missing them on server startup"); - } catch (error) { - createLog("error", "lst", "server", "There was an error adding new roles to the db"); - } + .values(newModules[i]) + .onConflictDoUpdate({ + target: modules.name, + set: { roles: newModules[i].roles }, + }) // this will only update the ones that are new :D + .returning({ name: modules.name }); + } catch (error) { + console.log(error); + createLog( + "error", + "lst", + "server", + "There was an error adding new modules to the db" + ); } - } catch (error) { - createLog( - "error", - "lst", - "server", - `Error: ${JSON.stringify(error)}"There was an error getting or adding new roles"` - ); } + createLog( + "info", + "lst", + "server", + "Modules were just added due to missing them on server startup" + ); }; diff --git a/server/services/server/utils/subModuleCheck.ts b/server/services/server/utils/subModuleCheck.ts index 956475a..9ae8664 100644 --- a/server/services/server/utils/subModuleCheck.ts +++ b/server/services/server/utils/subModuleCheck.ts @@ -15,7 +15,7 @@ const newSubModules = [ link: "/siloAdjustments", icon: "Cylinder", active: false, - roles: ["tester", "systemAdmin"], + roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"], subSubModule: [], }, { @@ -38,16 +38,6 @@ const newSubModules = [ active: false, subSubModule: [], }, - { - name: "Ocme cycle counts", - moduleName: "logistics", - description: "", - link: "#", - icon: "Package", - role: ["technician", "supervisor", "manager", "admin", "systemAdmin"], - active: false, - subSubModule: [], - }, { name: "Material Helper", moduleName: "logistics",