Compare commits

...

32 Commits

Author SHA1 Message Date
6665b77e09 feat(cyclecountcheck): added i cycle count check this is in mem right now
if this becomes a resource hog we will move it to the db. no need for it to really be presitant
2025-04-09 17:50:06 -05:00
2e2699ab3c refactor(ocp): change the way logs are brought in and other changes to clean the code up 2025-04-09 17:49:03 -05:00
bad390ec17 feat(silo): added in charts and historical data 2025-04-09 17:48:03 -05:00
efc630f5f8 refactor(auth): added module and submodule access to the user 2025-04-09 17:47:02 -05:00
ded074a1ce refactor(serverpage): added in linke to server by clicking the name 2025-04-09 17:46:12 -05:00
ca0b027e67 refactor(database): removed the createAt as it was a duplicate 2025-04-09 17:45:39 -05:00
1282f5efe4 ci(release): bump build number to 207 2025-04-09 14:05:37 -05:00
77988477f6 ci(release): bump build number to 206 2025-04-09 13:17:17 -05:00
37b26c4c92 ci(release): bump build number to 205 2025-04-09 13:00:20 -05:00
72e2c6ea6a ci(release): bump build number to 204 2025-04-09 12:49:17 -05:00
ffe80d6a58 ci(release): bump build number to 203 2025-04-09 12:31:25 -05:00
9dcd07bf2a ci(release): bump build number to 202 2025-04-09 12:26:47 -05:00
4a36107ebb ci(release): bump build number to 201 2025-04-09 12:15:02 -05:00
fb03fc83e6 ci(release): bump build number to 200 2025-04-09 12:13:28 -05:00
8c522c5b72 ci(release): bump build number to 199 2025-04-09 11:49:22 -05:00
e9c9a84a82 ci(release): bump build number to 198 2025-04-09 11:25:55 -05:00
ff2ecff146 ci(release): bump build number to 197 2025-04-09 08:28:03 -05:00
3757b367d5 ci(release): bump build number to 196 2025-04-09 08:24:14 -05:00
4d979089cb ci(release): bump build number to 195 2025-04-08 21:07:26 -05:00
b1e611c7b8 ci(release): bump build number to 194 2025-04-08 21:05:08 -05:00
02fdc14769 ci(release): bump build number to 193 2025-04-08 21:03:11 -05:00
a794943740 ci(release): bump build number to 192 2025-04-08 21:01:20 -05:00
c2ad046fb0 ci(release): bump build number to 191 2025-04-08 20:56:40 -05:00
ba2b8036b8 ci(release): bump build number to 190 2025-04-08 20:36:42 -05:00
26aa9d8f80 ci(release): bump build number to 189 2025-04-08 20:30:12 -05:00
663f2f436c ci(release): bump build number to 188 2025-04-08 19:26:01 -05:00
88f05eaf65 ci(release): bump build number to 187 2025-04-08 17:36:30 -05:00
b84d6f5186 ci(release): bump build number to 186 2025-04-08 17:32:57 -05:00
410790e693 ci(release): bump build number to 185 2025-04-08 16:00:46 -05:00
f847ce423d ci(release): bump build number to 184 2025-04-08 13:42:46 -05:00
27126cca35 ci(release): bump build number to 183 2025-04-08 11:11:09 -05:00
04f524419d ci(release): bump build number to 182 2025-04-08 11:02:14 -05:00
54 changed files with 1476 additions and 453 deletions

View File

