feat(dm): added import and exports plus first custom for dayton
This commit is contained in:
80
frontend/src/components/logistics/dm/DMButtons.tsx
Normal file
80
frontend/src/components/logistics/dm/DMButtons.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
66
frontend/src/components/logistics/dm/ForecastImport.tsx
Normal file
66
frontend/src/components/logistics/dm/ForecastImport.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
66
frontend/src/components/logistics/dm/OrderImport.tsx
Normal file
66
frontend/src/components/logistics/dm/OrderImport.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
129
frontend/src/components/logistics/dm/dmPage.tsx
Normal file
129
frontend/src/components/logistics/dm/dmPage.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user