Compare commits
22 Commits
4e1f7d4040
...
97cf624440
| Author | SHA1 | Date | |
|---|---|---|---|
| 97cf624440 | |||
| e0df6b8cd7 | |||
| c248cc0129 | |||
| 57d3727f49 | |||
| 5a48dcf553 | |||
| eac7444038 | |||
| b290b21ca2 | |||
| 86905b591b | |||
| 28b050859a | |||
| 3c9e627021 | |||
| 7152e72822 | |||
| a03130a961 | |||
| aea06c12a0 | |||
| 4f1d83137b | |||
| 72a3292633 | |||
| 174a6c0adc | |||
| 556aaa381d | |||
| 5f7915b81f | |||
| 613a486160 | |||
| caccaf143c | |||
| 2a7eb26e6b | |||
| 844f337f6c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -150,3 +150,4 @@ dist
|
|||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
backend-0.1.3.zip
|
backend-0.1.3.zip
|
||||||
|
BulkForecastTemplate
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# All CHanges to LST can be found below.
|
# All CHanges to LST can be found below.
|
||||||
|
|
||||||
|
### [2.16.1](https://git.tuffraid.net/cowch/lstV2/compare/v2.16.0...v2.16.1) (2025-04-23)
|
||||||
|
|
||||||
## [2.16.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.15.0...v2.16.0) (2025-04-22)
|
## [2.16.0](https://git.tuffraid.net/cowch/lstV2/compare/v2.15.0...v2.16.0) (2025-04-22)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
213
frontend/src/components/auth/Register.tsx
Normal file
213
frontend/src/components/auth/Register.tsx
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import { LstCard } from "../extendedUI/LstCard";
|
||||||
|
import { CardHeader } from "../ui/card";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { useForm } from "@tanstack/react-form";
|
||||||
|
import { Separator } from "../ui/separator";
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default function RegisterForm() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [registering, setRegistering] = useState(false);
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
email: "",
|
||||||
|
},
|
||||||
|
onSubmit: async ({ value }) => {
|
||||||
|
setRegistering(true);
|
||||||
|
try {
|
||||||
|
const res = await axios.post("/api/auth/register", value);
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
navigate({ to: "/login" });
|
||||||
|
form.reset();
|
||||||
|
toast.success(
|
||||||
|
`${value.username} was just created please login`
|
||||||
|
);
|
||||||
|
setRegistering(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.data.success) {
|
||||||
|
toast.error(res.data.message);
|
||||||
|
setRegistering(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//console.log(error);
|
||||||
|
toast.error("There was an error registering");
|
||||||
|
setRegistering(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ml-[25%]">
|
||||||
|
<LstCard className="p-3 w-96">
|
||||||
|
<CardHeader>
|
||||||
|
<div>
|
||||||
|
<p className="text-2xl">Login to register</p>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<hr className="rounded"></hr>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Separator />
|
||||||
|
<form.Field
|
||||||
|
name="username"
|
||||||
|
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">
|
||||||
|
<Label htmlFor="username" className="mb-2">
|
||||||
|
Username
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Separator />
|
||||||
|
<form.Field
|
||||||
|
name="email"
|
||||||
|
validators={{
|
||||||
|
// We can choose between form-wide and field-specific validators
|
||||||
|
onChange: ({ value }) =>
|
||||||
|
value.length > 3
|
||||||
|
? undefined
|
||||||
|
: "You must enter a valid email",
|
||||||
|
}}
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||||
|
<Label htmlFor="email" className="mb-2">
|
||||||
|
Alpla Email
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Separator className="m-2" />
|
||||||
|
<p>
|
||||||
|
Your password Should be your windows password, as this
|
||||||
|
is how you will interact with alplaprod
|
||||||
|
</p>
|
||||||
|
<form.Field
|
||||||
|
name="password"
|
||||||
|
// We can choose between form-wide and field-specific validators
|
||||||
|
validators={{
|
||||||
|
onChangeAsyncDebounceMs: 500,
|
||||||
|
onChangeAsync: ({ value }) => {
|
||||||
|
// if (
|
||||||
|
// window.location.pathname.includes(
|
||||||
|
// "/users"
|
||||||
|
// ) &&
|
||||||
|
// value.length === 0
|
||||||
|
// ) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
if (value.length < 4) {
|
||||||
|
return "Password must be at least 4 characters long.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[A-Z]/.test(value)) {
|
||||||
|
return "Password must contain at least one uppercase letter.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[a-z]/.test(value)) {
|
||||||
|
return "Password must contain at least one lower case letter.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[0-9]/.test(value)) {
|
||||||
|
return "Password must contain at least one number.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "Password must contain at least one special character.";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||||
|
<Label htmlFor="password1" className="mb-2">
|
||||||
|
Password
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
name={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
type="password"
|
||||||
|
onChange={(e) =>
|
||||||
|
field.handleChange(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{field.state.meta.errors.length ? (
|
||||||
|
<em>
|
||||||
|
{field.state.meta.errors.join(",")}
|
||||||
|
</em>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="mt-4 ml-4 flex justify-end">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
onClick={form.handleSubmit}
|
||||||
|
disabled={registering}
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</LstCard>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,78 +1,23 @@
|
|||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import OrderImport from "./OrderImport";
|
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";
|
import { useSettingStore } from "@/lib/store/useSettings";
|
||||||
|
|
||||||
export default function DMButtons() {
|
export default function DMButtons() {
|
||||||
const { settings } = useSettingStore();
|
const { settings } = useSettingStore();
|
||||||
|
|
||||||
const plantToken = settings.filter((n) => n.name === "plantToken");
|
const plantToken = settings.filter((n) => n.name === "plantToken");
|
||||||
console.log(plantToken);
|
//console.log(plantToken);
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row-reverse">
|
<div className="flex flex-row-reverse gap-1">
|
||||||
<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" && (
|
{plantToken[0]?.value === "usday1" && (
|
||||||
<div className="m-2">
|
<div className="flex flex-row gap-2">
|
||||||
<DropdownMenu>
|
<OrderImport
|
||||||
<DropdownMenuTrigger asChild>
|
fileType={"abbott"}
|
||||||
<Button>Dayton Customs</Button>
|
name={"Abbott truck list"}
|
||||||
</DropdownMenuTrigger>
|
/>
|
||||||
<DropdownMenuContent>
|
<OrderImport
|
||||||
<DropdownMenuLabel>
|
fileType={"energizer"}
|
||||||
Custom import templates
|
name={"Energizer Truck List"}
|
||||||
</DropdownMenuLabel>
|
/>
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<DropdownMenuItem>
|
|
||||||
<OrderImport
|
|
||||||
fileType={"abbott"}
|
|
||||||
name={"Abbott truck list"}
|
|
||||||
/>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function ForecastImport(props: any) {
|
|||||||
|
|
||||||
// create the form data with the correct fileType
|
// create the form data with the correct fileType
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("postOrders", e.target.files[0]);
|
formData.append("postForecast", e.target.files[0]);
|
||||||
formData.append("fileType", props.fileType); // extra field
|
formData.append("fileType", props.fileType); // extra field
|
||||||
// console.log(formData);
|
// console.log(formData);
|
||||||
|
|
||||||
@@ -33,10 +33,13 @@ export default function ForecastImport(props: any) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("Upload successful:", response.data);
|
//console.log("Upload successful:", response.data);
|
||||||
toast.success(
|
toast.success(response?.data?.message);
|
||||||
"File Uploaded, please validate processing in alplaprod 2.0"
|
fileInputRef.current.value = null;
|
||||||
);
|
setPosting(false);
|
||||||
|
// toast.success(
|
||||||
|
// "File Uploaded, please validate processing in alplaprod 2.0"
|
||||||
|
// );
|
||||||
setPosting(false);
|
setPosting(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export default function OrderImport(props: any) {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("postOrders", e.target.files[0]);
|
formData.append("postOrders", e.target.files[0]);
|
||||||
formData.append("fileType", props.fileType); // extra field
|
formData.append("fileType", props.fileType); // extra field
|
||||||
// console.log(formData);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
@@ -33,10 +32,9 @@ export default function OrderImport(props: any) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log("Upload successful:", response.data);
|
//console.log("Upload successful:", response.data);
|
||||||
toast.success(
|
toast.success(response?.data?.message);
|
||||||
"File Uploaded, please validate processing in alplaprod 2.0"
|
fileInputRef.current.value = null;
|
||||||
);
|
|
||||||
setPosting(false);
|
setPosting(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { LstCard } from "@/components/extendedUI/LstCard";
|
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import OrderImport from "./OrderImport";
|
||||||
|
import StandardOrderTemplate from "./StandardOrderTemplate";
|
||||||
|
import ForecastImport from "./ForecastImport";
|
||||||
|
import StandardForecastTemplate from "./StandardForecastTemplate";
|
||||||
|
|
||||||
export default function DmPage() {
|
export default function DmPage() {
|
||||||
return (
|
return (
|
||||||
@@ -9,6 +13,13 @@ export default function DmPage() {
|
|||||||
<h4 className="text-center underline text-2xl">
|
<h4 className="text-center underline text-2xl">
|
||||||
Simple instructions for creating/updating orders
|
Simple instructions for creating/updating orders
|
||||||
</h4>
|
</h4>
|
||||||
|
<div className="flex flex-row gap-3 m-1">
|
||||||
|
<OrderImport
|
||||||
|
fileType={"standard"}
|
||||||
|
name={"Standard Order Import"}
|
||||||
|
/>
|
||||||
|
<StandardOrderTemplate />
|
||||||
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -36,6 +47,7 @@ export default function DmPage() {
|
|||||||
<h4 className="text-center underline text-2xl">
|
<h4 className="text-center underline text-2xl">
|
||||||
Some notes to consider
|
Some notes to consider
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<ul className="list-disc mr-2">
|
<ul className="list-disc mr-2">
|
||||||
<li>
|
<li>
|
||||||
No longer need to add in the invoice id, we take the
|
No longer need to add in the invoice id, we take the
|
||||||
@@ -80,7 +92,14 @@ export default function DmPage() {
|
|||||||
<h4 className="text-center underline text-2xl">
|
<h4 className="text-center underline text-2xl">
|
||||||
Simple instructions for creating forecast
|
Simple instructions for creating forecast
|
||||||
</h4>
|
</h4>
|
||||||
<Separator className="my-4" />
|
<div className="flex flex-row gap-3 m-1">
|
||||||
|
<ForecastImport
|
||||||
|
fileType={"standard"}
|
||||||
|
name={"Standard Forecast Import"}
|
||||||
|
/>
|
||||||
|
<StandardForecastTemplate />
|
||||||
|
<Separator className="my-4" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="m-5">
|
<div className="m-5">
|
||||||
<ul className="list-disc mr-2">
|
<ul className="list-disc mr-2">
|
||||||
|
|||||||
193
frontend/src/components/user/PasswordChange.tsx
Normal file
193
frontend/src/components/user/PasswordChange.tsx
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import { useForm } from "@tanstack/react-form";
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { LstCard } from "../extendedUI/LstCard";
|
||||||
|
import { CardContent, CardHeader } from "../ui/card";
|
||||||
|
|
||||||
|
import { Label } from "../ui/label";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
|
export default function PasswordChange() {
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
const token = localStorage.getItem("auth_token");
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
},
|
||||||
|
onSubmit: async ({ value }) => {
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
const res = await axios.patch("/api/auth/profile", value, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
//console.log(res.data);
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
localStorage.removeItem("auth_token");
|
||||||
|
navigate({ to: "/login" });
|
||||||
|
form.reset();
|
||||||
|
toast.success(`Your password was just updated.`);
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.data.success) {
|
||||||
|
toast.error(res.data.message);
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
toast.error("There was an error updating your password");
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LstCard className="w-96 ml-7">
|
||||||
|
<CardHeader>
|
||||||
|
<p>Password Change Form</p>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<form.Field
|
||||||
|
name="password"
|
||||||
|
// We can choose between form-wide and field-specific validators
|
||||||
|
validators={{
|
||||||
|
onChangeAsyncDebounceMs: 500,
|
||||||
|
onChangeAsync: ({ value }) => {
|
||||||
|
// if (
|
||||||
|
// window.location.pathname.includes(
|
||||||
|
// "/users"
|
||||||
|
// ) &&
|
||||||
|
// value.length === 0
|
||||||
|
// ) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
if (value.length < 4) {
|
||||||
|
return "Password must be at least 4 characters long.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[A-Z]/.test(value)) {
|
||||||
|
return "Password must contain at least one uppercase letter.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[a-z]/.test(value)) {
|
||||||
|
return "Password must contain at least one lower case letter.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[0-9]/.test(value)) {
|
||||||
|
return "Password must contain at least one number.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "Password must contain at least one special character.";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||||
|
<Label
|
||||||
|
htmlFor="password1"
|
||||||
|
className="mb-2"
|
||||||
|
>
|
||||||
|
Password
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
name={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
type="password"
|
||||||
|
onChange={(e) =>
|
||||||
|
field.handleChange(
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{field.state.meta.errors.length ? (
|
||||||
|
<em>
|
||||||
|
{field.state.meta.errors.join(
|
||||||
|
","
|
||||||
|
)}
|
||||||
|
</em>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<form.Field
|
||||||
|
name="confirmPassword"
|
||||||
|
validators={{
|
||||||
|
onChange: ({ value, fieldApi }) => {
|
||||||
|
const password =
|
||||||
|
fieldApi.form.getFieldValue("password");
|
||||||
|
if (value !== password) {
|
||||||
|
return "Passwords do not match.";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(field) => {
|
||||||
|
return (
|
||||||
|
<div className="m-2 min-w-48 max-w-96 p-2">
|
||||||
|
<Label
|
||||||
|
htmlFor="confirmPassword"
|
||||||
|
className="mb-2"
|
||||||
|
>
|
||||||
|
Confirm Password
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
name={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
type="password"
|
||||||
|
onChange={(e) =>
|
||||||
|
field.handleChange(
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{field.state.meta.errors.length ? (
|
||||||
|
<em>
|
||||||
|
{field.state.meta.errors.join(
|
||||||
|
", "
|
||||||
|
)}
|
||||||
|
</em>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</form.Field>
|
||||||
|
<div className="mt-4 ml-4 flex justify-end">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
onClick={form.handleSubmit}
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</LstCard>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
// Import Routes
|
// Import Routes
|
||||||
|
|
||||||
import { Route as rootRoute } from './routes/__root'
|
import { Route as rootRoute } from './routes/__root'
|
||||||
|
import { Route as RegisterImport } from './routes/register'
|
||||||
import { Route as LoginImport } from './routes/login'
|
import { Route as LoginImport } from './routes/login'
|
||||||
import { Route as ChangelogImport } from './routes/changelog'
|
import { Route as ChangelogImport } from './routes/changelog'
|
||||||
import { Route as AboutImport } from './routes/about'
|
import { Route as AboutImport } from './routes/about'
|
||||||
@@ -27,6 +28,7 @@ import { Route as AdminSettingsImport } from './routes/_admin/settings'
|
|||||||
import { Route as AdminServersImport } from './routes/_admin/servers'
|
import { Route as AdminServersImport } from './routes/_admin/servers'
|
||||||
import { Route as AdminNotificationMGTImport } from './routes/_admin/notificationMGT'
|
import { Route as AdminNotificationMGTImport } from './routes/_admin/notificationMGT'
|
||||||
import { Route as AdminModulesImport } from './routes/_admin/modules'
|
import { Route as AdminModulesImport } from './routes/_admin/modules'
|
||||||
|
import { Route as userPasswordChangeImport } from './routes/(user)/passwordChange'
|
||||||
import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/index'
|
import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/index'
|
||||||
import { Route as logisticsSiloAdjustmentsIndexImport } from './routes/(logistics)/siloAdjustments/index'
|
import { Route as logisticsSiloAdjustmentsIndexImport } from './routes/(logistics)/siloAdjustments/index'
|
||||||
import { Route as logisticsOpenOrdersIndexImport } from './routes/(logistics)/openOrders/index'
|
import { Route as logisticsOpenOrdersIndexImport } from './routes/(logistics)/openOrders/index'
|
||||||
@@ -40,6 +42,12 @@ import { Route as logisticsSiloAdjustmentsCommentCommentImport } from './routes/
|
|||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
|
|
||||||
|
const RegisterRoute = RegisterImport.update({
|
||||||
|
id: '/register',
|
||||||
|
path: '/register',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const LoginRoute = LoginImport.update({
|
const LoginRoute = LoginImport.update({
|
||||||
id: '/login',
|
id: '/login',
|
||||||
path: '/login',
|
path: '/login',
|
||||||
@@ -133,6 +141,12 @@ const AdminModulesRoute = AdminModulesImport.update({
|
|||||||
getParentRoute: () => AdminRoute,
|
getParentRoute: () => AdminRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const userPasswordChangeRoute = userPasswordChangeImport.update({
|
||||||
|
id: '/(user)/passwordChange',
|
||||||
|
path: '/passwordChange',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const ocmeCyclecountIndexRoute = ocmeCyclecountIndexImport.update({
|
const ocmeCyclecountIndexRoute = ocmeCyclecountIndexImport.update({
|
||||||
id: '/(ocme)/cyclecount/',
|
id: '/(ocme)/cyclecount/',
|
||||||
path: '/cyclecount/',
|
path: '/cyclecount/',
|
||||||
@@ -252,6 +266,20 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof LoginImport
|
preLoaderRoute: typeof LoginImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/register': {
|
||||||
|
id: '/register'
|
||||||
|
path: '/register'
|
||||||
|
fullPath: '/register'
|
||||||
|
preLoaderRoute: typeof RegisterImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
|
'/(user)/passwordChange': {
|
||||||
|
id: '/(user)/passwordChange'
|
||||||
|
path: '/passwordChange'
|
||||||
|
fullPath: '/passwordChange'
|
||||||
|
preLoaderRoute: typeof userPasswordChangeImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/_admin/modules': {
|
'/_admin/modules': {
|
||||||
id: '/_admin/modules'
|
id: '/_admin/modules'
|
||||||
path: '/modules'
|
path: '/modules'
|
||||||
@@ -438,6 +466,8 @@ export interface FileRoutesByFullPath {
|
|||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/changelog': typeof ChangelogRoute
|
'/changelog': typeof ChangelogRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
|
'/register': typeof RegisterRoute
|
||||||
|
'/passwordChange': typeof userPasswordChangeRoute
|
||||||
'/modules': typeof AdminModulesRoute
|
'/modules': typeof AdminModulesRoute
|
||||||
'/notificationMGT': typeof AdminNotificationMGTRoute
|
'/notificationMGT': typeof AdminNotificationMGTRoute
|
||||||
'/servers': typeof AdminServersRoute
|
'/servers': typeof AdminServersRoute
|
||||||
@@ -465,6 +495,8 @@ export interface FileRoutesByTo {
|
|||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/changelog': typeof ChangelogRoute
|
'/changelog': typeof ChangelogRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
|
'/register': typeof RegisterRoute
|
||||||
|
'/passwordChange': typeof userPasswordChangeRoute
|
||||||
'/modules': typeof AdminModulesRoute
|
'/modules': typeof AdminModulesRoute
|
||||||
'/notificationMGT': typeof AdminNotificationMGTRoute
|
'/notificationMGT': typeof AdminNotificationMGTRoute
|
||||||
'/servers': typeof AdminServersRoute
|
'/servers': typeof AdminServersRoute
|
||||||
@@ -495,6 +527,8 @@ export interface FileRoutesById {
|
|||||||
'/about': typeof AboutRoute
|
'/about': typeof AboutRoute
|
||||||
'/changelog': typeof ChangelogRoute
|
'/changelog': typeof ChangelogRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
|
'/register': typeof RegisterRoute
|
||||||
|
'/(user)/passwordChange': typeof userPasswordChangeRoute
|
||||||
'/_admin/modules': typeof AdminModulesRoute
|
'/_admin/modules': typeof AdminModulesRoute
|
||||||
'/_admin/notificationMGT': typeof AdminNotificationMGTRoute
|
'/_admin/notificationMGT': typeof AdminNotificationMGTRoute
|
||||||
'/_admin/servers': typeof AdminServersRoute
|
'/_admin/servers': typeof AdminServersRoute
|
||||||
@@ -524,6 +558,8 @@ export interface FileRouteTypes {
|
|||||||
| '/about'
|
| '/about'
|
||||||
| '/changelog'
|
| '/changelog'
|
||||||
| '/login'
|
| '/login'
|
||||||
|
| '/register'
|
||||||
|
| '/passwordChange'
|
||||||
| '/modules'
|
| '/modules'
|
||||||
| '/notificationMGT'
|
| '/notificationMGT'
|
||||||
| '/servers'
|
| '/servers'
|
||||||
@@ -550,6 +586,8 @@ export interface FileRouteTypes {
|
|||||||
| '/about'
|
| '/about'
|
||||||
| '/changelog'
|
| '/changelog'
|
||||||
| '/login'
|
| '/login'
|
||||||
|
| '/register'
|
||||||
|
| '/passwordChange'
|
||||||
| '/modules'
|
| '/modules'
|
||||||
| '/notificationMGT'
|
| '/notificationMGT'
|
||||||
| '/servers'
|
| '/servers'
|
||||||
@@ -578,6 +616,8 @@ export interface FileRouteTypes {
|
|||||||
| '/about'
|
| '/about'
|
||||||
| '/changelog'
|
| '/changelog'
|
||||||
| '/login'
|
| '/login'
|
||||||
|
| '/register'
|
||||||
|
| '/(user)/passwordChange'
|
||||||
| '/_admin/modules'
|
| '/_admin/modules'
|
||||||
| '/_admin/notificationMGT'
|
| '/_admin/notificationMGT'
|
||||||
| '/_admin/servers'
|
| '/_admin/servers'
|
||||||
@@ -608,6 +648,8 @@ export interface RootRouteChildren {
|
|||||||
AboutRoute: typeof AboutRoute
|
AboutRoute: typeof AboutRoute
|
||||||
ChangelogRoute: typeof ChangelogRoute
|
ChangelogRoute: typeof ChangelogRoute
|
||||||
LoginRoute: typeof LoginRoute
|
LoginRoute: typeof LoginRoute
|
||||||
|
RegisterRoute: typeof RegisterRoute
|
||||||
|
userPasswordChangeRoute: typeof userPasswordChangeRoute
|
||||||
OcpIndexRoute: typeof OcpIndexRoute
|
OcpIndexRoute: typeof OcpIndexRoute
|
||||||
logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
|
logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
|
||||||
logisticsDmIndexRoute: typeof logisticsDmIndexRoute
|
logisticsDmIndexRoute: typeof logisticsDmIndexRoute
|
||||||
@@ -628,6 +670,8 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
AboutRoute: AboutRoute,
|
AboutRoute: AboutRoute,
|
||||||
ChangelogRoute: ChangelogRoute,
|
ChangelogRoute: ChangelogRoute,
|
||||||
LoginRoute: LoginRoute,
|
LoginRoute: LoginRoute,
|
||||||
|
RegisterRoute: RegisterRoute,
|
||||||
|
userPasswordChangeRoute: userPasswordChangeRoute,
|
||||||
OcpIndexRoute: OcpIndexRoute,
|
OcpIndexRoute: OcpIndexRoute,
|
||||||
logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
|
logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
|
||||||
logisticsDmIndexRoute: logisticsDmIndexRoute,
|
logisticsDmIndexRoute: logisticsDmIndexRoute,
|
||||||
@@ -660,6 +704,8 @@ export const routeTree = rootRoute
|
|||||||
"/about",
|
"/about",
|
||||||
"/changelog",
|
"/changelog",
|
||||||
"/login",
|
"/login",
|
||||||
|
"/register",
|
||||||
|
"/(user)/passwordChange",
|
||||||
"/ocp/",
|
"/ocp/",
|
||||||
"/(logistics)/siloAdjustments/$hist",
|
"/(logistics)/siloAdjustments/$hist",
|
||||||
"/(logistics)/dm/",
|
"/(logistics)/dm/",
|
||||||
@@ -708,6 +754,12 @@ export const routeTree = rootRoute
|
|||||||
"/login": {
|
"/login": {
|
||||||
"filePath": "login.tsx"
|
"filePath": "login.tsx"
|
||||||
},
|
},
|
||||||
|
"/register": {
|
||||||
|
"filePath": "register.tsx"
|
||||||
|
},
|
||||||
|
"/(user)/passwordChange": {
|
||||||
|
"filePath": "(user)/passwordChange.tsx"
|
||||||
|
},
|
||||||
"/_admin/modules": {
|
"/_admin/modules": {
|
||||||
"filePath": "_admin/modules.tsx",
|
"filePath": "_admin/modules.tsx",
|
||||||
"parent": "/_admin"
|
"parent": "/_admin"
|
||||||
|
|||||||
28
frontend/src/routes/(user)/passwordChange.tsx
Normal file
28
frontend/src/routes/(user)/passwordChange.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import PasswordChange from "@/components/user/PasswordChange";
|
||||||
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/(user)/passwordChange")({
|
||||||
|
component: RouteComponent,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
const auth = localStorage.getItem("auth_token");
|
||||||
|
if (!auth) {
|
||||||
|
throw redirect({
|
||||||
|
to: "/login",
|
||||||
|
search: {
|
||||||
|
// Use the current location to power a redirect after login
|
||||||
|
// (Do not use `router.state.resolvedLocation` as it can
|
||||||
|
// potentially lag behind the actual current location)
|
||||||
|
redirect: location.pathname + location.search,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PasswordChange />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -80,8 +80,12 @@ export const Route = createRootRoute({
|
|||||||
Hello {user?.username}
|
Hello {user?.username}
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
{/* <DropdownMenuItem>Profile</DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<DropdownMenuItem>Billing</DropdownMenuItem>
|
<Link to="/passwordChange">
|
||||||
|
Password Change
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{/* <DropdownMenuItem>Billing</DropdownMenuItem>
|
||||||
<DropdownMenuItem>Team</DropdownMenuItem>
|
<DropdownMenuItem>Team</DropdownMenuItem>
|
||||||
<DropdownMenuItem>Subscription</DropdownMenuItem> */}
|
<DropdownMenuItem>Subscription</DropdownMenuItem> */}
|
||||||
<hr className="solid"></hr>
|
<hr className="solid"></hr>
|
||||||
|
|||||||
106
frontend/src/routes/register.tsx
Normal file
106
frontend/src/routes/register.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import RegisterForm from "@/components/auth/Register";
|
||||||
|
import { LstCard } from "@/components/extendedUI/LstCard";
|
||||||
|
import { CardContent, CardHeader } from "@/components/ui/card";
|
||||||
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/register")({
|
||||||
|
component: RouteComponent,
|
||||||
|
beforeLoad: () => {
|
||||||
|
const isLoggedIn = localStorage.getItem("auth_token");
|
||||||
|
if (isLoggedIn) {
|
||||||
|
throw redirect({
|
||||||
|
to: "/",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row w-5/6 justify-between gap-2">
|
||||||
|
<RegisterForm />
|
||||||
|
<div>
|
||||||
|
<LstCard>
|
||||||
|
<CardHeader>
|
||||||
|
<h2 className="text-2xl font-bold mb-4">
|
||||||
|
Disclaimer and User Agreement
|
||||||
|
</h2>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="max-w-2xl mx-auto p-6 rounded-2xl shadow-md">
|
||||||
|
<ul className="list-decimal list-inside space-y-3">
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">
|
||||||
|
Authentication Notice:
|
||||||
|
</span>
|
||||||
|
To interact with the Alpla prod through this
|
||||||
|
application, you must use your{" "}
|
||||||
|
<span className="font-semibold">
|
||||||
|
Windows login credentials
|
||||||
|
</span>
|
||||||
|
. These credentials are used solely for
|
||||||
|
authentication purposes.
|
||||||
|
</li>
|
||||||
|
{/* <li>
|
||||||
|
<span className="font-bold">
|
||||||
|
Password Privacy and Security:
|
||||||
|
</span>
|
||||||
|
This application{" "}
|
||||||
|
<span className="font-semibold">
|
||||||
|
does not store, sync, or transmit
|
||||||
|
</span>{" "}
|
||||||
|
your Windows password in any form.
|
||||||
|
Authentication is handled securely, and your
|
||||||
|
credentials are never logged or accessible
|
||||||
|
to the developers or third parties.
|
||||||
|
</li> */}
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">
|
||||||
|
Data Handling:
|
||||||
|
</span>
|
||||||
|
All data accessed through the Alpla prod
|
||||||
|
remains the property of Alpla. The app
|
||||||
|
functions as a secure interface for
|
||||||
|
accessing and updating this information
|
||||||
|
where permitted by your role.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">
|
||||||
|
User Responsibility:
|
||||||
|
</span>
|
||||||
|
You are responsible for any actions
|
||||||
|
performed using your account. Please ensure
|
||||||
|
you do not share your credentials with
|
||||||
|
others and always log out of the application
|
||||||
|
when not in use.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">
|
||||||
|
No Warranty:
|
||||||
|
</span>
|
||||||
|
This application is provided "
|
||||||
|
<span className="italic">as is</span>"
|
||||||
|
without any warranty of any kind, either
|
||||||
|
expressed or implied. While every effort is
|
||||||
|
made to ensure functionality and data
|
||||||
|
accuracy, the app is subject to ongoing
|
||||||
|
development and may contain bugs or require
|
||||||
|
updates.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">
|
||||||
|
Consent Required:
|
||||||
|
</span>
|
||||||
|
By signing up or continuing to use this
|
||||||
|
application, you agree to the above terms
|
||||||
|
and acknowledge that you understand how your
|
||||||
|
login information is used.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</LstCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "lstv2",
|
"name": "lstv2",
|
||||||
"version": "2.16.0",
|
"version": "2.16.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lstv2",
|
"name": "lstv2",
|
||||||
"version": "2.16.0",
|
"version": "2.16.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dotenvx/dotenvx": "^1.39.0",
|
"@dotenvx/dotenvx": "^1.39.0",
|
||||||
"@hono/node-server": "^1.14.0",
|
"@hono/node-server": "^1.14.0",
|
||||||
|
|||||||
184
package.json
184
package.json
@@ -1,95 +1,95 @@
|
|||||||
{
|
{
|
||||||
"name": "lstv2",
|
"name": "lstv2",
|
||||||
"version": "2.16.0",
|
"version": "2.16.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
|
||||||
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
|
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
|
||||||
"dev:frontend": "cd frontend && npm run dev",
|
"dev:frontend": "cd frontend && npm run dev",
|
||||||
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
|
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
|
||||||
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
|
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
|
||||||
"build": "npm run build:server && npm run build:frontend && npm run zipServer && npm run dev",
|
"build": "npm run build:server && npm run build:frontend && npm run zipServer && npm run dev",
|
||||||
"build:server": "rimraf dist && tsc --build && npm run copy:scripts && xcopy server\\services\\notifications\\utils\\views\\ dist\\server\\services\\notifications\\utils\\views\\ /E /I /Y",
|
"build:server": "rimraf dist && tsc --build && npm run copy:scripts && xcopy server\\services\\notifications\\utils\\views\\ dist\\server\\services\\notifications\\utils\\views\\ /E /I /Y",
|
||||||
"build:frontend": "cd frontend && npm run build",
|
"build:frontend": "cd frontend && npm run build",
|
||||||
"copy:scripts": "tsx server/scripts/copyScripts.ts",
|
"copy:scripts": "tsx server/scripts/copyScripts.ts",
|
||||||
"copy:servers": "xcopy server\\services\\server\\utils\\serverData.json dist\\server\\services\\server\\utils /E /I /Y",
|
"copy:servers": "xcopy server\\services\\server\\utils\\serverData.json dist\\server\\services\\server\\utils /E /I /Y",
|
||||||
"start": "set NODE_ENV=production && npm run start:server",
|
"start": "set NODE_ENV=production && npm run start:server",
|
||||||
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
|
"start:server": "dotenvx run -f .env -- node dist/server/index.js",
|
||||||
"db:generate": "npx drizzle-kit generate",
|
"db:generate": "npx drizzle-kit generate",
|
||||||
"db:migrate": "npx drizzle-kit push",
|
"db:migrate": "npx drizzle-kit push",
|
||||||
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
|
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
|
||||||
"deploy": "standard-version --conventional-commits && npm run prodBuild",
|
"deploy": "standard-version --conventional-commits && npm run build",
|
||||||
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||||
"v1Build": "cd C:\\Users\\matthes01\\Documents\\logisticsSupportTool && npm run oldBuilder",
|
"v1Build": "cd C:\\Users\\matthes01\\Documents\\logisticsSupportTool && npm run oldBuilder",
|
||||||
"scriptBuild": "powershell -ExecutionPolicy Bypass -File server/scripts/build.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
"scriptBuild": "powershell -ExecutionPolicy Bypass -File server/scripts/build.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\"",
|
||||||
"removeOld": "rimraf dist && rimraf frontend/dist",
|
"removeOld": "rimraf dist && rimraf frontend/dist",
|
||||||
"prodBuild": "npm run v1Build && npm run build && npm run zipServer && npm run dev",
|
"prodBuild": "npm run v1Build && npm run build && npm run zipServer && npm run dev",
|
||||||
"commit": "cz",
|
"commit": "cz",
|
||||||
"prodinstall": "npm i --omit=dev && npm run db:migrate",
|
"prodinstall": "npm i --omit=dev && npm run db:migrate",
|
||||||
"checkupdates": "npx npm-check-updates",
|
"checkupdates": "npx npm-check-updates",
|
||||||
"testingCode": "dotenvx run -f .env -- tsx watch server/services/logistics/controller/warehouse/cycleCountChecks/cyclecountCheck.ts"
|
"testingCode": "dotenvx run -f .env -- tsx watch server/services/logistics/controller/warehouse/cycleCountChecks/cyclecountCheck.ts"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
"path": "./node_modules/cz-conventional-changelog"
|
"path": "./node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"admConfig": {
|
||||||
|
"build": 297,
|
||||||
|
"oldBuild": "backend-0.1.3.zip"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/adm-zip": "^0.5.7",
|
||||||
|
"@types/bcrypt": "^5.0.2",
|
||||||
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/js-cookie": "^3.0.6",
|
||||||
|
"@types/mssql": "^9.1.7",
|
||||||
|
"@types/node": "^22.13.11",
|
||||||
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
|
"@types/pg": "^8.11.11",
|
||||||
|
"@types/ws": "^8.18.0",
|
||||||
|
"concurrently": "^9.1.2",
|
||||||
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
|
"standard-version": "^9.5.0",
|
||||||
|
"tsx": "^4.19.3",
|
||||||
|
"typescript": "^5.8.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dotenvx/dotenvx": "^1.39.0",
|
||||||
|
"@hono/node-server": "^1.14.0",
|
||||||
|
"@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",
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"bcryptjs": "^3.0.2",
|
||||||
|
"croner": "^9.0.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"date-fns-tz": "^3.2.0",
|
||||||
|
"drizzle-kit": "^0.30.5",
|
||||||
|
"drizzle-orm": "^0.41.0",
|
||||||
|
"drizzle-zod": "^0.7.0",
|
||||||
|
"excel-date-to-js": "^1.1.5",
|
||||||
|
"fast-xml-parser": "^5.0.9",
|
||||||
|
"fs-extra": "^11.3.0",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"mssql": "^11.0.1",
|
||||||
|
"nodemailer": "^6.10.0",
|
||||||
|
"nodemailer-express-handlebars": "^7.0.0",
|
||||||
|
"pg": "^8.14.1",
|
||||||
|
"pino": "^9.6.0",
|
||||||
|
"pino-abstract-transport": "^2.0.0",
|
||||||
|
"pino-pretty": "^13.0.0",
|
||||||
|
"postgres": "^3.4.5",
|
||||||
|
"react-resizable-panels": "^2.1.7",
|
||||||
|
"rimraf": "^6.0.1",
|
||||||
|
"st-ethernet-ip": "^2.7.3",
|
||||||
|
"ws": "^8.18.1",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"zod": "^3.24.2"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"admConfig": {
|
|
||||||
"build": 288,
|
|
||||||
"oldBuild": "backend-0.1.3.zip"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/adm-zip": "^0.5.7",
|
|
||||||
"@types/bcrypt": "^5.0.2",
|
|
||||||
"@types/fs-extra": "^11.0.4",
|
|
||||||
"@types/js-cookie": "^3.0.6",
|
|
||||||
"@types/mssql": "^9.1.7",
|
|
||||||
"@types/node": "^22.13.11",
|
|
||||||
"@types/node-cron": "^3.0.11",
|
|
||||||
"@types/nodemailer": "^6.4.17",
|
|
||||||
"@types/pg": "^8.11.11",
|
|
||||||
"@types/ws": "^8.18.0",
|
|
||||||
"concurrently": "^9.1.2",
|
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
|
||||||
"standard-version": "^9.5.0",
|
|
||||||
"tsx": "^4.19.3",
|
|
||||||
"typescript": "^5.8.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@dotenvx/dotenvx": "^1.39.0",
|
|
||||||
"@hono/node-server": "^1.14.0",
|
|
||||||
"@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",
|
|
||||||
"axios": "^1.8.4",
|
|
||||||
"bcryptjs": "^3.0.2",
|
|
||||||
"croner": "^9.0.0",
|
|
||||||
"date-fns": "^4.1.0",
|
|
||||||
"date-fns-tz": "^3.2.0",
|
|
||||||
"drizzle-kit": "^0.30.5",
|
|
||||||
"drizzle-orm": "^0.41.0",
|
|
||||||
"drizzle-zod": "^0.7.0",
|
|
||||||
"excel-date-to-js": "^1.1.5",
|
|
||||||
"fast-xml-parser": "^5.0.9",
|
|
||||||
"fs-extra": "^11.3.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"mssql": "^11.0.1",
|
|
||||||
"nodemailer": "^6.10.0",
|
|
||||||
"nodemailer-express-handlebars": "^7.0.0",
|
|
||||||
"pg": "^8.14.1",
|
|
||||||
"pino": "^9.6.0",
|
|
||||||
"pino-abstract-transport": "^2.0.0",
|
|
||||||
"pino-pretty": "^13.0.0",
|
|
||||||
"postgres": "^3.4.5",
|
|
||||||
"react-resizable-panels": "^2.1.7",
|
|
||||||
"rimraf": "^6.0.1",
|
|
||||||
"st-ethernet-ip": "^2.7.3",
|
|
||||||
"ws": "^8.18.1",
|
|
||||||
"xlsx": "^0.18.5",
|
|
||||||
"zod": "^3.24.2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ $plantFunness = {
|
|||||||
############################################################################
|
############################################################################
|
||||||
Write-Host "Stopping the services to do the updates, pkgs and db changes."
|
Write-Host "Stopping the services to do the updates, pkgs and db changes."
|
||||||
|
|
||||||
Write-Host "Stopping services to update"
|
#Write-Host "Stopping services to update"
|
||||||
$serviceGateway = "LST-Gateway$(if ($token -eq "usiow2") { "_2" })"
|
$serviceGateway = "LST-Gateway$(if ($token -eq "usiow2") { "_2" })"
|
||||||
$serviceAuth = "LST-Auth$(if ($token -eq "usiow2") { "_2" })"
|
$serviceAuth = "LST-Auth$(if ($token -eq "usiow2") { "_2" })"
|
||||||
$serviceSystem = "LST-System$(if ($token -eq "usiow2") { "_2" })"
|
$serviceSystem = "LST-System$(if ($token -eq "usiow2") { "_2" })"
|
||||||
@@ -155,30 +155,30 @@ $plantFunness = {
|
|||||||
$serviceOcme = "LST-Ocme$(if ($token -eq "usiow2") { "_2" })"
|
$serviceOcme = "LST-Ocme$(if ($token -eq "usiow2") { "_2" })"
|
||||||
$serviceLstV2 = "LSTV2$(if ($token -eq "usiow2") { "_2" })"
|
$serviceLstV2 = "LSTV2$(if ($token -eq "usiow2") { "_2" })"
|
||||||
|
|
||||||
if($token -eq "usday1"){
|
# if($token -eq "usday1"){
|
||||||
Write-Host "Stopping $($serviceOcme)"
|
# Write-Host "Stopping $($serviceOcme)"
|
||||||
Stop-Service -DisplayName $serviceOcme -Force
|
# Stop-Service -DisplayName $serviceOcme -Force
|
||||||
}
|
# }
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceGateway)"
|
# Write-Host "Stopping $($serviceGateway)"
|
||||||
Stop-Service -DisplayName $serviceGateway -Force
|
# Stop-Service -DisplayName $serviceGateway -Force
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceAuth)"
|
# Write-Host "Stopping $($serviceAuth)"
|
||||||
Stop-Service -DisplayName $serviceAuth -Force
|
# Stop-Service -DisplayName $serviceAuth -Force
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceSystem)"
|
# Write-Host "Stopping $($serviceSystem)"
|
||||||
Stop-Service -DisplayName $serviceSystem -Force
|
# Stop-Service -DisplayName $serviceSystem -Force
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceApp)"
|
# Write-Host "Stopping $($serviceApp)"
|
||||||
Stop-Service -DisplayName $serviceApp -Force
|
# Stop-Service -DisplayName $serviceApp -Force
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceFrontEnd)"
|
# Write-Host "Stopping $($serviceFrontEnd)"
|
||||||
Stop-Service -DisplayName $serviceFrontEnd -Force
|
# Stop-Service -DisplayName $serviceFrontEnd -Force
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
|
|
||||||
Write-Host "Stopping $($serviceLstV2)"
|
Write-Host "Stopping $($serviceLstV2)"
|
||||||
Stop-Service -DisplayName $serviceLstV2 -Force
|
Stop-Service -DisplayName $serviceLstV2 -Force
|
||||||
@@ -194,11 +194,16 @@ $plantFunness = {
|
|||||||
Write-Host "Removing services that are no longer used."
|
Write-Host "Removing services that are no longer used."
|
||||||
& $nssmPath remove "LogisticsSupportTool" confirm
|
& $nssmPath remove "LogisticsSupportTool" confirm
|
||||||
& $nssmPath remove $serviceAuth confirm
|
& $nssmPath remove $serviceAuth confirm
|
||||||
|
& $nssmPath remove $serviceGateway confirm
|
||||||
|
& $nssmPath remove $serviceSystem confirm
|
||||||
|
& $nssmPath remove $serviceApp confirm
|
||||||
|
& $nssmPath remove $serviceFrontEnd confirm
|
||||||
|
& $nssmPath remove $serviceOcme confirm
|
||||||
# & $nssmPath remove $serviceGateway confirm
|
# & $nssmPath remove $serviceGateway confirm
|
||||||
# if($token -eq "usday1"){
|
# if($token -eq "usday1"){
|
||||||
# & $nssmPath remove $serviceOcme confirm
|
# & $nssmPath remove $serviceOcme confirm
|
||||||
# }
|
# }
|
||||||
Start-Sleep -Seconds 5
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
$service = Get-Service -Name $serviceLstV2 -ErrorAction SilentlyContinue
|
$service = Get-Service -Name $serviceLstV2 -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
@@ -233,188 +238,188 @@ $plantFunness = {
|
|||||||
# Frontend env
|
# Frontend env
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
Write-Host "Creating the env file in the front end"
|
# Write-Host "Creating the env file in the front end"
|
||||||
$envContentTemplatef = @"
|
# $envContentTemplatef = @"
|
||||||
NEXTAUTH_SECRET= "12348fssad5sdg2f2354afvfw34"
|
# NEXTAUTH_SECRET= "12348fssad5sdg2f2354afvfw34"
|
||||||
NEXTAUTH_URL_INTERNAL= "http://localhost:3000"
|
# NEXTAUTH_URL_INTERNAL= "http://localhost:3000"
|
||||||
NEXTAUTH_URL="{url}"
|
# NEXTAUTH_URL="{url}"
|
||||||
API_KEY= "E3ECD3619A943B98C6F33E3322362"
|
# API_KEY= "E3ECD3619A943B98C6F33E3322362"
|
||||||
"@
|
# "@
|
||||||
|
|
||||||
try {
|
# try {
|
||||||
$url = "http://$($token)vms006:3000"
|
# $url = "http://$($token)vms006:3000"
|
||||||
|
|
||||||
if ($token -eq "usiow2") {
|
# if ($token -eq "usiow2") {
|
||||||
$url = "http://usiow1vms006:3001"
|
# $url = "http://usiow1vms006:3001"
|
||||||
}
|
# }
|
||||||
|
|
||||||
if ($token -in @("test1", "test2", "test3")) {
|
# if ($token -in @("test1", "test2", "test3")) {
|
||||||
$url = "http://usmcd1vms036:3000"
|
# $url = "http://usmcd1vms036:3000"
|
||||||
}
|
# }
|
||||||
|
|
||||||
# Replace {url} with the actual $url
|
# # Replace {url} with the actual $url
|
||||||
$envContentf = $envContentTemplatef -replace "{url}", $url
|
# $envContentf = $envContentTemplatef -replace "{url}", $url
|
||||||
|
|
||||||
# Define the path where the .env file should be created
|
# # Define the path where the .env file should be created
|
||||||
$envFilePathf = $obslst + "\apps\frontend\.env"
|
# $envFilePathf = $obslst + "\apps\frontend\.env"
|
||||||
Write-Host "Final URL: $url"
|
# Write-Host "Final URL: $url"
|
||||||
# Write the content to the .env file
|
# # Write the content to the .env file
|
||||||
$envContentf | Out-File -FilePath $envFilePathf -Encoding UTF8 -Force
|
# $envContentf | Out-File -FilePath $envFilePathf -Encoding UTF8 -Force
|
||||||
|
|
||||||
# Optional: Verify the file was created
|
# # Optional: Verify the file was created
|
||||||
if (Test-Path $envFilePathf) {
|
# if (Test-Path $envFilePathf) {
|
||||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathf"
|
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathf"
|
||||||
} else {
|
# } else {
|
||||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||||
}
|
# }
|
||||||
|
|
||||||
} catch {
|
# } catch {
|
||||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||||
}
|
# }
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
# DB env
|
# DB env
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
Write-Host "Creating the env file in the front end"
|
# Write-Host "Creating the env file in the front end"
|
||||||
$envContentTemplateb = @"
|
# $envContentTemplateb = @"
|
||||||
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
# DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||||
"@
|
# "@
|
||||||
|
|
||||||
try {
|
# try {
|
||||||
|
|
||||||
$dbLink = "lstBackendDB"
|
# $dbLink = "lstBackendDB"
|
||||||
|
|
||||||
if ($token -eq "usiow2") {
|
# if ($token -eq "usiow2") {
|
||||||
$dbLink = "lstBackendDB_2"
|
# $dbLink = "lstBackendDB_2"
|
||||||
}
|
# }
|
||||||
|
|
||||||
if ($token -in @("test1", "test2", "test3")) {
|
# if ($token -in @("test1", "test2", "test3")) {
|
||||||
$dbLink = "lstBackendDB"
|
# $dbLink = "lstBackendDB"
|
||||||
}
|
# }
|
||||||
|
|
||||||
# Replace {url} with the actual $url
|
# # Replace {url} with the actual $url
|
||||||
$envContentb = $envContentTemplateb -replace "{dbLink}", $dbLink
|
# $envContentb = $envContentTemplateb -replace "{dbLink}", $dbLink
|
||||||
|
|
||||||
# Define the path where the .env file should be created
|
# # Define the path where the .env file should be created
|
||||||
$envFilePathb = $obslst + "\packages\database\.env"
|
# $envFilePathb = $obslst + "\packages\database\.env"
|
||||||
|
|
||||||
# Write the content to the .env file
|
# # Write the content to the .env file
|
||||||
$envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
|
# $envContentb | Out-File -FilePath $envFilePathb -Encoding UTF8 -Force
|
||||||
|
|
||||||
# Optional: Verify the file was created
|
# # Optional: Verify the file was created
|
||||||
if (Test-Path $envFilePathb) {
|
# if (Test-Path $envFilePathb) {
|
||||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathb"
|
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathb"
|
||||||
} else {
|
# } else {
|
||||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||||
}
|
# }
|
||||||
|
|
||||||
} catch {
|
# } catch {
|
||||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||||
}
|
# }
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
# backend env
|
# backend env
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
Write-Host "Creating the env file in the front end"
|
# Write-Host "Creating the env file in the front end"
|
||||||
$envContentTemplated = @"
|
# $envContentTemplated = @"
|
||||||
# Server env
|
# # Server env
|
||||||
NODE_ENV = production
|
# NODE_ENV = production
|
||||||
# server apiKey
|
# # server apiKey
|
||||||
API_KEY = E3ECD3619A943B98C6F33E3322362
|
# API_KEY = E3ECD3619A943B98C6F33E3322362
|
||||||
# Prisma DB link
|
# # Prisma DB link
|
||||||
DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
# DATABASE_URL="file:E:\LST\db\{dbLink}.db"
|
||||||
# if you still want the db in the same folder as the server install you need to do like the example below else use the relevent link
|
# # if you still want the db in the same folder as the server install you need to do like the example below else use the relevent link
|
||||||
DATEBASE_LOC="E:\LST\db\{dbLink}.db"
|
# DATEBASE_LOC="E:\LST\db\{dbLink}.db"
|
||||||
DATABASE_BACKUP_LOC="E:\LST\backups"
|
# DATABASE_BACKUP_LOC="E:\LST\backups"
|
||||||
# Server port
|
# # Server port
|
||||||
GATEWAY_PORT={gatewayport}
|
# GATEWAY_PORT={gatewayport}
|
||||||
AUTH_PORT=4100
|
# AUTH_PORT=4100
|
||||||
SYSTEM_APP_PORT={systemport}
|
# SYSTEM_APP_PORT={systemport}
|
||||||
OCME_PORT={ocme}
|
# OCME_PORT={ocme}
|
||||||
|
|
||||||
# This should me removed once we have the entire app broke out to its own apps
|
# # This should me removed once we have the entire app broke out to its own apps
|
||||||
OLD_APP_PORT={appPort}
|
# OLD_APP_PORT={appPort}
|
||||||
|
|
||||||
# Logging
|
# # Logging
|
||||||
LOG_LEVEL = info
|
# LOG_LEVEL = info
|
||||||
LOG_LOC ="E:\\LST\\logs"
|
# LOG_LOC ="E:\\LST\\logs"
|
||||||
|
|
||||||
# authentication
|
# # authentication
|
||||||
SALTING = 12
|
# SALTING = 12
|
||||||
SECRET = E3ECD3619A943B98C6F33E3322362
|
# SECRET = E3ECD3619A943B98C6F33E3322362
|
||||||
JWT_SECRET = 12348fssad5sdg2f2354afvfw34
|
# JWT_SECRET = 12348fssad5sdg2f2354afvfw34
|
||||||
JWT_EXPIRES_TIME = 1h
|
# JWT_EXPIRES_TIME = 1h
|
||||||
|
|
||||||
# cookie time is in min please take this into consideration when creating all the times
|
# # cookie time is in min please take this into consideration when creating all the times
|
||||||
COOKIE_EXPIRES_TIME = 60
|
# COOKIE_EXPIRES_TIME = 60
|
||||||
|
|
||||||
# password token reset in mintues
|
# # password token reset in mintues
|
||||||
RESET_TOKEN = 330
|
# RESET_TOKEN = 330
|
||||||
"@
|
# "@
|
||||||
|
|
||||||
try {
|
# try {
|
||||||
|
|
||||||
$dbLink = "lstBackendDB"
|
# $dbLink = "lstBackendDB"
|
||||||
$gatewayport = "4400"
|
# $gatewayport = "4400"
|
||||||
$systemport = "4200"
|
# $systemport = "4200"
|
||||||
$ocmeport = "4300"
|
# $ocmeport = "4300"
|
||||||
$appport = "4400"
|
# $appport = "4400"
|
||||||
|
|
||||||
if ($token -eq "usiow2") {
|
# if ($token -eq "usiow2") {
|
||||||
$dbLink = "lstBackendDB_2"
|
# $dbLink = "lstBackendDB_2"
|
||||||
$gatewayport = "4401"
|
# $gatewayport = "4401"
|
||||||
$systemport = "4201"
|
# $systemport = "4201"
|
||||||
$ocmeport = "4301"
|
# $ocmeport = "4301"
|
||||||
$appport = "4401"
|
# $appport = "4401"
|
||||||
}
|
# }
|
||||||
|
|
||||||
if ($token -in @("test1", "test2", "test3")) {
|
# if ($token -in @("test1", "test2", "test3")) {
|
||||||
$dbLink = "lstBackendDB"
|
# $dbLink = "lstBackendDB"
|
||||||
}
|
# }
|
||||||
|
|
||||||
#
|
# #
|
||||||
$port1 = $envContentTemplated -replace "{gatewayport}", $gatewayport
|
# $port1 = $envContentTemplated -replace "{gatewayport}", $gatewayport
|
||||||
$port2 = $port1 -replace "{systemport}", $systemport
|
# $port2 = $port1 -replace "{systemport}", $systemport
|
||||||
$port3 = $port2 -replace "{ocme}", $ocmeport
|
# $port3 = $port2 -replace "{ocme}", $ocmeport
|
||||||
$port4 = $port3 -replace "{appPort}", $appport
|
# $port4 = $port3 -replace "{appPort}", $appport
|
||||||
$envContentd = $port4 -replace "{dbLink}", $dbLink
|
# $envContentd = $port4 -replace "{dbLink}", $dbLink
|
||||||
|
|
||||||
|
|
||||||
# Define the path where the .env file should be created
|
# # Define the path where the .env file should be created
|
||||||
$envFilePathd = $obslst + "\.env"
|
# $envFilePathd = $obslst + "\.env"
|
||||||
|
|
||||||
# Write the content to the .env file
|
# # Write the content to the .env file
|
||||||
$envContentd | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
|
# $envContentd | Out-File -FilePath $envFilePathd -Encoding UTF8 -Force
|
||||||
|
|
||||||
# Optional: Verify the file was created
|
# # Optional: Verify the file was created
|
||||||
if (Test-Path $envFilePathd) {
|
# if (Test-Path $envFilePathd) {
|
||||||
Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathd"
|
# Write-Host "`.env` file created successfully on $env:COMPUTERNAME at $envFilePathd"
|
||||||
} else {
|
# } else {
|
||||||
Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
# Write-Host "Failed to create `.env` file on $env:COMPUTERNAME"
|
||||||
}
|
# }
|
||||||
|
|
||||||
} catch {
|
# } catch {
|
||||||
Write-Host "Error: Failed to create `.env` file on $server - $_"
|
# Write-Host "Error: Failed to create `.env` file on $server - $_"
|
||||||
}
|
# }
|
||||||
|
|
||||||
|
|
||||||
Write-Host "Running install on obs server."
|
# Write-Host "Running install on obs server."
|
||||||
Set-Location $obslst
|
# Set-Location $obslst
|
||||||
npm run newinstall # --omit=dev
|
# npm run newinstall # --omit=dev
|
||||||
Write-Host "Update the frontend"
|
# Write-Host "Update the frontend"
|
||||||
npm run install:front
|
# npm run install:front
|
||||||
npm run install:ui
|
# npm run install:ui
|
||||||
npm run install:db
|
# npm run install:db
|
||||||
|
|
||||||
Write-Host "Running db updates"
|
# Write-Host "Running db updates"
|
||||||
npm run db:migrate
|
# npm run db:migrate
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
npm run db:gen
|
# npm run db:gen
|
||||||
Start-Sleep -Seconds 1
|
# Start-Sleep -Seconds 1
|
||||||
Write-Host "incase a new default setting was added we want to add it in."
|
# Write-Host "incase a new default setting was added we want to add it in."
|
||||||
npm run db:init
|
# npm run db:init
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
# Starting the services back up.
|
# Starting the services back up.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const registerUser = async (
|
|||||||
setSysAdmin(updateUser, "systemAdmin");
|
setSysAdmin(updateUser, "systemAdmin");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { sucess: true, message: "User Registered", user };
|
return { success: true, message: "User Registered", user };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
createLog("error", "auth", "auth", `${error}`);
|
createLog("error", "auth", "auth", `${error}`);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { User } from "../../../../types/users.js";
|
|||||||
import { createPassword } from "../../utils/createPassword.js";
|
import { createPassword } from "../../utils/createPassword.js";
|
||||||
import { createLog } from "../../../logger/logger.js";
|
import { createLog } from "../../../logger/logger.js";
|
||||||
import { sendEmail } from "../../../notifications/controller/sendMail.js";
|
import { sendEmail } from "../../../notifications/controller/sendMail.js";
|
||||||
|
import { settings } from "../../../../../database/schema/settings.js";
|
||||||
|
|
||||||
export const updateUserADM = async (userData: User) => {
|
export const updateUserADM = async (userData: User) => {
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +14,6 @@ export const updateUserADM = async (userData: User) => {
|
|||||||
* password, username, email.
|
* password, username, email.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
console.log(userData);
|
|
||||||
createLog(
|
createLog(
|
||||||
"info",
|
"info",
|
||||||
"apiAuthedRoute",
|
"apiAuthedRoute",
|
||||||
@@ -39,6 +39,21 @@ export const updateUserADM = async (userData: User) => {
|
|||||||
"The user you are looking for has either been deleted or dose not exist.",
|
"The user you are looking for has either been deleted or dose not exist.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { data: s, error: se } = await tryCatch(db.select().from(settings));
|
||||||
|
|
||||||
|
if (se) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `There was an error getting setting data to post to the server.`,
|
||||||
|
data: se,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const set: any = s;
|
||||||
|
const server = set.filter((n: any) => n.name === "server");
|
||||||
|
const port = set.filter((n: any) => n.name === "serverPort");
|
||||||
|
|
||||||
const upd_user = user as User;
|
const upd_user = user as User;
|
||||||
const password: string = userData.password
|
const password: string = userData.password
|
||||||
? await createPassword(userData.password!)
|
? await createPassword(userData.password!)
|
||||||
@@ -72,6 +87,8 @@ export const updateUserADM = async (userData: User) => {
|
|||||||
context: {
|
context: {
|
||||||
password: userData.password!,
|
password: userData.password!,
|
||||||
username: user[0].username!,
|
username: user[0].username!,
|
||||||
|
server: server[0].value,
|
||||||
|
port: port[0].value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,114 +7,124 @@ import { createLog } from "../../../logger/logger.js";
|
|||||||
const app = new OpenAPIHono();
|
const app = new OpenAPIHono();
|
||||||
|
|
||||||
const UserSchema = z.object({
|
const UserSchema = z.object({
|
||||||
password: z
|
password: z
|
||||||
.string()
|
.string()
|
||||||
.min(6, { message: "Passwords must be longer than 3 characters" })
|
.min(6, { message: "Passwords must be longer than 3 characters" })
|
||||||
.regex(/[A-Z]/, {
|
.regex(/[A-Z]/, {
|
||||||
message: "Password must contain at least one uppercase letter",
|
message: "Password must contain at least one uppercase letter",
|
||||||
})
|
})
|
||||||
.regex(/[\W_]/, {
|
.regex(/[\W_]/, {
|
||||||
message: "Password must contain at least one special character",
|
message: "Password must contain at least one special character",
|
||||||
})
|
})
|
||||||
.openapi({ example: "Password1!" }),
|
.openapi({ example: "Password1!" }),
|
||||||
});
|
});
|
||||||
app.openapi(
|
app.openapi(
|
||||||
createRoute({
|
createRoute({
|
||||||
tags: ["auth:user"],
|
tags: ["auth:user"],
|
||||||
summary: "Updates a users Profile",
|
summary: "Updates a users Profile",
|
||||||
description: "Currently you can only update your password over the API",
|
description: "Currently you can only update your password over the API",
|
||||||
method: "post",
|
method: "patch",
|
||||||
path: "/profile",
|
path: "/profile",
|
||||||
middleware: authMiddleware,
|
middleware: authMiddleware,
|
||||||
request: {
|
request: {
|
||||||
body: {
|
body: {
|
||||||
content: {
|
content: {
|
||||||
"application/json": { schema: UserSchema },
|
"application/json": { schema: UserSchema },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
responses: {
|
||||||
},
|
200: {
|
||||||
responses: {
|
content: {
|
||||||
200: {
|
"application/json": {
|
||||||
content: {
|
schema: z.object({
|
||||||
"application/json": {
|
message: z.string().optional().openapi({
|
||||||
schema: z.object({
|
example: "User Profile has been updated",
|
||||||
message: z
|
}),
|
||||||
.string()
|
}),
|
||||||
.optional()
|
},
|
||||||
.openapi({ example: "User Profile has been updated" }),
|
},
|
||||||
}),
|
description: "Sucess return",
|
||||||
},
|
},
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
message: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.openapi({ example: "Unauthenticated" }),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Unauthorized",
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
message: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.openapi({ example: "Internal Server error" }),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Internal Server Error",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
description: "Sucess return",
|
}),
|
||||||
},
|
async (c) => {
|
||||||
401: {
|
// make sure we have a vaid user being accessed thats really logged in
|
||||||
content: {
|
const authHeader = c.req.header("Authorization");
|
||||||
"application/json": {
|
|
||||||
schema: z.object({
|
|
||||||
message: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.openapi({ example: "Unauthenticated" }),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: "Unauthorized",
|
|
||||||
},
|
|
||||||
500: {
|
|
||||||
content: {
|
|
||||||
"application/json": {
|
|
||||||
schema: z.object({
|
|
||||||
message: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.openapi({ example: "Internal Server error" }),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: "Internal Server Error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
async (c) => {
|
|
||||||
// make sure we have a vaid user being accessed thats really logged in
|
|
||||||
const authHeader = c.req.header("Authorization");
|
|
||||||
|
|
||||||
if (authHeader?.includes("Basic")) {
|
if (authHeader?.includes("Basic")) {
|
||||||
return c.json(
|
return c.json(
|
||||||
{ message: "You are a Basic user! Please login to get a token" },
|
{
|
||||||
401
|
message:
|
||||||
);
|
"You are a Basic user! Please login to get a token",
|
||||||
|
},
|
||||||
|
401
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
return c.json({ success: false, message: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader?.split("Bearer ")[1] || "";
|
||||||
|
let user;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await verify(token, process.env.JWT_SECRET!);
|
||||||
|
user = payload.user;
|
||||||
|
} catch (error) {
|
||||||
|
createLog(
|
||||||
|
"error",
|
||||||
|
"lst",
|
||||||
|
"auth",
|
||||||
|
"Failed session check, user must be logged out"
|
||||||
|
);
|
||||||
|
return c.json({ success: false, message: "Unauthorized" }, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now pass all the data over to update the user info
|
||||||
|
try {
|
||||||
|
const data = await c?.req.json();
|
||||||
|
await updateProfile(user, data, token);
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
message: "Your profile has been updated",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: "There was an error",
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authHeader) {
|
|
||||||
return c.json({ message: "Unauthorized" }, 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = authHeader?.split("Bearer ")[1] || "";
|
|
||||||
let user;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const payload = await verify(token, process.env.JWT_SECRET!);
|
|
||||||
user = payload.user;
|
|
||||||
} catch (error) {
|
|
||||||
createLog(
|
|
||||||
"error",
|
|
||||||
"lst",
|
|
||||||
"auth",
|
|
||||||
"Failed session check, user must be logged out"
|
|
||||||
);
|
|
||||||
return c.json({ message: "Unauthorized" }, 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now pass all the data over to update the user info
|
|
||||||
try {
|
|
||||||
const data = await c?.req.json();
|
|
||||||
await updateProfile(user, data, token);
|
|
||||||
return c.json({ message: "Your profile has been updated" });
|
|
||||||
} catch (error) {
|
|
||||||
return c.json({ message: "There was an error", error });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { lorealForecast } from "./mappings/loralForecast.js";
|
||||||
import { standardForecast } from "./mappings/standardForcast.js";
|
import { standardForecast } from "./mappings/standardForcast.js";
|
||||||
|
|
||||||
export const forecastIn = async (data: any, user: any) => {
|
export const forecastIn = async (data: any, user: any) => {
|
||||||
@@ -12,7 +13,7 @@ export const forecastIn = async (data: any, user: any) => {
|
|||||||
// what type of order are we dealing with?
|
// what type of order are we dealing with?
|
||||||
if (data["fileType"] === "standard") {
|
if (data["fileType"] === "standard") {
|
||||||
//run the standard forecast in
|
//run the standard forecast in
|
||||||
const standard = await standardForecast(data["postPostForecast"], user);
|
const standard = await standardForecast(data["postForecast"], user);
|
||||||
success = standard.success ?? false;
|
success = standard.success ?? false;
|
||||||
message = standard.message ?? "Error posting standard forecast";
|
message = standard.message ?? "Error posting standard forecast";
|
||||||
orderData = standard.data;
|
orderData = standard.data;
|
||||||
@@ -23,7 +24,11 @@ export const forecastIn = async (data: any, user: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data["fileType"] === "loreal") {
|
if (data["fileType"] === "loreal") {
|
||||||
// orders in
|
//run the standard forecast in
|
||||||
|
const loreal = await lorealForecast(data["postForecast"], user);
|
||||||
|
success = loreal.success ?? false;
|
||||||
|
message = loreal.message ?? "Error posting standard forecast";
|
||||||
|
orderData = loreal.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data["fileType"] === "pg") {
|
if (data["fileType"] === "pg") {
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { db } from "../../../../../../../database/dbclient.js";
|
||||||
|
import { settings } from "../../../../../../../database/schema/settings.js";
|
||||||
|
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
|
||||||
|
import XLSX from "xlsx";
|
||||||
|
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
|
||||||
|
import { postForecast } from "../postForecast.js";
|
||||||
|
|
||||||
|
let customerID = 4;
|
||||||
|
export const lorealForecast = async (data: any, user: any) => {
|
||||||
|
/**
|
||||||
|
* Post a standard forecast based on the standard template.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { data: s, error: e } = await tryCatch(db.select().from(settings));
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
return {
|
||||||
|
sucess: false,
|
||||||
|
message: `Error getting settings`,
|
||||||
|
data: e,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const plantToken = s.filter((s) => s.name === "plantToken");
|
||||||
|
|
||||||
|
const arrayBuffer = await data.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
|
const workbook = XLSX.read(buffer, { type: "buffer" });
|
||||||
|
|
||||||
|
const sheet: any = workbook.Sheets["Alpla HDPE"];
|
||||||
|
const range = XLSX.utils.decode_range(sheet["!ref"]);
|
||||||
|
|
||||||
|
const headers = [];
|
||||||
|
for (let C = range.s.c; C <= range.e.c; ++C) {
|
||||||
|
const cellAddress = XLSX.utils.encode_cell({ r: 0, c: C }); // row 0 = Excel row 1
|
||||||
|
const cell = sheet[cellAddress];
|
||||||
|
headers.push(cell ? cell.v : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
|
||||||
|
defval: "",
|
||||||
|
header: headers,
|
||||||
|
range: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract the customer code (assuming it's always present)
|
||||||
|
const customerCode = forecastData["NORTH HDPE BOTTLES"];
|
||||||
|
|
||||||
|
// Filter out non-date properties (these are your metadata fields)
|
||||||
|
const metadataFields = [
|
||||||
|
"NORTH HDPE BOTTLES",
|
||||||
|
"BLOCKED",
|
||||||
|
"INVENTORY",
|
||||||
|
"CALL-OFF",
|
||||||
|
"PALLET CONSUMPTION",
|
||||||
|
];
|
||||||
|
|
||||||
|
const foreCastData: any = [];
|
||||||
|
|
||||||
|
// process the forcast
|
||||||
|
forecastData.forEach((item: any, index: any) => {
|
||||||
|
//console.log(`Processing item ${index + 1} of ${forecastData.length}`);
|
||||||
|
|
||||||
|
// Extract the customer code
|
||||||
|
const customerCode = item["NORTH HDPE BOTTLES"];
|
||||||
|
|
||||||
|
// Process each date in the current object
|
||||||
|
for (const [date, qty] of Object.entries(item)) {
|
||||||
|
// Skip metadata fields
|
||||||
|
if (metadataFields.includes(date)) continue;
|
||||||
|
|
||||||
|
if (qty === 0) continue;
|
||||||
|
|
||||||
|
// Create your transformed record
|
||||||
|
const record = {
|
||||||
|
customerArticleNo: customerCode,
|
||||||
|
requirementDate: excelDateStuff(parseInt(date)),
|
||||||
|
quantity: qty,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do something with this record
|
||||||
|
foreCastData.push(record);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const predefinedObject = {
|
||||||
|
receivingPlantId: plantToken[0].value,
|
||||||
|
documentName: `ForecastFromLST-${new Date(Date.now()).toLocaleString(
|
||||||
|
"en-US"
|
||||||
|
)}`,
|
||||||
|
sender: user.username || "lst-system",
|
||||||
|
customerId: customerID,
|
||||||
|
positions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
let updatedPredefinedObject = {
|
||||||
|
...predefinedObject,
|
||||||
|
positions: [
|
||||||
|
...predefinedObject.positions,
|
||||||
|
...foreCastData.filter((q: any) => q.customerArticleNo != ""),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const posting: any = await postForecast(updatedPredefinedObject, user);
|
||||||
|
return {
|
||||||
|
success: posting.success,
|
||||||
|
message: posting.message,
|
||||||
|
data: posting.data,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,114 @@
|
|||||||
|
import { db } from "../../../../../../../database/dbclient.js";
|
||||||
|
import { settings } from "../../../../../../../database/schema/settings.js";
|
||||||
|
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
|
||||||
|
import XLSX from "xlsx";
|
||||||
|
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
|
||||||
|
import { postForecast } from "../postForecast.js";
|
||||||
|
|
||||||
export const standardForecast = async (data: any, user: any) => {
|
export const standardForecast = async (data: any, user: any) => {
|
||||||
/**
|
/**
|
||||||
* Post a standard forecast based on the standard template.
|
* Post a standard forecast based on the standard template.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const { data: s, error: e } = await tryCatch(db.select().from(settings));
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
return {
|
||||||
|
sucess: false,
|
||||||
|
message: `Error getting settings`,
|
||||||
|
data: e,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const plantToken = s.filter((s) => s.name === "plantToken");
|
||||||
|
|
||||||
|
const arrayBuffer = await data.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
|
const workbook = XLSX.read(buffer, { type: "buffer" });
|
||||||
|
|
||||||
|
const sheetName = workbook.SheetNames[0];
|
||||||
|
const sheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
"CustomerArticleNumber",
|
||||||
|
"Quantity",
|
||||||
|
"RequirementDate",
|
||||||
|
"CustomerID",
|
||||||
|
];
|
||||||
|
|
||||||
|
const forecastData: any = XLSX.utils.sheet_to_json(sheet, {
|
||||||
|
defval: "",
|
||||||
|
header: headers,
|
||||||
|
range: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupedByCustomer: any = forecastData.reduce(
|
||||||
|
(acc: any, item: any) => {
|
||||||
|
const id = item.CustomerID;
|
||||||
|
if (!acc[id]) {
|
||||||
|
acc[id] = [];
|
||||||
|
}
|
||||||
|
acc[id].push(item);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const foreCastData: any = [];
|
||||||
|
|
||||||
|
for (const [customerID, forecast] of Object.entries(groupedByCustomer)) {
|
||||||
|
//console.log(`Running for Customer ID: ${customerID}`);
|
||||||
|
const newForecast: any = forecast;
|
||||||
|
|
||||||
|
const predefinedObject = {
|
||||||
|
receivingPlantId: plantToken[0].value,
|
||||||
|
documentName: `ForecastFromLST-${new Date(
|
||||||
|
Date.now()
|
||||||
|
).toLocaleString("en-US")}`,
|
||||||
|
sender: user.username || "lst-system",
|
||||||
|
customerId: customerID,
|
||||||
|
positions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// map everything out for each order
|
||||||
|
const nForecast = newForecast.map((o: any) => {
|
||||||
|
// const invoice = i.filter(
|
||||||
|
// (i: any) => i.deliveryAddress === parseInt(customerID)
|
||||||
|
// );
|
||||||
|
// if (!invoice) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
return {
|
||||||
|
customerArticleNo: o.CustomerArticleNumber,
|
||||||
|
requirementDate: excelDateStuff(parseInt(o.RequirementDate)),
|
||||||
|
quantity: o.Quantity,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// do that fun combining thing
|
||||||
|
let updatedPredefinedObject = {
|
||||||
|
...predefinedObject,
|
||||||
|
positions: [...predefinedObject.positions, ...nForecast],
|
||||||
|
};
|
||||||
|
|
||||||
|
//console.log(updatedPredefinedObject);
|
||||||
|
|
||||||
|
// post the orders to the server
|
||||||
|
const posting: any = await postForecast(updatedPredefinedObject, user);
|
||||||
|
|
||||||
|
foreCastData.push({
|
||||||
|
customer: customerID,
|
||||||
|
//totalOrders: orders?.length(),
|
||||||
|
success: posting.success,
|
||||||
|
message: posting.message,
|
||||||
|
data: posting.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Forecast Posted",
|
message: "Forecast Posted",
|
||||||
data: [],
|
data: foreCastData,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import XLSX from "xlsx";
|
||||||
|
import { tryCatch } from "../../../../../../globalUtils/tryCatch.js";
|
||||||
|
import { db } from "../../../../../../../database/dbclient.js";
|
||||||
|
import { settings } from "../../../../../../../database/schema/settings.js";
|
||||||
|
import { query } from "../../../../../sqlServer/prodSqlServer.js";
|
||||||
|
import { orderState } from "../../../../../sqlServer/querys/dm/orderState.js";
|
||||||
|
import { excelDateStuff } from "../../../../utils/excelDateStuff.js";
|
||||||
|
import { invoiceAddress } from "../../../../../sqlServer/querys/dm/invoiceAddress.js";
|
||||||
|
import { postOrders } from "../postOrders.js";
|
||||||
|
|
||||||
|
export const energizerOrders = async (data: any, user: any) => {
|
||||||
|
/**
|
||||||
|
* Standard orders meaning that we get the standard file exported and fill it out and uplaod to lst.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { data: s, error: e } = await tryCatch(db.select().from(settings));
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
return {
|
||||||
|
sucess: false,
|
||||||
|
message: `Error getting settings`,
|
||||||
|
data: e,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// order state
|
||||||
|
const { data: openOrders, error: oe } = await tryCatch(
|
||||||
|
query(orderState, "Gets the next 500 orders that have not been started")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oe) {
|
||||||
|
return {
|
||||||
|
sucess: false,
|
||||||
|
message: `Error getting article data`,
|
||||||
|
data: oe,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// order state
|
||||||
|
const { data: i, error: ie } = await tryCatch(
|
||||||
|
query(invoiceAddress, "Gets invoices addresses")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ie) {
|
||||||
|
return {
|
||||||
|
sucess: false,
|
||||||
|
message: `Error getting invoice address data`,
|
||||||
|
data: ie,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const plantToken = s.filter((s) => s.name === "plantToken");
|
||||||
|
|
||||||
|
const arrayBuffer = await data.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
|
const workbook = XLSX.read(buffer, { type: "buffer" });
|
||||||
|
|
||||||
|
const sheetName = workbook.SheetNames[0];
|
||||||
|
const sheet = workbook.Sheets[sheetName];
|
||||||
|
|
||||||
|
// define custom headers
|
||||||
|
const headers = [
|
||||||
|
"ITEM",
|
||||||
|
"PO",
|
||||||
|
"ReleaseNo",
|
||||||
|
"QTY",
|
||||||
|
"DELDATE",
|
||||||
|
"COMMENTS",
|
||||||
|
"What changed",
|
||||||
|
"CUSTOMERID",
|
||||||
|
];
|
||||||
|
const orderData = XLSX.utils.sheet_to_json(sheet, {
|
||||||
|
defval: "",
|
||||||
|
header: headers,
|
||||||
|
range: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// the base of the import
|
||||||
|
const predefinedObject = {
|
||||||
|
receivingPlantId: plantToken[0].value,
|
||||||
|
documentName: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
|
||||||
|
"en-US"
|
||||||
|
)}`,
|
||||||
|
sender: user.username || "lst-system",
|
||||||
|
externalRefNo: `OrdersFromLST-${new Date(Date.now()).toLocaleString(
|
||||||
|
"en-US"
|
||||||
|
)}`,
|
||||||
|
orders: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
let newOrders: any = orderData;
|
||||||
|
|
||||||
|
// filter out the orders that have already been started just to reduce the risk of errors.
|
||||||
|
newOrders.filter((oo: any) =>
|
||||||
|
openOrders.some(
|
||||||
|
(o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// filter out the blanks
|
||||||
|
newOrders = newOrders.filter((z: any) => z.ITEM !== "");
|
||||||
|
|
||||||
|
// let postedOrders: any = [];
|
||||||
|
// for (const [customerID, orders] of Object.entries(orderData)) {
|
||||||
|
// // console.log(`Running for Customer ID: ${customerID}`);
|
||||||
|
// const newOrders: any = orderData;
|
||||||
|
|
||||||
|
// // filter out the orders that have already been started just to reduce the risk of errors.
|
||||||
|
// newOrders.filter((oo: any) =>
|
||||||
|
// openOrders.some(
|
||||||
|
// (o: any) => o.CustomerOrderNumber === oo.CustomerOrderNumber
|
||||||
|
// )
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // map everything out for each order
|
||||||
|
const nOrder = newOrders.map((o: any) => {
|
||||||
|
const invoice = i.filter(
|
||||||
|
(i: any) => i.deliveryAddress === parseInt(o.CUSTOMERID)
|
||||||
|
);
|
||||||
|
if (!invoice) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
customerId: parseInt(o.CUSTOMERID),
|
||||||
|
invoiceAddressId: invoice[0].invoiceAddress, // matched to the default invoice address
|
||||||
|
customerOrderNo: o.PO,
|
||||||
|
orderDate: new Date(Date.now()).toLocaleString("en-US"),
|
||||||
|
positions: [
|
||||||
|
{
|
||||||
|
deliveryAddressId: parseInt(o.CUSTOMERID),
|
||||||
|
customerArticleNo: o.ITEM,
|
||||||
|
quantity: parseInt(o.QTY),
|
||||||
|
deliveryDate: o.DELDATE, //excelDateStuff(o.DELDATE),
|
||||||
|
customerLineItemNo: o.ReleaseNo, // this is how it is currently sent over from abbott
|
||||||
|
customerReleaseNo: o.ReleaseNo, // same as above
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// // do that fun combining thing
|
||||||
|
const updatedPredefinedObject = {
|
||||||
|
...predefinedObject,
|
||||||
|
orders: [...predefinedObject.orders, ...nOrder],
|
||||||
|
};
|
||||||
|
|
||||||
|
// //console.log(updatedPredefinedObject);
|
||||||
|
|
||||||
|
// // post the orders to the server
|
||||||
|
const posting: any = await postOrders(updatedPredefinedObject, user);
|
||||||
|
|
||||||
|
return {
|
||||||
|
customer: nOrder[0].CUSTOMERID,
|
||||||
|
//totalOrders: orders?.length(),
|
||||||
|
success: posting.success,
|
||||||
|
message: posting.message,
|
||||||
|
data: posting.data,
|
||||||
|
};
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// success: true,
|
||||||
|
// message:
|
||||||
|
// "Standard Template was just processed successfully, please check AlplaProd 2.0 to confirm no errors. ",
|
||||||
|
// data: nOrder,
|
||||||
|
// };
|
||||||
|
};
|
||||||
@@ -100,7 +100,27 @@ export const standardOrders = async (data: any, user: any) => {
|
|||||||
let postedOrders: any = [];
|
let postedOrders: any = [];
|
||||||
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
|
for (const [customerID, orders] of Object.entries(groupedByCustomer)) {
|
||||||
// console.log(`Running for Customer ID: ${customerID}`);
|
// console.log(`Running for Customer ID: ${customerID}`);
|
||||||
const newOrders: any = orders;
|
const filterOrders: any = orders;
|
||||||
|
const newOrders: any = [];
|
||||||
|
//newOrders.filter((oo) => openOrders.some((o) => String(o.CustomerOrderNumber) === String(oo.CustomerOrderNumber)));
|
||||||
|
//console.log(newOrders)
|
||||||
|
filterOrders.forEach((oo: any) => {
|
||||||
|
const isMatch = openOrders.some(
|
||||||
|
(o: any) =>
|
||||||
|
String(o.CustomerOrderNumber).trim() ===
|
||||||
|
String(oo.CustomerOrderNumber).trim()
|
||||||
|
);
|
||||||
|
if (!isMatch) {
|
||||||
|
console.log(`ok to update: ${oo.CustomerOrderNumber}`);
|
||||||
|
|
||||||
|
newOrders.push(oo);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`Not valid order to update: ${oo.CustomerOrderNumber}`
|
||||||
|
);
|
||||||
|
//console.log(oo)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// filter out the orders that have already been started just to reduce the risk of errors.
|
// filter out the orders that have already been started just to reduce the risk of errors.
|
||||||
newOrders.filter((oo: any) =>
|
newOrders.filter((oo: any) =>
|
||||||
@@ -119,7 +139,7 @@ export const standardOrders = async (data: any, user: any) => {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
customerId: parseInt(customerID),
|
customerId: parseInt(customerID),
|
||||||
invoiceAddressId: invoice[0].invoiceAddress, // matched to the default invoice address
|
invoiceAddressId: invoice[0]?.invoiceAddress, // matched to the default invoice address
|
||||||
customerOrderNo: o.CustomerOrderNumber,
|
customerOrderNo: o.CustomerOrderNumber,
|
||||||
orderDate: new Date(Date.now()).toLocaleString("en-US"),
|
orderDate: new Date(Date.now()).toLocaleString("en-US"),
|
||||||
positions: [
|
positions: [
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { abbottOrders } from "./mappings/abbottTruckList.js";
|
import { abbottOrders } from "./mappings/abbottTruckList.js";
|
||||||
|
import { energizerOrders } from "./mappings/energizerOrdersIn.js";
|
||||||
import { standardOrders } from "./mappings/standardOrders.js";
|
import { standardOrders } from "./mappings/standardOrders.js";
|
||||||
|
|
||||||
export const ordersIn = async (data: any, user: any) => {
|
export const ordersIn = async (data: any, user: any) => {
|
||||||
@@ -29,6 +30,10 @@ export const ordersIn = async (data: any, user: any) => {
|
|||||||
|
|
||||||
if (data["fileType"] === "energizer") {
|
if (data["fileType"] === "energizer") {
|
||||||
// orders in
|
// orders in
|
||||||
|
const energizer = await energizerOrders(data["postOrders"], user);
|
||||||
|
success = energizer.success ?? false;
|
||||||
|
message = energizer.message ?? "Error posting Energizer Orders";
|
||||||
|
orderData = energizer.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data["fileType"] === "loreal") {
|
if (data["fileType"] === "loreal") {
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ app.openapi(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: "Error posting forecast",
|
message: "Error posting Orders",
|
||||||
data: error,
|
data: error,
|
||||||
},
|
},
|
||||||
400
|
400
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ app.openapi(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
@@ -22,11 +22,17 @@
|
|||||||
|
|
||||||
Your password was change. Please find your new temporary password below:<br/><br/>
|
Your password was change. Please find your new temporary password below:<br/><br/>
|
||||||
|
|
||||||
Temporary Password: <em><b>{{password}}</b></em><br/><br/>
|
Temporary Password: <em><b>{{password}}</b></em><br/><br/>
|
||||||
|
|
||||||
For security reasons, we strongly recommend changing your password as soon as possible.<br/><br/>
|
For security reasons, we strongly recommend changing your password as soon as possible.<br/><br/>
|
||||||
|
|
||||||
You can update it by logging into your account and navigating to the password settings section.<br/><br/>
|
You can update it by logging into your account, clicking your profile at the top right and click password change.<br/><br/>
|
||||||
|
|
||||||
|
|
||||||
|
Or <a href="http://{{server}}:{{port}}/passwordChange"
|
||||||
|
style="display:inline-block; padding:10px 20px; text-decoration:none; border-radius:5px;">
|
||||||
|
Click Here
|
||||||
|
</a> to login and change your password.<br/><br/>
|
||||||
|
|
||||||
Best regards,<br/><br/>
|
Best regards,<br/><br/>
|
||||||
LST team<br/>
|
LST team<br/>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const assignedPrinters = async () => {
|
|||||||
|
|
||||||
//console.log(lots);
|
//console.log(lots);
|
||||||
|
|
||||||
return;
|
//return;
|
||||||
for (let i = 0; i < printers.length; i++) {
|
for (let i = 0; i < printers.length; i++) {
|
||||||
// is the printer assinged in alplalabel online?
|
// is the printer assinged in alplalabel online?
|
||||||
const assigned = lots?.filter(
|
const assigned = lots?.filter(
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
export const orderState = `
|
export const orderState = `
|
||||||
SELECT top(500)
|
SELECT top(10000)
|
||||||
CustomerOrderNumber
|
CustomerOrderNumber
|
||||||
, OrderState
|
, OrderState
|
||||||
|
, r.ReleaseState
|
||||||
|
, h.CreatedByEdi
|
||||||
|
|
||||||
--, *
|
--, *
|
||||||
FROM [test1_AlplaPROD2.0_Read].[order].[Header] (nolock)
|
FROM [test1_AlplaPROD2.0_Read].[order].[Header] (nolock) h
|
||||||
where OrderState = 0
|
|
||||||
|
/* get the line items to link to the headers */
|
||||||
|
left join
|
||||||
|
[test1_AlplaPROD2.0_Read].[order].[LineItem] (nolock) l on
|
||||||
|
l.HeaderId = h.id
|
||||||
|
|
||||||
|
/* get the releases to link to the headers */
|
||||||
|
left join
|
||||||
|
[test1_AlplaPROD2.0_Read].[order].[Release] (nolock) r on
|
||||||
|
r.LineItemId = l.id
|
||||||
|
|
||||||
|
where
|
||||||
|
--h.CreatedByEdi = 1
|
||||||
|
r.ReleaseState > 0
|
||||||
|
--and CustomerOrderNumber in ( '2365862', '2360391')
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user