feat(dm): added import and exports plus first custom for dayton

This commit is contained in:
2025-04-21 21:02:44 -05:00
parent 06d2f1464b
commit 8422955d39
9 changed files with 440 additions and 4 deletions

View File

@@ -0,0 +1,80 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import OrderImport from "./OrderImport";
import StandardOrderTemplate from "./StandardOrderTemplate";
import { Button } from "@/components/ui/button";
import StandardForecastTemplate from "./StandardForecastTemplate";
import ForecastImport from "./ForecastImport";
import { useSettingStore } from "@/lib/store/useSettings";
export default function DMButtons() {
const { settings } = useSettingStore();
const plantToken = settings.filter((n) => n.name === "plantToken");
console.log(plantToken);
return (
<div className="flex flex-row-reverse">
<div className="m-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button>Standard DM</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>
Standard templates and imports
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<StandardOrderTemplate />
</DropdownMenuItem>
<DropdownMenuItem>
<OrderImport
fileType={"standard"}
name={"Standard Order Import"}
/>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<StandardForecastTemplate />
</DropdownMenuItem>
<DropdownMenuItem>
<ForecastImport
fileType={"standard"}
name={"Standard Forecast Import"}
/>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{plantToken[0]?.value === "usday1" && (
<div className="m-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button>Dayton Customs</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>
Custom import templates
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<OrderImport
fileType={"abbott"}
name={"Abbott truck list"}
/>
</DropdownMenuItem>
<DropdownMenuSeparator />
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,66 @@
import { Button } from "@/components/ui/button";
import axios from "axios";
import { useRef, useState } from "react";
import { toast } from "sonner";
export default function ForecastImport(props: any) {
const fileInputRef: any = useRef(null);
const [posting, setPosting] = useState(false);
const token = localStorage.getItem("auth_token");
//const [fileType, setFileType] = useState("");
const importOrders = async (e: any) => {
const file = e.target.files[0];
if (!file) {
toast.error("Missing file please try again");
setPosting(false);
return;
}
// create the form data with the correct fileType
const formData = new FormData();
formData.append("postOrders", e.target.files[0]);
formData.append("fileType", props.fileType); // extra field
// console.log(formData);
try {
const response = await axios.post(
"/api/logistics/postforecastin",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`,
},
}
);
console.log("Upload successful:", response.data);
toast.success(
"File Uploaded, please validate processing in alplaprod 2.0"
);
setPosting(false);
} catch (error) {
console.log(error);
toast.error("Upload failed");
}
setPosting(false);
};
const handleButtonClick = () => {
setPosting(true);
fileInputRef.current.click();
};
return (
<div>
<Button onClick={handleButtonClick} disabled={posting}>
{props.name}
</Button>
<input
type="file"
accept=".xlsx, .xls, .xlsm"
ref={fileInputRef}
style={{ display: "none" }}
onChange={importOrders}
/>
</div>
);
}

View File

@@ -0,0 +1,66 @@
import { Button } from "@/components/ui/button";
import axios from "axios";
import { useRef, useState } from "react";
import { toast } from "sonner";
export default function OrderImport(props: any) {
const fileInputRef: any = useRef(null);
const [posting, setPosting] = useState(false);
const token = localStorage.getItem("auth_token");
//const [fileType, setFileType] = useState("");
const importOrders = async (e: any) => {
const file = e.target.files[0];
if (!file) {
toast.error("Missing file please try again");
setPosting(false);
return;
}
// create the form data with the correct fileType
const formData = new FormData();
formData.append("postOrders", e.target.files[0]);
formData.append("fileType", props.fileType); // extra field
// console.log(formData);
try {
const response = await axios.post(
"/api/logistics/postbulkorders",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`,
},
}
);
console.log("Upload successful:", response.data);
toast.success(
"File Uploaded, please validate processing in alplaprod 2.0"
);
setPosting(false);
} catch (error) {
console.log(error);
toast.error("Upload failed");
}
setPosting(false);
};
const handleButtonClick = () => {
setPosting(true);
fileInputRef.current.click();
};
return (
<div>
<Button onClick={handleButtonClick} disabled={posting}>
{props.name}
</Button>
<input
type="file"
accept=".xlsx, .xls, .xlsm"
ref={fileInputRef}
style={{ display: "none" }}
onChange={importOrders}
/>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { Button } from "@/components/ui/button";
import axios from "axios";
import { format } from "date-fns";
import { useState } from "react";
import { toast } from "sonner";
export default function StandardForecastTemplate() {
const [template, setTemplate] = useState(false);
const getTemplate = async () => {
setTemplate(true);
try {
const res = await axios.get(`/api/logistics/bulkforcasttemplate`, {
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 = `ForecastTemplate-${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(`Forecast template`);
setTemplate(false);
} catch (error) {
setTemplate(false);
toast.error("There was an error getting the template");
}
};
return (
<Button onClick={getTemplate} disabled={template}>
Standard Forecast Template
</Button>
);
}

View File

@@ -0,0 +1,41 @@
import { Button } from "@/components/ui/button";
import axios from "axios";
import { format } from "date-fns";
import { useState } from "react";
import { toast } from "sonner";
export default function StandardOrderTemplate() {
const [template, setTemplate] = useState(false);
const getTemplate = async () => {
setTemplate(true);
try {
const res = await axios.get(`/api/logistics/bulkorderstemplate`, {
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 = `BulkOrderTemplate-${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(`Bulk Order template`);
setTemplate(false);
} catch (error) {
setTemplate(false);
toast.error("There was an error getting the tempalte");
}
};
return (
<Button onClick={getTemplate} disabled={template}>
Standard Order Template
</Button>
);
}

View File

@@ -0,0 +1,129 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Separator } from "@/components/ui/separator";
export default function DmPage() {
return (
<div className="flex flex-row gap-2">
<LstCard className="w-1/2">
<div className="w-96">
<h4 className="text-center underline text-2xl">
Simple instructions for creating/updating orders
</h4>
<Separator />
</div>
<ul className="list-disc mr-2">
<li>
Download the standard template if you have not yet done
so, top right click standard, then template.
</li>
<li>
Add in the orders like you see in the example below.
</li>
<li>
When updating orders you are required to have the
customerOrderNumber, customerLineNumber, and
customerReleaseNumber. Quatity and dates can change.
</li>
<li>
Once you have all the orders enters click the upload
button on the top right
</li>
</ul>
<Separator className="my-4" />
<div className="m-5">
<h4 className="text-center underline text-2xl">
Some notes to consider
</h4>
<ul className="list-disc mr-2">
<li>
No longer need to add in the invoice id, we take the
default one.
</li>
<li>
You can have as many customers you want in the file
and in any order.
</li>
<li>
Orders created Manually, can not be updated with
this process.
</li>
<li>
If you desire you can send up to 1000 orders at one
time... or more....
</li>
<li>
Customer mappings can be added, this means that you
can upload your customer file and it will parse
onces mapped, you need to request this.
</li>
<li>
Custom imports will be at the top right under
custom, here you will just upload the customer file
once mapped.
</li>
</ul>
</div>
<Separator className="my-4" />
<p className="text-center underline text-2xl">Example order</p>
<div className="flex justify-center">
<img
src="/imgs/ordersInExample.png"
alt="orders in example"
className="w-[680px] h-[280px]"
/>
</div>
</LstCard>
<LstCard className="w-1/2">
<div className="w-96">
<h4 className="text-center underline text-2xl">
Simple instructions for creating forecast
</h4>
<Separator className="my-4" />
</div>
<div className="m-5">
<ul className="list-disc mr-2">
<li>
Download the template if you have not yet done so.
</li>
<li>
Add in the forecast like you see in the example
below.
</li>
<li>
Once you have all the forecast enters click the
upload button on the top right
</li>
</ul>
</div>
<Separator className="my-4" />
<div className="m-5">
<h4 className="text-center underline text-2xl">
Some notes to consider.
</h4>
<ul className="list-disc mr-2">
<li>You can only use one customer at a time.</li>
<li>
If you desire you can send up to 1000 orders at one
time...
</li>
</ul>
</div>
<Separator className="my-4" />
<p className="text-center underline text-2xl">
Example forecast
</p>
<div className="flex justify-center">
<img
src="/imgs/exampleforecast.png"
alt="orders in example"
className="w-[480px] h-[280px]"
/>
</div>
</LstCard>
</div>
);
}

View File

@@ -1,3 +1,4 @@
import DmPage from "@/components/logistics/dm/dmPage";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/(logistics)/dm/")({
@@ -19,5 +20,9 @@ export const Route = createFileRoute("/(logistics)/dm/")({
});
function RouteComponent() {
return <div>Hello "/(logistics)/dm/"!</div>;
return (
<div>
<DmPage />
</div>
);
}

View File

@@ -27,6 +27,7 @@ import { useSession } from "@/hooks/useSession";
import { useLogout } from "@/hooks/useLogout";
import ExportInventoryData from "@/components/logistics/warehouse/ExportInventoryData";
import { AddCards } from "@/components/dashboard/AddCards";
import DMButtons from "@/components/logistics/dm/DMButtons";
//import { AddCards } from "@/components/dashboard/AddCards";
// same as the layout
@@ -44,12 +45,19 @@ export const Route = createRootRoute({
<ThemeProvider>
<nav className="flex justify-end w-full shadow ">
<div className="m-2 flex flex-row">
{/* Inventory section */}
{location.pathname === "/" && (
<div className="m-auto pr-2 flex flex-row gap-2">
<ExportInventoryData />
<AddCards />
</div>
)}
{/* Demand mgt section this should also include plant token stuff */}
{location.pathname === "/dm" && (
<div className="m-auto pr-2 flex flex-row gap-2">
<DMButtons />
</div>
)}
<div className="m-1">
<ModeToggle />
</div>