refactor(ocp): finished the dashboard and move logs and labels to a tab style

This commit is contained in:
2025-03-27 21:06:25 -05:00
parent e3ba45ae13
commit 7b1c6e1361
9 changed files with 329 additions and 101 deletions

View File

@@ -125,7 +125,10 @@ export default function LabelLog() {
{label.runningNr} {label.runningNr}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell className="font-medium">
{format(label.upd_date, "M/d/yyyy hh:mm")} {format(
label?.upd_date.replace("Z", ""),
"M/d/yyyy hh:mm"
)}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell className="font-medium">
{label.status} {label.status}

View File

@@ -70,8 +70,6 @@ export default function Lots() {
const { settings } = useSettingStore(); const { settings } = useSettingStore();
const server = settings.filter((n) => n.name === "server")[0]?.value || ""; const server = settings.filter((n) => n.name === "server")[0]?.value || "";
console.log(data);
const roles = ["admin", "manager", "operator"]; const roles = ["admin", "manager", "operator"];
if (user && roles.includes(user.role)) { if (user && roles.includes(user.role)) {
@@ -234,9 +232,7 @@ export default function Lots() {
server === "localhost" ? ( server === "localhost" ? (
<> <>
<TableCell className="flex justify-center"> <TableCell className="flex justify-center">
<ManualPrintForm <ManualPrintForm />
lot={lot}
/>
</TableCell> </TableCell>
</> </>
) : ( ) : (

View File

@@ -1,4 +1,4 @@
import {Button} from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -8,8 +8,8 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import {Input} from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import {Label} from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -19,81 +19,81 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import {Textarea} from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import {useSessionStore} from "@/lib/store/sessionStore"; import { useSettingStore } from "@/lib/store/useSettings";
import {useSettingStore} from "@/lib/store/useSettings";
import {LotType} from "@/types/lots";
import axios from "axios"; import axios from "axios";
import {Tag} from "lucide-react"; import { Tag } from "lucide-react";
import {useState} from "react"; import { useState } from "react";
import {Controller, useForm} from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import {toast} from "sonner"; import { toast } from "sonner";
import {manualPrintLabels} from "./ManualPrintLabel";
const printReason = [ const printReason = [
{key: "printerIssue", label: "Printer Related"}, { key: "printerIssue", label: "Printer Related" },
{key: "strapper", label: "Strapper Error"}, { key: "missingRfidTag", label: "Missing or incorrect tag" },
{key: "manualCheck", label: "20th pallet check"}, { key: "strapper", label: "Strapper Error" },
{key: "outOfSync", label: "Labeler Out of Sync"}, { key: "manualCheck", label: "20th pallet check" },
{ key: "outOfSync", label: "Labeler Out of Sync" },
]; ];
export default function ManualPrintForm({lot}: {lot: LotType}) { export default function ManualPrintForm() {
const {user} = useSessionStore();
const token = localStorage.getItem("auth_token"); const token = localStorage.getItem("auth_token");
const {settings} = useSettingStore(); const { settings } = useSettingStore();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const server = settings.filter((n) => n.name === "server")[0]?.value; const server = settings.filter((n) => n.name === "server")[0]?.value;
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value; // const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
// const serverUrl = `http://${server}:${serverPort}`; // const serverUrl = `http://${server}:${serverPort}`;
// what is the dyco set to? rfid or dyco
const dyco = settings.filter((n) => n.name === "dycoPrint");
const { const {
register, register,
handleSubmit, handleSubmit,
//watch, //watch,
formState: {errors}, formState: { errors },
reset, reset,
control, control,
} = useForm(); } = useForm();
const handlePrintLabel = async (lot: LotType) => { const handleManualPrintLog = async (logData: any) => {
//console.log(lot);
const labels: any = await manualPrintLabels(lot, user);
if (labels.success) {
toast.success(labels.message);
} else {
toast.error(labels.message);
}
};
const handleManualPrintLog = async (logData: any, lot: LotType) => {
// toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `); // toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `);
const logdataUrl = `/api/ocp/manualLabelLog`; const logdataUrl = `/api/ocp/manuallabellog`;
axios axios
.post(logdataUrl, logData, {headers: {Authorization: `Bearer ${token}`}}) .post(logdataUrl, logData, {
.then((d) => { headers: { Authorization: `Bearer ${token}` },
//console.log(d); })
toast.success(d.data.message); .then((d) => {
handlePrintLabel(lot); console.log(d);
reset(); if (d.data.success) {
}) toast.success(d.data.message);
.catch((e) => { } else {
if (e.response.status === 500) { toast.error(d.data.message);
toast.error(`Internal Server error please try again.`); }
return {sucess: false}; reset();
} setOpen(false);
})
.catch((e) => {
if (e.response.status === 500) {
toast.error(`Internal Server error please try again.`);
return { sucess: false };
}
if (e.response.status === 401) { if (e.response.status === 401) {
//console.log(e.response); //console.log(e.response);
toast.error(`You are not authorized to do this.`); toast.error(`You are not authorized to do this.`);
return {sucess: false}; return { sucess: false };
} }
}); });
}; };
const onSubmit = (data: any) => { const onSubmit = (data: any) => {
console.log(data); //console.log(data);
handleManualPrintLog(data, lot); handleManualPrintLog(data);
};
const closeForm = () => {
reset();
setOpen(false);
}; };
return ( return (
<Dialog <Dialog
@@ -117,12 +117,14 @@ export default function ManualPrintForm({lot}: {lot: LotType}) {
<DialogHeader> <DialogHeader>
<DialogTitle>Edit profile</DialogTitle> <DialogTitle>Edit profile</DialogTitle>
<DialogDescription> <DialogDescription>
Make changes to your profile here. Click save when you're done. Make changes to your profile here. Click save when
you're done.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<p> <p>
To manually print a label you must complete all the required fields below. To manually print a label you must complete all the
required fields below.
<br /> <br />
If you clicked this in error just click close If you clicked this in error just click close
</p> </p>
@@ -133,7 +135,7 @@ export default function ManualPrintForm({lot}: {lot: LotType}) {
name="printReason" name="printReason"
defaultValue={""} defaultValue={""}
render={({ render={({
field: {onChange}, field: { onChange },
fieldState: {}, fieldState: {},
//formState, //formState,
}) => ( }) => (
@@ -143,35 +145,46 @@ export default function ManualPrintForm({lot}: {lot: LotType}) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectLabel>Print Reasons</SelectLabel> <SelectLabel>
{printReason.map((printReason: any) => ( Print Reasons
<SelectItem value={printReason.key}>{printReason.label}</SelectItem> </SelectLabel>
))} {printReason.map(
(printReason: any) => (
<SelectItem
value={printReason.key}
>
{printReason.label}
</SelectItem>
)
)}
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
)} )}
/> />
) : ( ) : (
<div> <div className="m-2">
<Label htmlFor="printRason" className="m-1"> <Label htmlFor="printRason" className="m-1">
Why are you manually printing? Why are you manually printing?
</Label> </Label>
<Input <Input
type="text" type="text"
className={errors.printReason ? "border-red-500" : ""} className={
errors.printReason ? "border-red-500" : ""
}
aria-invalid={!!errors.printReason} aria-invalid={!!errors.printReason}
{...register("printReason", { {...register("printReason", {
required: true, required: true,
minLength: { minLength: {
value: 5, value: 5,
message: "To short of a reason please try again!", message:
"To short of a reason please try again!",
}, },
})} })}
/> />
</div> </div>
)} )}
<div> <div className="m-2">
<Label htmlFor="line" className="m-1"> <Label htmlFor="line" className="m-1">
"What is the line number you are printing?" "What is the line number you are printing?"
</Label> </Label>
@@ -180,11 +193,11 @@ export default function ManualPrintForm({lot}: {lot: LotType}) {
type="number" type="number"
className={errors.line ? "border-red-500" : ""} className={errors.line ? "border-red-500" : ""}
aria-invalid={!!errors.line} aria-invalid={!!errors.line}
{...register("line", {required: true})} {...register("line", { required: true })}
/> />
</div> </div>
<div> <div className="m-2">
<Label htmlFor="initials" className="m-1"> <Label htmlFor="initials" className="m-1">
Enter intials Enter intials
</Label> </Label>
@@ -192,23 +205,55 @@ export default function ManualPrintForm({lot}: {lot: LotType}) {
//variant="underlined" //variant="underlined"
//label="Enter intials" //label="Enter intials"
{...register("initials", {required: true})} {...register("initials", { required: true })}
/>
</div>
<hr />
{dyco[0].value === "0" && (
<div>
<p>Enter the missing tag number.</p>
<hr />
<Label htmlFor="rfidTag" className="m-1">
Enter the tag number only Example
ALPLA000002541. only enter 2541
</Label>
<Input
type="text"
className={
errors.printReason ? "border-red-500" : ""
}
aria-invalid={!!errors.printReason}
{...register("rfidTag", {
required: true,
minLength: {
value: 1,
message: "Tag number is to short!",
},
})}
/>
</div>
)}
<div className="m-2">
<Textarea
//label="Comments"
placeholder="add more info as needed."
{...register("additionalComments")}
/> />
</div> </div>
<Textarea
//label="Comments"
placeholder="add more info as needed."
{...register("additionalComments")}
/>
<DialogFooter> <DialogFooter>
<Button color="danger" variant="default" onClick={() => setOpen(!open)}> <div className="mt-3">
Close <Button
</Button> color="danger"
<Button color="primary" type="submit"> variant="default"
Print onClick={closeForm}
</Button> >
Close
</Button>
<Button color="primary" type="submit">
Print
</Button>
</div>
</DialogFooter> </DialogFooter>
</form> </form>
</DialogContent> </DialogContent>

View File

@@ -1,15 +1,15 @@
import {LotType} from "@/types/lots"; import { LotType } from "@/types/lots";
import axios from "axios"; import axios from "axios";
export const manualPrintLabels = async (lot: LotType, user: any) => { export const manualPrintLabels = async (lot: LotType, user: any) => {
//console.log(lot); //console.log(lot);
const labelUrl = `/ocp/manualPrintAndFollow`; const labelUrl = `/api/ocp/manualprintandfollow`;
try { try {
const res = await axios.post( const res = await axios.post(
labelUrl, labelUrl,
{line: lot.MachineLocation, printerName: lot.PrinterName}, { line: lot.MachineLocation, printerName: lot.PrinterName },
{headers: {Authorization: `Basic ${user?.prod}`}} { headers: { Authorization: `Basic ${user?.prod}` } }
); );
if (res.data.success) { if (res.data.success) {
@@ -19,7 +19,7 @@ export const manualPrintLabels = async (lot: LotType, user: any) => {
}; };
} else { } else {
return { return {
success: true, success: false,
message: `Line ${lot.MachineDescription} encountered an error printing labels: ${res.data.message}`, message: `Line ${lot.MachineDescription} encountered an error printing labels: ${res.data.message}`,
}; };
} }

View File

@@ -1,5 +1,154 @@
import {LstCard} from "@/components/extendedUI/LstCard"; import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { getOcpLogs } from "@/utils/querys/production/ocpLogs";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { format } from "date-fns";
import { Trash } from "lucide-react";
import { toast } from "sonner";
const labelLogs = [
{ key: "message", label: "Error Message" },
{ key: "created_at", label: "ErrorDat" },
{ key: "clear", label: "Clear" },
//{key: "reprint", label: "Reprint"}, // removing the reprint button for now until repritning is working as intended
];
export default function OcpLogs() { export default function OcpLogs() {
return <LstCard className="m-2 p-2">Ocp Logs</LstCard>; const { data, isError, isLoading } = useQuery(getOcpLogs("4"));
const clearLog = async (log: any) => {
try {
const res = await axios.patch(`/api/logger/logs/${log.log_id}`);
if (res.data.success) {
toast.success(`Log message: ${log.message}, was just cleared`);
} else {
console.log(res);
toast.error(`There was an error clearing the message.`);
}
} catch (error) {
toast.error(`There was an error trying to clearing the message.`);
}
};
const logData = data ? data : [];
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>
{labelLogs.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{Array(7)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</LstCard>
</div>
);
}
return (
<LstCard className="m-2 p-2 min-h-2/5">
<p className="text-center">Labels for the last 2 hours</p>
<Table>
<TableHeader>
<TableRow>
{labelLogs.map((l) => (
<TableHead key={l.key}>{l.label}</TableHead>
))}
</TableRow>
</TableHeader>
{isLoading ? (
<>
<TableBody>
{Array(7)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</>
) : (
<TableBody>
{logData.map((label: any) => (
<TableRow key={label.log_id}>
<TableCell className="font-medium max-w-5/6">
<p className="text-balance">
{label.message}
</p>
</TableCell>
<TableCell className="font-medium">
{format(
label?.created_at.replace("Z", ""),
"M/d/yyyy hh:mm"
)}
</TableCell>
<TableCell className="font-medium">
<Button
size="icon"
onClick={() => clearLog(label)}
>
<Trash />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
)}
</Table>
</LstCard>
);
} }

View File

@@ -4,6 +4,7 @@ import Lots from "./Lots";
import OcpLogs from "./OcpLogs"; import OcpLogs from "./OcpLogs";
import PrinterStatus from "./PrinterStatus"; import PrinterStatus from "./PrinterStatus";
import { useSettingStore } from "@/lib/store/useSettings"; import { useSettingStore } from "@/lib/store/useSettings";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export default function OCPPage() { export default function OCPPage() {
const { settings } = useSettingStore(); const { settings } = useSettingStore();
@@ -17,13 +18,25 @@ export default function OCPPage() {
<Lots /> <Lots />
</div> </div>
<div className="flex flex-row"> <div className="w-5/6">
<div className="w-1/2"> <Tabs defaultValue="ocplogs" className="w-full">
<LabelLog /> <TabsList className="grid w-full grid-cols-2">
</div> <TabsTrigger value="ocplogs">
<div className="w-1/2"> OcpLogs
<OcpLogs /> </TabsTrigger>
</div> <TabsTrigger value="labels">Labels</TabsTrigger>
</TabsList>
<TabsContent value="ocplogs">
<div className="w-full">
<OcpLogs />
</div>
</TabsContent>
<TabsContent value="labels">
<div className="w-full">
<LabelLog />
</div>
</TabsContent>
</Tabs>
</div> </div>
</div> </div>
<div className="w-1/6 flex flex-col"> <div className="w-1/6 flex flex-col">

View File

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

View File

@@ -7,7 +7,7 @@ export function getlots() {
queryFn: () => fetchSettings(), queryFn: () => fetchSettings(),
staleTime: 10 * 1000, staleTime: 10 * 1000,
//refetchInterval: 10 * 1000, refetchInterval: 10 * 1000,
refetchOnWindowFocus: true, refetchOnWindowFocus: true,
}); });
} }

View File

@@ -0,0 +1,22 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getOcpLogs(hours: string) {
return queryOptions({
queryKey: ["ocpLogs"],
queryFn: () => fetchSettings(hours),
staleTime: 1000,
refetchInterval: 2 * 1000,
refetchOnWindowFocus: true,
});
}
const fetchSettings = async (hours: string) => {
const { data } = await axios.get(
`/api/logger/logs?service=ocp&service=rfid&level=error&level=warn&hours=${hours}`
);
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};