@@ -1,6 +1,15 @@
import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {
text,
pgTable,
numeric,
index,
timestamp,
boolean,
uuid,
uniqueIndex,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { z } from "zod";
export const logs = pgTable(
"logs",
@@ -11,7 +20,7 @@ export const logs = pgTable(
service: text("service").notNull().default("system"),
message: text("message").notNull(),
checked: boolean("checked").default(false),
checkedAt: timestamp("checkedAt"),
//checkedAt: timestamp("checkedAt"),
created_at: timestamp("add_Date").defaultNow(),
},
(table) => [

View File

@@ -33,6 +33,7 @@ export type Servers = {
idAddress: string;
lastUpdated: string;
isUpgrading: boolean;
lstServerPort: string;
};
export default function ServerPage() {
@@ -144,7 +145,12 @@ export default function ServerPage() {
return (
<TableRow key={server.server_id}>
<TableCell className="font-medium">
{server.sName}
<a
href={`http://${server.serverDNS}:${server.lstServerPort}`}
target={"_blank"}
>
<span>{server.sName}</span>
</a>
</TableCell>
<TableCell className="font-medium">
{server.serverDNS}

View File

@@ -1,9 +1,3 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { getUsers } from "@/utils/querys/admin/users";
import { useQuery } from "@tanstack/react-query";
import UserCard from "./components/UserCard";
@@ -22,22 +16,13 @@ export default function UserPage() {
return (
<div className="m-2 w-dvw">
<Accordion type="single" collapsible>
{data.map((u: any) => {
return (
<AccordionItem key={u.user_id} value={u.user_id}>
<AccordionTrigger>
<span>{u.username}</span>
</AccordionTrigger>
<AccordionContent>
<div>
<UserCard user={u} />
</div>
</AccordionContent>
</AccordionItem>
);
})}
</Accordion>
</div>
);
}

View File

@@ -19,11 +19,13 @@ export default function ModuleAccess(data: any) {
<div className="flex flex-row flex-wrap">
{modules?.map((m: any) => {
return (
<div key={m.module_id}>
<ModuleForm
key={m.module_id}
i={m}
username={data.user.username}
module={m}
user={data.user}
refetch={data.refetch}
/>
</div>
);
})}
</div>

View File

@@ -1,4 +1,3 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import {
@@ -10,21 +9,26 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { getUserRoles } from "@/utils/querys/admin/userRoles";
import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { toast } from "sonner";
export default function ModuleForm(props: any) {
const { refetch } = useQuery(getUserRoles());
const token = localStorage.getItem("auth_token");
const role =
props.user?.moduleRoles?.filter(
(m: any) => m.module_id === props.module.module_id
)[0]?.role ?? " ";
const form = useForm({
defaultValues: { role: props.i.role },
defaultValues: {
role: role,
},
onSubmit: async ({ value }) => {
const data = {
username: props.username,
module: props.i.name,
const data: any = {
username: props.user.username,
module: props.module.name,
role: value.role,
};
console.log(data);
@@ -37,7 +41,7 @@ export default function ModuleForm(props: any) {
if (res.data.success) {
toast.success(res.data.message);
refetch();
props.refetch();
form.reset();
} else {
res.data.message;
@@ -49,16 +53,13 @@ export default function ModuleForm(props: any) {
});
return (
<div className="m-2 p-1">
<LstCard>
<p className="text-center">Module: {props.i.name}</p>
<p className="p-1">Current role: </p>
<div className="">
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
className="flex flex-row"
>
<form.Field
name="role"
@@ -67,7 +68,7 @@ export default function ModuleForm(props: any) {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor={field.name}>
Select role
Module: {props.module.name}
</Label>
<Select
value={field.state.value}
@@ -113,7 +114,6 @@ export default function ModuleForm(props: any) {
</Button>
</div>
</form>
</LstCard>
</div>
);
}

View File

@@ -22,13 +22,12 @@ import axios from "axios";
import { toast } from "sonner";
import { CardHeader } from "@/components/ui/card";
import ModuleAccess from "./ModuleAccess";
export default function UserCard(data: any) {
const token = localStorage.getItem("auth_token");
const { refetch } = useQuery(getUsers());
//console.log(modules);
//console.log(data.user);
//console.log(userRoles);
const form = useForm({
...userFormOptions(data.user),
@@ -268,13 +267,17 @@ export default function UserCard(data: any) {
);
}}
/>
</form>
</LstCard>
<div className="mt-4">
<div className="mt-4 ml-4">
<Button onClick={form.handleSubmit}>Save</Button>
</div>
</form>
</LstCard>
</div>
<div>
<LstCard>
<ModuleAccess user={data.user} refetch={refetch} />
</LstCard>
</div>
<div className="flex flex-col"></div>
</div>
);
}

View File

@@ -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 (
<Sidebar collapsible="icon">
@@ -22,19 +27,31 @@ export function AppSidebar() {
{moduleActive("production") && (
<ProductionSideBar
user={user}
moduleID={modules.filter((n) => n.name === "production")[0].module_id as string}
moduleID={
modules.filter((n) => n.name === "production")[0]
.module_id as string
}
/>
)}
{moduleActive("logistics") && (
<LogisticsSideBar
user={user}
moduleID={modules.filter((n) => 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) && <ForkliftSideBar />}
{moduleActive("eom") && hasAccess(user, "eom", modules) && <EomSideBar />}
{moduleActive("quality") && hasAccess(user, "quality", modules) && <QualitySideBar />}
{moduleActive("admin") && hasAccess(user, "admin", modules) && <AdminSideBar />}
{moduleActive("forklift") &&
hasAccess(user, "forklift", modules) && <ForkliftSideBar />}
{moduleActive("eom") && hasAccess(user, "eom", modules) && (
<EomSideBar />
)}
{moduleActive("quality") &&
hasAccess(user, "quality", modules) && <QualitySideBar />}
{moduleActive("admin") && hasAccess(user, "admin", modules) && (
<AdminSideBar />
)}
</SidebarContent>
<SidebarFooter>
<SidebarTrigger />

View File

@@ -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 <div>Loading chart data</div>;
if (isError) return <div>Error in loading chart data</div>;
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 (
<LstCard className="w-[425px] h-[250px] m-1">
<CardContent>
{adjustments.length === 0 ? (
<span>No silo data has been entered for this silo.</span>
) : (
<ChartContainer config={chartConfig}>
<AreaChart
accessibilityLayer
data={adjustments}
margin={{
left: 35,
right: 45,
bottom: 50,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="date"
tickLine={false}
axisLine={false}
tickMargin={14}
angle={-45}
textAnchor="end"
dy={10}
tickFormatter={(value) =>
format(value, "M/d/yyyy")
}
/>
<ChartTooltip
cursor={false}
content={
<ChartTooltipContent indicator="dot" />
}
/>
<Area
dataKey="stock"
type="natural"
fill="rgba(53, 162, 235, 0.5)"
fillOpacity={0.4}
stroke="rgba(53, 162, 235, 0.5)"
stackId="a"
/>
<Area
dataKey="actual"
type="natural"
fill="rgba(255, 99, 132, 0.5)"
fillOpacity={0.4}
stroke="rgba(255, 99, 132, 0.5)"
stackId="a"
/>
</AreaChart>
</ChartContainer>
)}
</CardContent>
{/* <CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none">
Trending up by 5.2% this month{" "}
<TrendingUp className="h-4 w-4" />
</div>
<div className="flex items-center gap-2 leading-none text-muted-foreground">
January - June 2024
</div>
</div>
</div>
</CardFooter> */}
</LstCard>
);
}

View File

@@ -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 <div>Loading adjustmnet data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the adjustments.</p>
</div>
);
}
//console.log(data[0].locationID, parseInt(props.laneId));
const adjustments: any = data.filter(
(l: any) => l.locationID === parseInt(props.laneId)
);
console.log(adjustments);
return (
<div className="container mx-auto py-10">
<DataTable columns={columns} data={adjustments} />
</div>
);
}

View File

@@ -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) {
)}
</div>
</LstCard>
<div className="grow max-w-[400px]">
<LstCard className="m-1 ">charts go here</LstCard>
<LstCard className="m-1">extra options here</LstCard>
<div className="grow max-w-[600px]">
<ChartData laneId={silo.LocationID} />
<div className="flex justify-end m-1">
<Link
to={"/siloAdjustments/$hist"}
params={{ hist: silo.LocationID }}
>
Historical Data
</Link>
</div>
</div>
</div>
</LstCard>

View File

@@ -12,23 +12,28 @@ import {
TableRow,
} from "../ui/table";
import { toast } from "sonner";
import { getOcmeInfo } from "@/utils/querys/production/getOcmeInfo";
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import { Trash } from "lucide-react";
const currentPallets = [
{ key: "line", label: "Line" },
{ key: "runningNr", label: "Running #" },
{ key: "runningNumber", label: "Running #" },
{ key: "upd_date", label: "Date Scanned" },
{ key: "waitingfor", label: "Waiting For" },
{ key: "clear", label: "Clear" },
];
const currentTags = [
{ key: "line", label: "Line" },
{ key: "printerName", label: "Printer" },
{ key: "runningNr", label: "Running #" },
{ key: "upd_date", label: "Label date" },
{ key: "status", label: "Label Status" },
];
// const currentTags = [
// { key: "line", label: "Line" },
// { key: "printerName", label: "Printer" },
// { key: "runningNr", label: "Running #" },
// { key: "upd_date", label: "Label date" },
// { key: "status", label: "Label Status" },
// ];
export default function WrapperManualTrigger() {
const { data, isError, isLoading } = useQuery(getOcmeInfo());
const cameraTrigger = async () => {
try {
const res = await axios.get("/ocme/api/v1/manualCameraTrigger");
@@ -46,6 +51,42 @@ export default function WrapperManualTrigger() {
//stoast.success(error.data.message);
}
};
const clearLabel = async (d: any) => {
const data = {
runningNr: d.runningNr,
};
try {
const res = await axios.patch("/ocme/api/v1/pickedUp", data);
if (res.data.success) {
toast.success(
`${d.runningNr} was just removed from being picked up.`
);
return;
}
if (!res.data.success) {
toast.error(res.data.message);
}
} catch (error) {
console.log(error);
//stoast.success(error.data.message);
}
};
if (isError) {
return (
<div>
<p className="text-center text-pretty">
There was an error getting wrapper scans
</p>
</div>
);
}
const info = data?.filter((r: any) => r.areaFrom === "wrapper_1");
return (
<LstCard className="m-2 p-2">
<ScrollArea className="max-h-[200px]">
@@ -59,6 +100,7 @@ export default function WrapperManualTrigger() {
))}
</TableRow>
</TableHeader>
{isLoading ? (
<TableBody>
{Array(3)
.fill(0)
@@ -82,48 +124,42 @@ export default function WrapperManualTrigger() {
</TableRow>
))}
</TableBody>
</Table>
</ScrollArea>
<ScrollArea className="max-h-[200px]">
<Table>
<TableHeader>
<TableRow>
{currentTags.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
) : (
<TableBody>
{Array(3)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
{info.map((i: any) => (
<TableRow key={i.runningNr}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
{i.lineNum}
</TableCell>
<TableCell>{i.runningNr}</TableCell>
<TableCell>
<Skeleton className="h-4" />
{format(
i?.upd_date.replace("Z", ""),
"M/d/yyyy hh:mm"
)}
</TableCell>
<TableCell>{i.waitingFor}</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
<Button
variant="destructive"
size="icon"
onClick={() => clearLabel(i)}
>
<Trash />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
)}
</Table>
</ScrollArea>
<div>
<hr />
<p className="text-center mb-3">Manual Triggers</p>
<p className="text-center mb-3">Manual Trigger</p>
<div className="flex flex-row justify-between">
<Button onClick={cameraTrigger}>Camera</Button>
<Button>Rfid</Button>
</div>
</div>
</LstCard>

View File

@@ -184,7 +184,7 @@ export default function OcmeCycleCount() {
{data.map((i: any) => {
let classname = ``;
if (
i.info === "Quality Check Required"
i.info === "Validate pallet is ok."
) {
classname = `bg-red-500`;
}

View File

@@ -5,7 +5,6 @@ import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
@@ -26,17 +25,13 @@ const labelLogs = [
];
export default function LabelLog() {
const { data, isError, isLoading } = useQuery(getlabels("4"));
//const {user} = useSessionStore();
//const {settings} = useSettingStore();
//const server = settings.filter((n) => n.name === "server")[0]?.value || "";
//const roles = ["admin", "manager", "operator"];
if (isError) {
return (
<div className="m-2 p-2 min-h-2/5">
<LstCard>
<p className="text-center">Labels for the last 2 hours</p>
<Table>
<TableHeader>
<TableRow>
@@ -77,7 +72,13 @@ export default function LabelLog() {
const labelData = data ? data : [];
return (
<LstCard className="m-2 p-2 min-h-2/5">
<p className="text-center">Labels for the last 2 hours</p>
<p className="text-center">
{labelData.length === 0 ? (
<span>No labels have been printed in the last 2 hours</span>
) : (
<span>Labels for the last 2 hours</span>
)}
</p>
<Table>
<TableHeader>
<TableRow>
@@ -115,7 +116,7 @@ export default function LabelLog() {
) : (
<TableBody>
{labelData.map((label: any) => (
<TableRow key={label.runningNr}>
<TableRow key={label.label_id}>
<TableCell className="font-medium">
{label.line}
</TableCell>
@@ -139,15 +140,15 @@ export default function LabelLog() {
</TableBody>
)}
<TableFooter>
{/* <TableFooter>
{labelData.length === 0 && (
<div>
<div className="flex justify-center">
<h2 className="text-center text-2xl">
No labels have been printed in the last 2 hours
</h2>
</div>
)}
</TableFooter>
</TableFooter> */}
</Table>
</LstCard>
);

View File

@@ -1,31 +1,43 @@
import {Button} from "@/components/ui/button";
import {useSessionStore} from "@/lib/store/sessionStore";
import { Button } from "@/components/ui/button";
import { useSessionStore } from "@/lib/store/sessionStore";
//import {useSettingStore} from "@/lib/store/useSettings";
import {LotType} from "@/types/lots";
import {Tag} from "lucide-react";
import {toast} from "sonner";
import {manualPrintLabels} from "./ManualPrintLabel";
import { LotType } from "@/types/lots";
import { Tag } from "lucide-react";
import { toast } from "sonner";
import { manualPrintLabels } from "./ManualPrintLabel";
import { useState } from "react";
export default function ManualPrint({lot}: {lot: LotType}) {
const {user} = useSessionStore();
export default function ManualPrint({ lot }: { lot: LotType }) {
const { user } = useSessionStore();
const [printing, setPrinting] = useState(false);
//const {settings} = useSettingStore();
//const server = settings.filter((n) => n.name === "server")[0]?.value;
//const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
//const serverUrl = `http://${server}:${serverPort}`;
const handlePrintLabel = async (lot: LotType) => {
//console.log(lot);
const labels: any = await manualPrintLabels(lot, user);
if (labels.success) {
toast.success(labels.message);
setTimeout(() => {
setPrinting(false);
}, 5 * 1000);
setPrinting(true);
} else {
toast.error(labels.message);
setTimeout(() => {
setPrinting(false);
}, 5 * 1000);
}
};
return (
<Button variant="outline" size="icon" onClick={() => handlePrintLabel(lot)}>
<Button
variant="outline"
size="icon"
onClick={() => handlePrintLabel(lot)}
disabled={printing}
>
<Tag className="h-[16px] w-[16px]" />
</Button>
);

View File

@@ -86,7 +86,13 @@ export default function OcpLogs() {
return (
<LstCard className="m-2 p-2 min-h-2/5">
<p className="text-center">Labels for the last 2 hours</p>
<p className="text-center">
{data?.length === 0 ? (
<span>No errors in the last 4 hours</span>
) : (
<span>Logs for the last 4 hours</span>
)}
</p>
<Table>
<TableHeader>
<TableRow>

View File

@@ -1,19 +1,19 @@
import WrapperManualTrigger from "@/components/ocme/WrapperCard";
import LabelLog from "./LabelLog";
import Lots from "./Lots";
import OcpLogs from "./OcpLogs";
import PrinterStatus from "./PrinterStatus";
import { useSettingStore } from "@/lib/store/useSettings";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import LabelLog from "./LabelLog";
export default function OCPPage() {
const { settings } = useSettingStore();
const server = settings.filter((n) => n.plantToken === "usday1");
console.log(server);
let server = settings.filter((n) => n.name === "server");
return (
<div className="h-screen w-full ">
<div className="flex flex-wrap gap-2">
<div className="flex flex-row gap-2">
<div className="flex flex-col w-4/5 h-dvh">
<div className="">
<Lots />
@@ -33,15 +33,18 @@ export default function OCPPage() {
</div>
</TabsContent>
<TabsContent value="labels">
<div className="w-full">
<LabelLog />
</div>
</TabsContent>
</Tabs>
</div>
</div>
<div className="w-1/6 flex flex-col">
{server.length >= 1 && (
<div className="flex flex-col">
{server[0].value === "usday1vms006" && (
<div>
<WrapperManualTrigger />
</div>
)}
{server[0].value === "localhost" && (
<div>
<WrapperManualTrigger />
</div>

View File

@@ -81,7 +81,7 @@ export default function PrinterStatus() {
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>{" "}
</TableHeader>
{isLoading ? (
<TableBody>
{Array(5)

View File

@@ -19,12 +19,18 @@ export const useGetUserRoles = create<SettingState>()((set) => ({
try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const token = localStorage.getItem("auth_token");
if (token) {
const response = await axios.get("/api/auth/getuseraccess", {
headers: { Authorization: `Bearer ${token}` },
});
const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data);
set({ userRoles: data.data });
} else {
//console.log(data);
set({ userRoles: [] });
}
} catch (error) {
console.error("Failed to fetch settings:", error);
}

View File

@@ -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 (
<div>
Hello "/(logistics)/siloAdjustments/$hist"! where the historical
data will be shown in a table alone with a graph
<HistoricalData laneId={hist} />
</div>
);
}

View File

@@ -8,7 +8,7 @@ export function getUsers() {
queryFn: () => fetchUsers(token),
enabled: !!token, // Prevents query if token is null
staleTime: 1000,
//refetchInterval: 2 * 2000,
refetchInterval: 2 * 2000,
refetchOnWindowFocus: true,
});
}

View File

@@ -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,
});
}

View File

@@ -0,0 +1,19 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getOcmeInfo() {
return queryOptions({
queryKey: ["ocmeInfo"],
queryFn: () => fetchSettings(),
staleTime: 1000,
refetchInterval: 2 * 2000,
refetchOnWindowFocus: true,
});
}
const fetchSettings = async () => {
const { data } = await axios.get(`/ocme/api/v1/getInfo`);
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};

View File

@@ -6,7 +6,7 @@ export function getlabels(hours: string) {
queryKey: ["labels"],
queryFn: () => fetchSettings(hours),
staleTime: 1000,
//staleTime: 1000,
refetchInterval: 2 * 2000,
refetchOnWindowFocus: true,
});

View File

@@ -14,7 +14,7 @@ export function getOcpLogs(hours: string) {
const fetchSettings = async (hours: string) => {
const { data } = await axios.get(
`/api/logger/logs?service=ocp&service=rfid&level=error&level=warn&hours=${hours}`
`/api/logger/logs?service=ocp&service=rfid&service=dyco&level=error&level=warn&hours=${hours}`
);
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];

View File

@@ -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<Adjustmnets>[] = [
{
accessorKey: "currentStockLevel",
header: () => <div className="text-right">Stock At Post</div>,
},
{
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 (
<div className="text-right font-medium">{correctDate}</div>
);
}
},
},
{
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 (
<div className="text-right font-medium">{correctDate}</div>
);
}
},
},
{
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 (
<div className="text-right font-medium">{correctDate}</div>
);
}
},
},
{
accessorKey: "add_user",
header: "Creator",
},
];

View File

@@ -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<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
//console.log(data);
return (
<div className="rounded-md border w-[1028px]">
<div>
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef
.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}

View File

@@ -1,25 +1,44 @@
import {Modules} from "@/types/modules";
import {User} from "@/types/users";
import { Modules } from "@/types/modules";
import { User } from "@/types/users";
// user will need access to the module.
// users role will determine there visual access
export function hasAccess(user: User | null, moduleName: string | null, modules: Modules[]): boolean {
export function hasAccess(
user: User | null,
moduleName: string | null,
modules: Modules[]
): boolean {
// get the modules for the id
const filteredModule = modules?.filter((f) => f.name === moduleName);
//console.log(filteredModule[0].module_id);
//console.log(filteredModule[0]);
// userroles and filter out by the module id,
return user?.roles.find((role) => role.module_id === filteredModule[0].module_id) ? true : false;
const roleCheck: any = user?.roles.find(
(role) => role.module_id === filteredModule[0].module_id
);
if (filteredModule[0].roles.includes(roleCheck?.role)) {
return true;
}
//if(filteredModule[0].roles.includes(roleCheck.))
return false;
}
export function hasPageAccess(user: User | null, role: any, module_id: string): boolean {
export function hasPageAccess(
user: User | null,
role: any,
module_id: string
): boolean {
if (role.includes("viewer")) return true;
if (!user) return false;
// get only the module in the user profile
//console.log(user);
const userRole = user?.roles.filter((role) => role.module_id === module_id);
console.log(userRole[0]?.role);
// if (role.includes(userRole[0]?.role)) {
if (role.includes(userRole[0]?.role)) return true;
// return true};
if (userRole.length !== 0) return true;
return false;
}

34
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@hono/zod-openapi": "^0.19.2",
"@scalar/hono-api-reference": "^0.7.2",
"@tanstack/react-form": "^1.2.1",
"@tanstack/react-table": "^8.21.2",
"@types/jsonwebtoken": "^9.0.9",
"@types/nodemailer-express-handlebars": "^4.0.5",
"adm-zip": "^0.5.16",
@@ -1788,6 +1789,26 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@tanstack/react-table": {
"version": "8.21.2",
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
"integrity": "sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg==",
"license": "MIT",
"dependencies": {
"@tanstack/table-core": "8.21.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/@tanstack/store": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.0.tgz",
@@ -1798,6 +1819,19 @@
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/table-core": {
"version": "8.21.2",
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.2.tgz",
"integrity": "sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tediousjs/connection-string": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz",

View File

@@ -27,7 +27,7 @@
"commit": "cz",
"prodinstall": "npm i --omit=dev && npm run db:migrate",
"checkupdates": "npx npm-check-updates",
"testingCode": "dotenvx run -f .env -- tsx watch server/services/logistics/controller/siloAdjustments/migrateAdjustments.ts"
"testingCode": "dotenvx run -f .env -- tsx watch server/services/logistics/controller/warehouse/cycleCountChecks/cyclecountCheck.ts"
},
"config": {
"commitizen": {
@@ -35,7 +35,7 @@
}
},
"admConfig": {
"build": 181,
"build": 207,
"oldBuild": "backend-0.1.3.zip"
},
"devDependencies": {
@@ -61,6 +61,7 @@
"@hono/zod-openapi": "^0.19.2",
"@scalar/hono-api-reference": "^0.7.2",
"@tanstack/react-form": "^1.2.1",
"@tanstack/react-table": "^8.21.2",
"@types/jsonwebtoken": "^9.0.9",
"@types/nodemailer-express-handlebars": "^4.0.5",
"adm-zip": "^0.5.16",

View File

@@ -1,4 +1,5 @@
import { db } from "../../../../../database/dbclient.js";
import { userRoles } from "../../../../../database/schema/userRoles.js";
import { users } from "../../../../../database/schema/users.js";
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
@@ -9,8 +10,25 @@ export const getAllUsers = async () => {
* returns all users that are in lst
*/
createLog("info", "apiAuthedRoute", "auth", "Get all users");
// get all users
const { data, error } = await tryCatch(db.select().from(users));
// add there modules they are in
const { data: m, error: em } = await tryCatch(db.select().from(userRoles));
const user: any = data;
const userData = user.map((i: any) => {
// module in
const module = m?.filter((x: any) => x.user_id === i.user_id);
if (module) {
return { ...i, moduleRoles: module };
} else {
return;
}
});
if (error) {
returnRes(
false,
@@ -19,6 +37,6 @@ export const getAllUsers = async () => {
);
}
returnRes(true, "All users.", data);
return { success: true, message: "All users", data };
//returnRes(true, "All users.", data);
return { success: true, message: "All users", data: userData };
};

View File

@@ -54,11 +54,17 @@ export const setUserAccess = async (
// set the user
try {
const userRole = await db.insert(userRoles).values({
const userRole = await db
.insert(userRoles)
.values({
user_id: user[0].user_id,
role_id: role[0].role_id,
module_id: module[0].module_id,
role: roleName,
})
.onConflictDoUpdate({
target: userRoles.user_id,
set: { role_id: role[0].role_id, role: roleName },
});
//.returning({user: users.username, email: users.email});

View File

@@ -59,7 +59,7 @@ app.openapi(
{
success: access.success,
message: access.message,
data: access.data,
data: [], //access?.data,
},
200
);

View File

@@ -1,7 +1,7 @@
import {eq, sql} from "drizzle-orm";
import {db} from "../../../../database/dbclient.js";
import {logs} from "../../../../database/schema/logs.js";
import {createLog} from "../logger.js";
import { eq, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../logger.js";
export const clearLog = async (id: string) => {
/**
@@ -11,12 +11,20 @@ export const clearLog = async (id: string) => {
try {
const clear = await db
.update(logs)
.set({checked: true, checkedAt: sql`NOW()`})
.set({ checked: true, created_at: sql`NOW()` })
.where(eq(logs.log_id, id));
createLog("info", "lst", "logger", "Log just cleared.");
return {success: true, message: "Log was just cleared."};
return { success: true, message: "Log was just cleared." };
} catch (error) {
createLog("error", "lst", "logger", "There was an error clearing the log.");
return {success: false, message: "There was an error clearing the log."};
createLog(
"error",
"lst",
"logger",
"There was an error clearing the log."
);
return {
success: false,
message: "There was an error clearing the log.",
};
}
};

View File

@@ -1,4 +1,4 @@
import { and, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { and, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../logger.js";
@@ -21,7 +21,8 @@ export const getLogs = async (data: any) => {
inArray(logs.level, data.level),
eq(logs.checked, checked)
)
);
)
.orderBy(desc(logs.created_at));
return { success: true, message: "logs returned", data: logData };
} catch (error) {

View File

@@ -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();

View File

@@ -0,0 +1,59 @@
import { differenceInDays, differenceInSeconds, format } from "date-fns";
import { timeZoneFix } from "../../../../../globalUtils/timeZoneFix.js";
import { createLog } from "../../../../logger/logger.js";
import { delay } from "../../../../../globalUtils/delay.js";
import { tryCatch } from "../../../../../globalUtils/tryCatch.js";
import { query } from "../../../../sqlServer/prodSqlServer.js";
import { cycleCountCheck } from "../../../../sqlServer/querys/warehouse/cycleCountCheck.js";
// setting timer for updating stockCheck on a restart will always check.
let lastCheck = 0;
export const lanes: any = [];
export const getLanesToCycleCount = async () => {
const currentTime: any = timeZoneFix();
// store the lanes in memeory
createLog("info", "warehouse", "logistics", "Empty lane triggered update.");
lastCheck = currentTime;
const ageQuery = cycleCountCheck.replaceAll("[ageOfRow]", `1000`);
const { data: prodLanes, error: pl } = await tryCatch(
query(ageQuery, "Get Stock lane date.")
);
// run the update on the lanes
for (let i = 0; i < prodLanes.length; i++) {
const createLane = {
laneID: prodLanes[i]?.laneID,
warehouseID: prodLanes[i]?.warehouseID,
warehouseName: prodLanes[i]?.warehouseName || "na",
Description: prodLanes[i]?.Description,
LastMoveDate: prodLanes[i]?.LastMoveDate
? format(prodLanes[i]?.LastInv, "M/d/yyyy")
: undefined,
LastInv: format(prodLanes[i]?.LastInv, "M/d/yyyy"),
rowType: prodLanes[i].rowType,
DaysSinceLast:
differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
) <= 0
? 0
: differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
),
upd_date: format(new Date(Date.now()), "M/d/yyyy"),
};
lanes.push(createLane);
createLog(
"debug",
"warehouse",
"logistics",
`${lanes[i].Description} was just added`
);
await delay(10);
//delay to slow this thing down
}
};

View File

@@ -0,0 +1,28 @@
import { lanes } from "./cyclecountCheck.js";
export const getCycleCountCheck = async (
age: number = 1000,
type: string = ""
) => {
/**
* Get the lane data based on the age and type
*/
let filteredLanes = lanes.filter((t: any) => t.DaysSinceLast >= age);
if (type != "") {
return {
sucess: true,
message: `${filteredLanes.length} lanes that are of type ${type} and have not been cycle counted in the last ${age} days.`,
data: filteredLanes.filter(
(t: any) => t.rowType === type.toUpperCase()
),
};
} else {
return {
success: true,
message: `${filteredLanes.length} lanes grabed that have not been cycle counted in the last ${age} days.`,
data: filteredLanes,
};
}
};

View File

@@ -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;

View File

@@ -0,0 +1,60 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { getCycleCountCheck } from "../controller/warehouse/cycleCountChecks/getCycleCountCheck.js";
const app = new OpenAPIHono();
const Body = z
.object({
age: z.number().optional().openapi({ example: 90 }),
//email: z.string().optional().openapi({example: "s.smith@example.com"}),
type: z.string().optional().openapi({ example: "fg" }),
})
.openapi("User");
app.openapi(
createRoute({
tags: ["logistics"],
summary: "Returns lanes that need cycle counted",
method: "post",
path: "/cyclecountcheck",
request: {
body: {
content: {
"application/json": { schema: Body },
},
},
},
// description:
// "Provided a running number and lot number you can consume material.",
responses: responses(),
}),
async (c: any) => {
//apiHit(c, { endpoint: "api/sqlProd/close" });
const { data: body, error: be } = await tryCatch(c.req.json());
if (be) {
return c.json({ success: false, message: "Missing Data." });
}
const check: any = body;
const { data: lanes, error: le } = await tryCatch(
getCycleCountCheck(check.age, check.type)
);
if (le) {
return c.json({
success: false,
message: "Error getting lane data.",
data: le,
});
}
return c.json({
success: lanes.success,
message: lanes.message,
data: lanes.data,
});
}
);
export default app;

View File

@@ -3,6 +3,7 @@ import { ocmeData } from "../../../../database/schema/ocme.js";
import { differenceInMinutes } from "date-fns";
import { createLog } from "../../logger/logger.js";
import { eq } from "drizzle-orm";
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
export const getInfo = async () => {
let ocmeInfo: any = [];
@@ -16,7 +17,7 @@ export const getInfo = async () => {
ocmeInfo = ocmeInfo.map((o: any) => {
const now = new Date(Date.now());
//const strippedDate = o.add_Date.replace("Z", "");
const diff = differenceInMinutes(now, o.add_Date);
const diff = differenceInMinutes(timeZoneFix(), o.add_Date);
return { ...o, waitingFor: diff };
});
createLog(

View File

@@ -32,7 +32,7 @@ export const createLabel = async (data: any, userPrinted: any) => {
if (settingsError) {
return {
success: false,
message: "There was an error getting the printer.",
message: "There was an error getting the settings.",
settingsError,
};
}

View File

@@ -60,11 +60,11 @@ export const labelingProcess = async ({
"error",
"labeling",
"ocp",
`There is not a lot assigned to ${macId[0]?.Name}.`
`There is not a lot assigned to ${line}.`
);
return {
success: false,
message: `There is not a lot assigned to ${macId[0]?.Name}.`,
message: `There is not a lot assigned to ${line}.`,
};
}
}
@@ -128,7 +128,7 @@ export const labelingProcess = async ({
}
// if there are more than 2 lots it might be an auto labeler, autolabeler will be defined by its id for now only dayton
if (filteredLot.length > 2 && plantToken[0].value !== "usday1") {
if (filteredLot?.length > 2 && plantToken[0].value !== "usday1") {
createLog(
"error",
"labeling",
@@ -214,15 +214,15 @@ export const labelingProcess = async ({
// create the label
const label = await createLabel(filteredLot[0], userPrinted);
if (!label.success) {
createLog(
"error",
"labeling",
"ocp",
`There was an error creating the label: ${label.message}`
);
return { sucess: false, message: label.message, data: label.data };
}
// if (!label.success) {
// createLog(
// "error",
// "labeling",
// "ocp",
// `There was an error creating the label: ${label.message}`
// );
// return { sucess: false, message: label.message, data: label.data };
// }
// send over to be booked in if we can do it.
const bookin = settingData.filter((s) => s.name === "bookin");

View File

@@ -0,0 +1,121 @@
import { eq } from "drizzle-orm";
import { db } from "../../../../../database/dbclient.js";
import { settings } from "../../../../../database/schema/settings.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { getPrinters } from "./getPrinters.js";
import { autoLabelingStats } from "./printerStatus.js";
let isPrinterCycling = false;
let actualPrinterCycle: any;
export const printerCycleAutoLabelers = async () => {
/**
* Will only check the auto labelers for status updates.
*/
if (isPrinterCycling)
return {
success: false,
message: "Printers are already being cycled.",
};
createLog("info", "ocp", "ocp", "AutoLabeling cycle has started.");
// get the printers
const { data: s, error: se } = await tryCatch(
db.select().from(settings).where(eq(settings.name, "ocpCycleDelay"))
);
if (se) {
createLog(
"error",
"ocp",
"ocp",
"There was an error getting the ocpCycleDelay."
);
return {
success: false,
message: "Error getting printers.",
};
}
const ocpDelay: any = s;
isPrinterCycling = true;
// start the actual printer cycle
actualPrinterCycle = setInterval(async () => {
const { data, error } = await tryCatch(getPrinters());
if (error) {
createLog(
"error",
"ocp",
"ocp",
"There was an error getting the printers."
);
return {
success: false,
message: "Error getting printers.",
};
}
let printers: any = data.data;
// only keep the assigned ones
printers = printers.filter((p: any) => p.assigned === true);
// for printers we want to ignore there must be a remark stateing to ignore.
printers = printers.filter((p: any) => !p.remark.includes("ignore"));
printers.forEach(async (p: any) => {
/**
* if the last timeprinted would be greater than x well just change the status to idle and extended based on the 2 times.
*
* to get a printer going again label will need to come from the front end as that will just unpause the printer and start the labeling, or the api for manual print
* well need to adjust this to actually print the label then unpause it.
*
* it will be
*
* less than x since time printed run the printer status
* greater than x but less than y change the status to idle, but ping to make sure its online,
* if greater than y change to extended idle but stil also ping to make sure its online.
*/
// ignore pdf printer as we want it here for testing purposes
if (p.name.toLowerCase() === "pdf24") {
return;
}
if (p.name === "Autolabeler") {
await autoLabelingStats(p);
return;
}
});
}, parseInt(ocpDelay[0]?.value) * 2 * 1000);
return { success: true, message: "AutoLabeling cycle has been started." };
};
export const stopPrinterCycle = async () => {
/**
* We will stop the print cylce this is more an emergancy thing.
*/
if (actualPrinterCycle && !actualPrinterCycle._destroyed) {
createLog("info", "ocp", "ocp", "AutoLabeling cycle is being stopped.");
clearInterval(actualPrinterCycle);
isPrinterCycling = false;
return {
success: true,
message: "AutoLabeling cycle has been stopped.",
};
} else {
createLog(
"info",
"ocp",
"ocp",
"AutoLabeling cycle is already stopped."
);
isPrinterCycling = false;
return {
success: true,
message: "AutoLabeling cycle is already Stopped.",
};
}
};

View File

@@ -75,8 +75,16 @@ export const labelerTagRead = async (tagData: any) => {
// check if we need to manual check due to 20 pallets.
if (currentPalletCheck <= cameraPalletCheck) {
currentPalletCheck = currentPalletCheck + 1;
currentPalletCheck++;
labelingProcess({ line: numericString });
createLog(
"info",
"dyco",
"ocp",
`You have printed ${currentPalletCheck} pallets, remaining until ${
cameraPalletCheck - currentPalletCheck
}.`
);
} else {
currentPalletCheck = 0;
createLog(

View File

@@ -17,6 +17,9 @@ import { assignedPrinters } from "./utils/checkAssignments.js";
import { printerCycle } from "./controller/printers/printerCycle.js";
import stopPrinterCycle from "./routes/printers/stopCycle.js";
import startPrinterCycle from "./routes/printers/startCycle.js";
import { printerCycleAutoLabelers } from "./controller/printers/printerCycleAutoLabelers.js";
import AutostartPrinterCycle from "./routes/printers/autoLabelerStart.js";
import AutostopPrinterCycle from "./routes/printers/autoLabelerStop.js";
const app = new OpenAPIHono();
@@ -27,6 +30,8 @@ const routes = [
updateprinters,
startPrinterCycle,
stopPrinterCycle,
AutostartPrinterCycle,
AutostopPrinterCycle,
// lots
getLots,
// labeling
@@ -76,6 +81,7 @@ setTimeout(async () => {
await updatePrinters();
await assignedPrinters();
printerCycle();
printerCycleAutoLabelers();
}
}, 10 * 1000);

View File

@@ -0,0 +1,41 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { printerCycle } from "../../controller/printers/printerCycle.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["ocp:printers"],
summary: "starts the printers cycling.",
method: "get",
path: "/startsprintercycle",
middleware: authMiddleware,
//description: "This might be a temp soltuin during the transtion between versions",
// request: {
// body: {content: {"application/json": {schema: CreateLog}}},
// },
responses: responses(),
}),
async (c) => {
const { data, error } = await tryCatch(printerCycle());
const dataError: any = error;
if (error) {
return c.json({
success: false,
message: "Error in stopping the printer cycle",
data: dataError?.data,
});
}
const getData: any = data;
return c.json({
success: getData?.success,
message: getData?.message,
data: getData.data ?? [],
});
}
);
export default app;

View File

@@ -0,0 +1,41 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { authMiddleware } from "../../../auth/middleware/authMiddleware.js";
import { responses } from "../../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { stopPrinterCycle } from "../../controller/printers/printerCycle.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["ocp:printers"],
summary: "Stops the printers cycling.",
method: "get",
path: "/stopprintercycle",
middleware: authMiddleware,
//description: "This might be a temp soltuin during the transtion between versions",
// request: {
// body: {content: {"application/json": {schema: CreateLog}}},
// },
responses: responses(),
}),
async (c) => {
const { data, error } = await tryCatch(stopPrinterCycle());
const dataError: any = error;
if (error) {
return c.json({
success: false,
message: "Error in stopping the printer cycle",
data: dataError?.data,
});
}
const getData: any = data;
return c.json({
success: getData?.success,
message: getData?.message,
data: getData.data ?? [],
});
}
);
export default app;

View File

@@ -49,7 +49,7 @@ app.openapi(
return c.json({
success: getData?.success,
message: getData?.message,
data: getData.data ?? [],
data: getData?.data ?? [],
});
}
);

View File

@@ -28,8 +28,8 @@ export const assignedPrinters = async () => {
};
}
const printers: any = print.data;
const lots: any = l.data;
const printers: any = print.data ?? [];
const lots: any = l.data ?? [];
for (let i = 0; i < printers.length; i++) {
// is the printer assinged in alplalabel online?

View File

@@ -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);

View File

@@ -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 = [];

View File

@@ -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) {
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",
`Error: ${JSON.stringify(error)}"There was an error getting or adding new roles"`
"There was an error adding new modules to the db"
);
}
}
createLog(
"info",
"lst",
"server",
"Modules were just added due to missing them on server startup"
);
};

View File

@@ -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",

View File

@@ -0,0 +1,80 @@
export const cycleCountCheck = `
-- Define the structure of the result set from the stored procedure
DECLARE @results TABLE (
IdLocation INT,
LastMoveDate Date
)
-- insert into the temp table
insert into @results
select IdLagerAbteilung, MAX(CaSE WHEN CONVERT(char(10), Buchungsdatum, 120) IS NULL THEN '1900-01-01' ELSE CONVERT(char(10), Buchungsdatum, 120) END) AS LastLocMov
from AlplaPROD_test1.dbo.V_LagerBuchungen x(nolock)
group by IdLagerAbteilung
select * from (
select x.IdLagerAbteilung as laneID,
x.IdWarenLager as warehouseID,
w.Bezeichnung as warehouseName,
w.LagerTyp as warehouseIDTyp,
w.Standort as warehouseLocation,
x.Bezeichnung as Description,
LastMoveDate,
CASE WHEN CONVERT(char(10), i.Datum, 120) is null then getdate() - 365 else CONVERT(char(10), i.Datum, 120) end as LastInv,
--create the types of warehouses to choose from
case
-- empty
when (sum(EinlagerungsMengeSum) is null and Datum < LastMoveDate) or (
(sum(EinlagerungsMengeSum) is null and Datum < DATEADD(day, -[ageOfRow], getdate()))
) then 'EMPTY'
-- finished goods
when w.LagerTyp = 2 and w.Standort = 10 then 'FG'
-- external finished goods
when w.LagerTyp = 2 and w.Standort = 20 then 'EXTERNAL'
-- silos
when w.LagerTyp in (3) and x.MaterialSilo = 1 then 'BULK'
-- MATERIALS
when w.LagerTyp = 3 and x.MaterialSilo = 0 then 'MATERIALS'
-- MATERIALS
when w.LagerTyp = 11 then 'WASTE'
-- MATERIALS
when w.LagerTyp = 9 then 'PACKAGING'
end as rowType,
CASE WHEN DateDiff(DAY,i.Datum,getDate()) is null then 1000 else DateDiff(DAY,i.Datum,getDate()) end as DaysSinceLast
from AlplaPROD_test1.dbo.T_LagerAbteilungen as x (NOLOCK)
-- last move
left join
@results as b on
x.IdLagerAbteilung = b.IdLocation
-- last inv
left join
(select * from [AlplaPROD_test1].[dbo].[T_LagerAbteilungenInventuren] (nolock)) as i on x.IdLagerAbteilung =
i.IdLagerAbteilung
-- useing this to determin only if the lane is empty
left join
(select * from [AlplaPROD_test1].dbo.V_LagerPositionenBarcodes (nolock)) as y on x.IdLagerAbteilung =
y.IdLagerAbteilung
-- get the warehosue type
left join
(select * from [AlplaPROD_test1].dbo.T_WarenLager (nolock)) as w on x.IdWarenLager = w.IdWarenLager
where x.aktiv = 1 and x.IdWarenLager not in (1,5,6)
group by x.IdLagerAbteilung,
x.IdWarenLager,
w.LagerTyp,
w.Standort,
x.Bezeichnung,
LastMoveDate,
i.Datum,
x.MaterialSilo,
w.Bezeichnung
) xb
`;