feat(frontend): migrated old > new silo adjustments

moved the apps around so we can use 1 url for cors bs
This commit is contained in:
2025-10-25 17:22:51 -05:00
parent d46ef922f3
commit 425f8f5f71
179 changed files with 7511 additions and 2724 deletions

View File

@@ -0,0 +1,47 @@
import { Button } from "../../../../../components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../../../components/ui/dialog";
import Cards from "./Cards";
//import { toast } from "sonner";
export function AddCards() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Add Cards</Button>
</DialogTrigger>
<DialogContent className="min-w-fit ">
<DialogHeader>
<DialogTitle>Cards</DialogTitle>
<DialogDescription>
Manage Cards and there settings.
</DialogDescription>
</DialogHeader>
<div className="flex flex-row">
<div className="">
<Cards name={"ppoo"} inventory />
<Cards name={"inv-empty"} rowType={"empty"} />
<Cards name={"inv-fg"} rowType={"fg"} />
</div>
<div className="">
<Cards name={"inv-materials"} rowType={"materials"} />
<Cards name={"inv-packaging"} rowType={"packaging"} />
<Cards name={"inv-waste"} rowType={"waste"} />
<Cards name={"openOrder"} inventory />
</div>
</div>
{/* <DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter> */}
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,104 @@
import { Button } from "../../../../../components/ui/button";
import { Checkbox } from "../../../../../components/ui/checkbox";
import { Label } from "../../../../../components/ui/label";
import { useAppForm } from "../../../../../lib/formStuff";
import { useCardStore } from "../../-lib/store/useCardStore";
export default function Cards(card: any) {
const { addCard, removeCard, cards } = useCardStore();
let existing: any = cards.filter((n: any) => n.name === card.name);
//console.log(existing);
const form = useAppForm({
defaultValues: {
name: existing[0]?.name || card.name,
rowType: existing[0]?.type ?? card.rowType,
age: existing[0]?.age ?? 90,
active: existing[0]?.active ?? false,
},
onSubmit: async ({ value }) => {
console.log(value);
const testCard: any = cards.filter((i: any) => i.name === value.name);
if (value.active) {
const newCard = {
name: `${value.name}`,
rowType: value.rowType,
age: value.age ?? 90,
active: value.active,
};
if (testCard.length > 0) {
removeCard(value.name);
addCard(newCard);
return;
}
// change the name for a type card
addCard(newCard);
} else {
removeCard(value.name);
}
},
});
return (
<div className="border-solid border-2 m-2">
<p>{card.name}</p>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
className="flex flex-row"
>
<form.AppField
name="active"
// validators={{
// // We can choose between form-wide and field-specific validators
// onChange: ({ value }) =>
// value.length > 3
// ? undefined
// : "Username must be longer than 3 letters",
// }}
children={(field) => {
return (
<div className="m-2 p-2 flex flex-row">
<div>
<Label htmlFor="active">
<span>Active</span>
</Label>
</div>
<Checkbox
className="ml-2"
name={field.name}
onBlur={field.handleBlur}
checked={field.state.value}
onCheckedChange={(e) => field.handleChange(e)}
/>
</div>
);
}}
/>
{!card.inventory && (
<>
<form.AppField
name="age"
children={(field) => (
<field.InputField
label="Age"
inputType="number"
required={true}
/>
)}
/>
</>
)}
<div className="mt-7">
<Button type="submit" onClick={() => form.handleSubmit()}>
Save Card
</Button>
</div>
</form>
</div>
);
}

View File

@@ -0,0 +1,40 @@
import { useCardStore } from "../../-lib/store/useCardStore";
import INVCheckCard from "../logistics/warehouse/InventoryCard";
import OpenOrders from "../logistics/warehouse/openOrders";
import PPOO from "../logistics/warehouse/PPOOCard";
const componentsMap: any = {
ppoo: PPOO,
inv: INVCheckCard,
openOrder: OpenOrders,
//QualityRequest,
};
export default function DashBoard() {
const { cards } = useCardStore();
//console.log(cards);
return (
<div className="ml-5 w-11/12 h-9/10 grid grid-cols-12 gap-1">
{cards.map((a: any) => {
const name = a.name; //.filter((c) => c.i === card.i)[0].i || "name";
const Component = componentsMap[name.split("-")[0]];
if (name === "openOrder") {
return (
<div key={a.name} className="col-span-6">
<Component age={a.age} type={a.rowType} />
</div>
);
} else {
//console.log(name.split("-")[0], a);
return (
<div key={a.name} className="col-span-3">
<Component data={a} />
</div>
);
}
})}
</div>
);
}

View File

@@ -0,0 +1,23 @@
import type { ReactNode } from "react";
import { Card } from "@/components/ui/card";
interface LstCardProps {
children?: ReactNode;
className?: string;
style?: React.CSSProperties;
}
export function LstCard({
children,
className = "",
style = {},
}: LstCardProps) {
return (
<Card
className={`border-solid border-1 border-[#00659c] ${className}`}
style={style}
>
{children}
</Card>
);
}

View File

@@ -0,0 +1,55 @@
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarTrigger,
} from "../../../../../components/ui/sidebar";
import {
useAuth,
userAccess,
useUserRoles,
} from "../../../../../lib/authClient";
import { useModuleStore } from "../../-lib/store/useModuleStore";
import { AdminSideBar } from "./side-components/admin";
import { EomSideBar } from "./side-components/eom";
import { ForkliftSideBar } from "./side-components/forklift";
import { Header } from "./side-components/header";
import { LogisticsSideBar } from "./side-components/logistics";
import { ProductionSideBar } from "./side-components/production";
import { QualitySideBar } from "./side-components/quality";
export function AppSidebar() {
const { session } = useAuth();
const { userRoles } = useUserRoles();
const { modules } = useModuleStore();
return (
<Sidebar collapsible="icon">
<SidebarContent>
<Header />
<ProductionSideBar
user={session?.user as any}
moduleID={
modules.filter((n) => n.name === "production")[0]
?.module_id as string
}
/>
{/* userAccess("logistics", ["systemAdmin", "admin","manager","viewer"]) */}
<LogisticsSideBar user={session?.user as any} userRoles={userRoles} />
{userAccess(null, ["systemAdmin"]) && (
<>
<ForkliftSideBar />
<EomSideBar />
<QualitySideBar />
<AdminSideBar />
</>
)}
</SidebarContent>
<SidebarFooter>
<SidebarTrigger />
</SidebarFooter>
</Sidebar>
);
}

View File

@@ -0,0 +1,194 @@
import {
AlignJustify,
Atom,
Logs,
Minus,
Plus,
Server,
Settings,
ShieldCheck,
Users,
Webhook,
} from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../../../../../../components/ui/collapsible";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../../../../../components/ui/sidebar";
import { useSettingStore } from "../../../-lib/store/useSettings";
import { useSubModuleStore } from "../../../-lib/store/useSubModuleStore";
const iconMap: any = {
ShieldCheck: ShieldCheck,
AlignJustify: AlignJustify,
Settings: Settings,
Atom: Atom,
Logs: Logs,
Users: Users,
Webhook: Webhook,
Server: Server,
};
export function AdminSideBar() {
const { subModules } = useSubModuleStore();
const { settings } = useSettingStore();
const plantToken = settings.filter((n) => n.name === "plantToken");
const items = subModules.filter((m) => m.moduleName === "admin");
return (
<SidebarGroup>
<SidebarGroupLabel>Admin section</SidebarGroupLabel>
<SidebarGroupContent>
{items.map((item: any, index) => {
const Icon = iconMap[item.icon] || AlignJustify;
// drop down menu setup
return (
<SidebarMenu key={item.name}>
{item.link === "#" ? (
<Collapsible
key={item.name}
defaultOpen={index === 1}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<Icon />
{item.name}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.subSubModule?.length > 0 ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.subSubModule.map((i: any) => {
const SubIcon = iconMap[i.icon] || AlignJustify;
return (
<SidebarMenuSubItem key={i.name}>
{i.isActive && (
<SidebarMenuSubButton asChild>
<a
href={
i.name === "Swagger"
? `https://${plantToken[0].value}prod.alpla.net/application/swagger/index.html`
: i.link
}
target={i.newWindow ? "_blank" : "_self"}
>
<SubIcon />
<span>{i.name}</span>
</a>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
);
})}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
) : (
<SidebarMenu>
{items.map((item) => {
if (item.link === "#") return;
return (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<a href={item.link}>
<Icon />
<span>{item.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
)}
</SidebarMenu>
);
})}
</SidebarGroupContent>
</SidebarGroup>
);
}
{
/* <SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible
key={item.title}
defaultOpen={index === 1}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem
key={item.title}
>
{item.isActive && (
<SidebarMenuSubButton
asChild
>
<a
href={item.url}
target={
item.newWindow
? "_blank"
: "_self"
}
>
<item.icon />
<span>
{item.title}
</span>
</a>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu> */
}

View File

@@ -0,0 +1,40 @@
import { FileText } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../../../../../components/ui/sidebar";
const items = [
{
title: "End Of Month",
url: "/eom",
icon: FileText,
},
];
export function EomSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>End of month</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,116 @@
import { Forklift, Hourglass, Minus, Plus, Signature } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../../../../../../components/ui/collapsible";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../../../../../components/ui/sidebar";
const items = [
{
title: "Gemone",
url: "#",
icon: Forklift,
isActive: false,
},
];
const data = {
navMain: [
{
title: "Forklift Management",
url: "#",
icon: Forklift,
items: [
{
title: "All Forklifts",
url: "#",
icon: Forklift,
},
{
title: "Leasing data",
url: "#",
isActive: false,
icon: Signature,
},
{
title: "Forklift Hours",
url: "#",
isActive: false,
icon: Hourglass,
},
],
},
],
};
export function ForkliftSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Forklift Section</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible
key={item.title}
defaultOpen={index === 1}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem key={item.title}>
<SidebarMenuSubButton
asChild
isActive={item.isActive}
>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,33 @@
import { Link } from "@tanstack/react-router";
import {
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../../../../../components/ui/sidebar";
export function Header() {
return (
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<Link to="/old">
<SidebarMenuButton size="lg" asChild>
<div className="flex flex-row">
<img
src={"/lst/app/imgs/dkLst.png"}
alt="Description"
className="size-8"
/>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Logistics Support Tool</span>
</div>
</div>
</SidebarMenuButton>
</Link>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
);
}

View File

@@ -0,0 +1,65 @@
import { Barcode, Command, Cylinder, Package, Truck } from "lucide-react";
import type { UserRoles } from "@/lib/authClient";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../../../../../components/ui/sidebar";
import { useSubModuleStore } from "../../../-lib/store/useSubModuleStore";
import type { User } from "../../../-types/users";
import { hasPageAccess } from "../../../-utils/userAccess";
const iconMap: any = {
Package: Package,
Truck: Truck,
Cylinder: Cylinder,
Barcode: Barcode,
Command: Command,
};
export function LogisticsSideBar({
user,
userRoles,
}: {
user: User | null;
userRoles: UserRoles[] | null;
}) {
const { subModules } = useSubModuleStore();
const items = subModules?.filter((m) => m.moduleName === "logistics");
const userUpdate = { ...user, roles: userRoles };
return (
<SidebarGroup>
<SidebarGroupLabel>Logistics</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => {
const Icon = iconMap[item.icon];
return (
<SidebarMenuItem key={item.submodule_id}>
{hasPageAccess(
userUpdate as any,
item.roles,
item.moduleName,
) && (
<>
<SidebarMenuButton asChild>
<a href={item?.link}>
<Icon />
<span>{item?.name}</span>
</a>
</SidebarMenuButton>
</>
)}
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,62 @@
import { Printer, Tag } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../../../../../components/ui/sidebar";
import type { User } from "../../../-types/users";
import { hasPageAccess } from "../../../-utils/userAccess";
export function ProductionSideBar({
user,
moduleID,
}: {
user: User | null;
moduleID: string;
}) {
const url: string = window.location.host.split(":")[0];
const items = [
{
title: "One Click Print",
url: "/lst/app/old/ocp",
icon: Printer,
role: ["viewer"],
module: "ocp",
active: true,
},
{
title: "Rfid Readers",
url: "/lst/app/old/rfid",
icon: Tag,
role: ["viewer"],
module: "production",
active: url === "usday1vms006" || url === "localhost" ? true : false,
},
];
return (
<SidebarGroup>
<SidebarGroupLabel>Production</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<>
{hasPageAccess(user, item.role, moduleID) && item.active && (
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
)}
</>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,39 @@
import { Printer } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../../../../../components/ui/sidebar";
const items = [
{
title: "Qaulity Request",
url: "#",
icon: Printer,
},
];
export function QualitySideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Quality</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,156 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "../../../../../../components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../../../../components/ui/dialog";
import { useAppForm } from "../../../../../../lib/formStuff";
import { getMachineConnected } from "../../../-utils/querys/logistics/machineConnected";
import { getMachineNotConnected } from "../../../-utils/querys/logistics/notConnected";
export function AttachSilo(props: any) {
const [open, setOpen] = useState(false);
const { data, isError, isLoading, refetch } = useQuery(
getMachineNotConnected({
siloID: props.silo.LocationID,
connectionType: "detached",
}),
);
const { refetch: attached } = useQuery(
getMachineConnected({
siloID: props.silo.LocationID,
connectionType: "connected",
}),
);
const form = useAppForm({
defaultValues: {
laneId: props.silo.LocationID,
productionLotId: "",
machineId: "",
},
onSubmit: async ({ value }) => {
try {
const res = await axios.post(
"/lst/old/api/logistics/attachsilo",
value,
);
if (res.data.success) {
toast.success(res.data.message);
refetch();
attached();
form.reset();
setOpen(!open);
} else {
console.log(res.data);
toast.error(res.data.message);
refetch();
form.reset();
setOpen(!open);
}
} catch (error) {
console.log(error);
toast.error(
"There was an error attaching the silo please try again, if persist please enter a helpdesk ticket.",
);
}
},
});
if (isError)
return (
<div>
<p>There was an error loading data</p>
</div>
);
if (isLoading)
return (
<div>
<p>Loading....</p>
</div>
);
// convert the array that comes over to label and value
const tranMachine = data.map((i: any) => ({
value: i.machineId.toString(),
label: i.name,
}));
return (
<Dialog open={open}>
<DialogTrigger asChild>
<Button variant="outline" onClick={() => setOpen(!open)}>
Attach Silo
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<DialogHeader>
<DialogTitle>Attach silo for: {props.silo.Description}</DialogTitle>
<DialogDescription>
Enter the new lotnumber, select the machine you would like to
attach.
</DialogDescription>
</DialogHeader>
<div>
<p className="text-sm">
NOTE: If the machine you are trying to attach is not showing in
the drop down this means it is already attached to this silo.
</p>
</div>
<div className="mt-3">
<form.AppField
name="productionLotId"
children={(field) => (
<field.InputField
label="Lot Number"
inputType="number"
required={true}
/>
)}
/>
</div>
<div className="mt-2">
<form.AppField
name="machineId"
children={(field) => (
<field.SelectField
label="Select Machine"
options={tranMachine}
/>
)}
/>
</div>
<DialogFooter className="mt-2">
<DialogClose asChild>
<Button variant="outline" onClick={() => setOpen(!open)}>
Cancel
</Button>
</DialogClose>
<form.AppForm>
<form.SubmitButton>Attach</form.SubmitButton>
</form.AppForm>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,106 @@
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import { CardContent } from "../../../../../../components/ui/card";
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "../../../../../../components/ui/chart";
import { LstCard } from "../../../../../../components/ui/lstCard";
import { getAdjustments } from "../../../-utils/querys/logistics/siloAdjustments/getAdjustments";
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,103 @@
import { useForm } from "@tanstack/react-form";
import { useRouter } from "@tanstack/react-router";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { LstCard } from "../../extendedUi/LstCard";
export default function Comment(data: any) {
const token = localStorage.getItem("auth_token");
const [isSubmitting, setIsSubmitting] = useState(false);
const router = useRouter();
const form = useForm({
defaultValues: {
comment: "",
},
onSubmit: async ({ value }) => {
setIsSubmitting(true);
try {
const res = await axios.post(
`/lst/old/api/logistics/postcomment/${data.id.split("&")[0]}`,
{
comment: value.comment,
key: data.id.split("&")[1].replace("amp;", ""),
},
{ headers: { Authorization: `Bearer ${token}` } },
);
if (res.data.success) {
toast.success(res.data.message);
form.reset();
router.navigate({ to: "/old/siloAdjustments" });
}
if (!res.data.success) {
toast.error(res.data.message);
form.reset();
}
} catch (error) {
console.log(error);
toast.error(`There was an error posting your comment.`);
}
setIsSubmitting(false);
},
});
return (
<div className="">
<LstCard>
<CardHeader>Please enter your comment for the silo adjust</CardHeader>
<CardContent>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field
name="comment"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 10
? undefined
: "Comment must be longer than 10 characters.",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor="comment" className="mb-2">
Comment
</Label>
<Textarea
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
//type="number"
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em>
) : null}
</div>
);
}}
/>
</form>
</CardContent>
<CardFooter>
<div className="flex justify-end">
<Button onClick={form.handleSubmit} disabled={isSubmitting}>
Submit
</Button>
</div>
</CardFooter>
</LstCard>
</div>
);
}

View File

@@ -0,0 +1,144 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "../../../../../../components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../../../../components/ui/dialog";
import { useAppForm } from "../../../../../../lib/formStuff";
import { getMachineConnected } from "../../../-utils/querys/logistics/machineConnected";
import { getMachineNotConnected } from "../../../-utils/querys/logistics/notConnected";
export function DetachSilo(props: any) {
const [open, setOpen] = useState(false);
const { data, isError, isLoading, refetch } = useQuery(
getMachineConnected({
siloID: props.silo.LocationID,
connectionType: "connected",
}),
);
const { refetch: notConnected } = useQuery(
getMachineNotConnected({
siloID: props.silo.LocationID,
connectionType: "detached",
}),
);
const form = useAppForm({
defaultValues: {
laneId: props.silo.LocationID,
machineId: 0,
},
onSubmit: async ({ value }) => {
try {
const res = await axios.post(
"/lst/old/api/logistics/detachsilo",
value,
);
if (res.status === 200) {
console.log(res.data.data);
toast.success(res.data.message);
refetch();
notConnected();
form.reset();
setOpen(!open);
} else {
console.log(res.data);
toast.error(res.data.message);
refetch();
form.reset();
setOpen(!open);
}
} catch (error) {
console.log(error);
toast.error(
"There was an error detaching the silo please try again, if persist please enter a helpdesk ticket.",
);
}
},
});
if (isError)
return (
<div>
<p>There was an error loading data</p>
</div>
);
if (isLoading)
return (
<div>
<p>Loading....</p>
</div>
);
// convert the array that comes over to label and value
const tranMachine = data.map((i: any) => ({
value: i.machineId.toString(),
label: i.name,
}));
return (
<Dialog open={open}>
<DialogTrigger asChild>
<Button
variant="outline"
onClick={() => setOpen(!open)}
disabled={data.length === 0}
>
Detach Silo
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<DialogHeader>
<DialogTitle>Attach silo for: {props.silo.Description}</DialogTitle>
<DialogDescription>
Select the machine you would like to detach.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4">
<div className="grid gap-3">
<div className="mt-2">
<form.AppField
name="machineId"
children={(field) => (
<field.SelectField
label="Select Machine"
options={tranMachine}
/>
)}
/>
</div>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline" onClick={() => setOpen(!open)}>
Cancel
</Button>
</DialogClose>
<form.AppForm>
<form.SubmitButton>Detach</form.SubmitButton>
</form.AppForm>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,26 @@
import { useQuery } from "@tanstack/react-query";
import { getAdjustments } from "../../../-utils/querys/logistics/siloAdjustments/getAdjustments";
import { columns } from "../../../-utils/tableData/siloAdjustmentHist/siloAdjHistColumns";
import { SiloTable } from "../../../-utils/tableData/siloAdjustmentHist/siloData";
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),
);
return (
<div className="container mx-auto py-10">
<SiloTable columns={columns} data={adjustments} />
</div>
);
}

View File

@@ -0,0 +1,224 @@
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 { Button } from "../../../../../../components/ui/button";
import { CardHeader } from "../../../../../../components/ui/card";
import { Input } from "../../../../../../components/ui/input";
import { Label } from "../../../../../../components/ui/label";
import { LstCard } from "../../../../../../components/ui/lstCard";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "../../../../../../components/ui/tooltip";
import { useAuth, userAccess } from "../../../../../../lib/authClient";
import { getStockSilo } from "../../../-utils/querys/logistics/siloAdjustments/getStockSilo";
import { AttachSilo } from "./AttachSilo";
import ChartData from "./ChartData";
import { DetachSilo } from "./DetachSilo";
export default function SiloCard(data: any) {
const [submitting, setSubmitting] = useState(false);
const { refetch } = useQuery(getStockSilo());
const { session } = useAuth();
const silo = data.silo;
// roles that can do the silo adjustments
const form = useForm({
defaultValues: {
newLevel: "",
},
onSubmit: async ({ value }) => {
setSubmitting(true);
const dataToSubmit = {
quantity: parseFloat(value.newLevel),
warehouseId: silo.WarehouseID,
laneId: silo.LocationID,
};
try {
const res = await axios.post(
"/lst/old/api/logistics/createsiloadjustment",
dataToSubmit,
{ withCredentials: true },
);
//console.log(res.data);
if (res.data.success) {
toast.success(res.data.message);
refetch();
form.reset();
}
if (!res.data.success && res.data.data?.status === 400) {
if (res.data.data.status === 400) {
toast.error(res.data.data.data.errors[0].message);
}
} else if (!res.data.success) {
toast.error(res.data.message);
}
setSubmitting(false);
} catch (error: any) {
console.log(error);
if (error.status === 401) {
toast.error(error.response.statusText);
setSubmitting(false);
}
}
},
});
return (
<LstCard>
<div className="flex flex-row">
<LstCard className="grow m-1 max-w-[400px]">
<CardHeader>{silo.Description}</CardHeader>
<div className="m-1">
<hr className="m-2" />
<span>Current Stock: </span>
{silo.Stock_Total}
<hr className="m-2" />
<span>Last date adjusted </span>
{format(silo.LastAdjustment, "M/dd/yyyy")}
<hr className="m-2" />
</div>
<div>
{silo.Stock_Total === 0 ? (
<div className="flex justify-center flex-col">
<span>
The silo is currently empty you will not be able to do an
adjustment until you have received material in.
</span>
<hr />
<ul>
<li>
-Someone click "Take inventory on a empty location" in
stock.
</li>
<li>
-Silo virtualy ran empty due to production over consumption.
</li>
<li>
-Someone forgot to move a railcar compartment over to this
location.
</li>
</ul>
</div>
) : (
<>
{session?.user &&
userAccess("logistics", [
"supervisor",
"manager",
"admin",
"systemAdmin",
]) && (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field
name="newLevel"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 1
? undefined
: "You must enter a value greate than 1",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<div className="flex flex-row">
<Label htmlFor="newLevel">New level</Label>
<div>
<Disclaimer />
</div>
</div>
<div className="flex flex-row">
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
type="decimal"
onChange={(e) =>
field.handleChange(e.target.value)
}
/>
<Button
className="ml-1"
variant="outline"
type="submit"
onClick={form.handleSubmit}
disabled={submitting}
>
{submitting ? (
<span className="w-24">Submitting...</span>
) : (
<span className="w-24">Submit</span>
)}
</Button>
</div>
{field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em>
) : null}
</div>
);
}}
/>
</form>
)}
</>
)}
</div>
</LstCard>
<div className="grow max-w-[600px]">
<ChartData laneId={silo.LocationID} />
<div className="flex justify-end m-1 gap-3">
<AttachSilo silo={silo} />
<DetachSilo silo={silo} />
<Button variant="outline">
<Link
to={"/old/siloAdjustments/$hist"}
params={{ hist: silo.LocationID }}
>
Historical Data
</Link>
</Button>
</div>
</div>
</div>
</LstCard>
);
}
const Disclaimer = () => {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<CircleAlert className="ml-1 w-[14px]" />
</TooltipTrigger>
<TooltipContent className="max-w-48">
<p className="text-pretty">
If you have had this page open for a period of time before
submitting your data, there is a chance that the stock levels will
be different from the ones you see above
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};

View File

@@ -0,0 +1,30 @@
import { useQuery } from "@tanstack/react-query";
import { getStockSilo } from "../../../-utils/querys/logistics/siloAdjustments/getStockSilo";
import SiloCard from "./SiloCard";
export default function SiloPage() {
const { data, isError, error, isLoading } = useQuery(getStockSilo());
if (isLoading) return;
if (isError) return;
if (error)
return (
<div>
{" "}
There was an error getting the silos please notify your admin if this
continues to be an issue
</div>
);
return (
<div className="flex flex-wrap">
{data?.map((s: any) => (
<div key={s.LocationID} className="grow m-2 max-w-[800px]">
<SiloCard silo={s} />
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,153 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useForm } from "@tanstack/react-form";
import axios from "axios";
import { format } from "date-fns";
import { useState } from "react";
import { toast } from "sonner";
export default function ExportInventoryData() {
const [open, setOpen] = useState(false);
const [saving, setSaving] = useState(false);
const form = useForm({
defaultValues: {
age: "",
},
onSubmit: async ({ value }) => {
setSaving(true);
try {
const res = await axios.get(
`/api/logistics/getcyclecount?age=${value.age}`,
{
responseType: "blob",
}
);
const blob = new Blob([res.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `CycleCount-${format(new Date(Date.now()), "M-d-yyyy")}.xlsx`; // You can make this dynamic
document.body.appendChild(link);
link.click();
// Clean up
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
toast.success(`File Downloaded`);
setSaving(false);
setOpen(false);
form.reset();
} catch (error) {
console.log(error);
console.log(`There was an error getting cycle counts.`);
}
},
});
return (
<div>
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!open) {
form.reset();
}
setOpen(isOpen);
// toast.message("Model was something", {
// description: isOpen ? "Modal is open" : "Modal is closed",
// });
}}
>
<DialogTrigger asChild>
<Button variant="outline">Export Inventory Check</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Export Inventory lane check</DialogTitle>
<DialogDescription>
Exports all lanes based on the age you enter, except
empty lanes.
</DialogDescription>
</DialogHeader>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<div>
<>
<form.Field
name="age"
// validators={{
// // We can choose between form-wide and field-specific validators
// onChange: ({ value }) =>
// value.length > 3
// ? undefined
// : "Username must be longer than 3 letters",
// }}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2 flex flex-row">
<Label htmlFor="active">
Age
</Label>
<Input
className="ml-2"
name={field.name}
onBlur={field.handleBlur}
type="number"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
</div>
);
}}
/>
</>
</div>
<DialogFooter>
<div className="flex justify-end mt-2">
<Button onClick={() => setOpen(false)}>
Close
</Button>
<Button
type="submit"
disabled={saving}
onClick={form.handleSubmit}
>
{saving ? (
<>
<span>Saving....</span>
</>
) : (
<span>Save setting</span>
)}
</Button>
</div>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -0,0 +1,40 @@
import { useQuery } from "@tanstack/react-query";
import { getinventoryCheck } from "../../../-utils/querys/logistics/getInventoryCheck";
import { invColumns } from "../../../-utils/tableData/InventoryCards/inventoryColumns";
import { InvTable } from "../../../-utils/tableData/InventoryCards/inventoryData";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function INVCheckCard(props: any) {
const { age, rowType } = props.data;
//console.log(props.data);
const { data, isError, isLoading } = useQuery(
getinventoryCheck({ age: age }),
);
if (isLoading) return <div>Loading inventory data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the inv.</p>
</div>
);
}
let laneData: any = data;
if (props.type != "") {
laneData = laneData.filter((l: any) => l.rowType === rowType.toUpperCase());
// age
laneData = laneData.filter((l: any) => l.DaysSinceLast >= age);
}
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return <InvTable columns={invColumns} data={laneData} info={props.data} />;
}

View File

@@ -0,0 +1,62 @@
//import { LstCard } from "@/components/extendedUI/LstCard";
import { useQuery } from "@tanstack/react-query";
import { getPPOO } from "../../../-utils/querys/logistics/getPPOO";
import { columns } from "../../../-utils/tableData/ppoo/ppooColumns";
import { PPOOTable } from "../../../-utils/tableData/ppoo/ppooData";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function PPOO() {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getPPOO());
if (isLoading) return <div>Loading adjustmnet data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the adjustments.</p>
</div>
);
}
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return (
<PPOOTable
columns={columns}
data={data}
//style={style}
/>
);
// return (
// <div style={style}>
// <LstCard style={style}>
// <Suspense fallback={<p>Loading PPOO...</p>}>
// <div className={`flex justify-center`}>
// <p
// className={`drag-handle w-fit`}
// style={{ cursor: "move", padding: "5px" }}
// >
// PPOO
// </p>
// <button onClick={handleCloseCard}>
// <CircleX />
// </button>
// </div>
// <PPOOTable
// columns={columns}
// data={data}
// //style={style}
// />
// </Suspense>
// </LstCard>
// </div>
// );
}

View File

@@ -0,0 +1,30 @@
import { useQuery } from "@tanstack/react-query";
import { getOpenOrders } from "../../../-utils/querys/logistics/getOpenOrders";
import { openOrderColumns } from "../../../-utils/tableData/openorders/ooColumns";
import { OpenOrderTable } from "../../../-utils/tableData/openorders/ooData";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function OpenOrders() {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getOpenOrders());
if (isLoading) return <div>Loading openOrder data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the openorders.</p>
</div>
);
}
let openOrders: any = data;
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return <OpenOrderTable columns={openOrderColumns} data={openOrders} />;
}