7 Commits

25 changed files with 6141 additions and 43 deletions

View File

@@ -0,0 +1,23 @@
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import type { z } from "zod";
export const dockDoorScans = pgTable("dock_door_scans", {
id: uuid("id").defaultRandom().primaryKey(),
dockId: text("dock_id").notNull(),
loadingOrder: text("loading_order").notNull(),
loadingUnit: text("loading_Unit").unique(), // can be running number or sscc depending on where it came from
loadingUnitStatus: text("loading_unit_status").default("loaded"), // TODO: add enums on the status of each load.
message: text("message"), // the response it gave when scanning
status: text("status").default("active"), // TODO: add in enums for this
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
add_user: text("add_user").default("lst-system"),
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
upd_user: text("upd_user").default("lst-system"),
});
export const dockDoorScansSchema = createSelectSchema(dockDoorScans);
export const newDockDoorScansSchema = createInsertSchema(dockDoorScans);
export type DockDoorScans = z.infer<typeof dockDoorScansSchema>;
export type NewDockDoorScans = z.infer<typeof newDockDoorScansSchema>;

View File

@@ -2,7 +2,9 @@ import { eq, sql } from "drizzle-orm";
import { Router } from "express"; import { Router } from "express";
import z from "zod"; import z from "zod";
import { db } from "../db/db.controller.js"; import { db } from "../db/db.controller.js";
import { dockDoorScans } from "../db/schema/dockdoor.scans.schema.js";
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
import { runProdApi } from "../utils/prodEndpoint.utils.js";
import { apiReturn } from "../utils/returnHelper.utils.js"; import { apiReturn } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js"; import { tryCatch } from "../utils/trycatch.utils.js";
@@ -14,20 +16,52 @@ const endLoading = z.object({
}); });
r.post("/", async (req, res) => { r.post("/", async (req, res) => {
// close the loading order // TODO: setup the emitter to just emit the data when we post to the db
// clear the loading order off the dock
try { try {
const validated = endLoading.parse(req.body); const validated = endLoading.parse(req.body);
const orders = (await runProdApi({
method: "post",
endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${req.body.loadingOrder}/Finish`,
data: [
{
printDeliveryDocuments: true,
},
],
})) as any;
if (orders?.data.errors) {
console.log(orders.data.errors);
return apiReturn(res, {
success: false,
level: "error",
module: "dockdoor",
subModule: "loadingOrder",
message: `Failed to finish the order.`,
data: (orders.data.errors as any) ?? [],
status: 400,
});
}
await tryCatch(
db
.update(dockDoorScans)
.set({
upd_date: sql`NOW()`,
upd_user: req.user?.username ?? "lst-dock-system",
})
.where(eq(dockDoorScanners.currentLoadingOrder, validated.loadingOrder))
.returning(),
);
const { data, error } = await tryCatch( const { data, error } = await tryCatch(
db db
.update(dockDoorScanners) .update(dockDoorScanners)
.set({ .set({
currentLoadingOrder: "", currentLoadingOrder: "",
upd_date: sql`NOW()`, upd_date: sql`NOW()`,
upd_user: req.user?.username, upd_user: req.user?.username ?? "lst-dock-system",
}) })
.where(eq(dockDoorScanners.dockId, validated.dockId)) .where(eq(dockDoorScanners.dockId, validated.dockId))
.returning(), .returning(),
@@ -46,13 +80,15 @@ r.post("/", async (req, res) => {
} }
return apiReturn(res, { return apiReturn(res, {
success: true, success: orders.data.errors ? false : true,
level: "info", level: orders.data.errors ? "error" : "info",
module: "dockdoor", module: "dockdoor",
subModule: "loadingOrder", subModule: "loadingOrder",
message: `Loading order ${validated.loadingOrder} was just closed.`, message: orders.data.errors
? `Loading order was cleared but encountered an error: \n${orders.data.errors[0].message} \nPossible reason for this is the loading order was completed via scanner or other means.`
: `Loading order ${validated.loadingOrder} was just closed.`,
data: data ?? [], data: data ?? [],
status: 200, status: orders.data.errors ? 400 : 200,
}); });
} catch (error) { } catch (error) {
return apiReturn(res, { return apiReturn(res, {
@@ -60,7 +96,7 @@ r.post("/", async (req, res) => {
level: "error", level: "error",
module: "dockdoor", module: "dockdoor",
subModule: "loadingOrder", subModule: "loadingOrder",
message: `Failed to start loading order.`, message: `Failed to Close loading order.`,
data: (error as any) ?? [], data: (error as any) ?? [],
status: 400, status: 400,
}); });

View File

@@ -2,6 +2,7 @@
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { db } from "../db/db.controller.js"; import { db } from "../db/db.controller.js";
import { dockDoorScans } from "../db/schema/dockdoor.scans.schema.js";
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
import { emitToRoom } from "../socket.io/roomEmitter.socket.js"; import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
import { runProdApi } from "../utils/prodEndpoint.utils.js"; import { runProdApi } from "../utils/prodEndpoint.utils.js";
@@ -15,12 +16,35 @@ type Data = {
runningNo?: string; runningNo?: string;
}; };
const postScan = async (data: any) => {
try {
await db.insert(dockDoorScans).values({
dockId: data.dockId,
loadingOrder: data.loadingOrder,
loadingUnit: data.unit, // can be running number or sscc depending on where it came from
loadingUnitStatus: data.unitStatus, // TODO: add enums on the status of each load.
message: data.message, // the response it gave when scanning
});
} catch (error) {
console.log("Error: ", error);
}
};
const loadUnit = async (data: Data) => { const loadUnit = async (data: Data) => {
// are we even active at this time? // are we even active at this time?
const dockDoorActive = await db.query.settings.findFirst({ const dockDoorActive = await db.query.settings.findFirst({
where: (u, { eq }) => eq(u.name, "dockDoorScanning"), where: (u, { eq }) => eq(u.name, "dockDoorScanning"),
}); });
const unitToScan = data.sscc
? { sscc: data.sscc?.slice(2) }
: { runningNo: Number(data.runningNo) };
const dock = await db
.select()
.from(dockDoorScanners)
.where(eq(dockDoorScanners.dockId, data.dockId as string));
if (!dockDoorActive?.active) { if (!dockDoorActive?.active) {
return returnFunc({ return returnFunc({
success: false, success: false,
@@ -33,29 +57,18 @@ const loadUnit = async (data: Data) => {
room: "", room: "",
}); });
} }
// check if its a valids an sscc
if (data.sscc === "noread") {
return returnFunc({
success: false,
level: "error",
module: "dockdoor",
subModule: "loadUnit",
message:
"Failed to load the unit to the truck, there was no pallet read.",
data: [],
notify: false,
room: `dockDoorLoading:${data.dockId}`,
});
}
// check if we currently have a loading order attached to the dock door. // check if we currently have a loading order attached to the dock door.
const dock = await db
.select()
.from(dockDoorScanners)
.where(eq(dockDoorScanners.dockId, data.dockId as string));
if (dock[0]?.currentLoadingOrder === "") { if (dock[0]?.currentLoadingOrder === "") {
postScan({
dockId: data.dockId,
loadingOrder: dock[0]?.currentLoadingOrder,
loadingUnit: unitToScan,
loadingUnitStatus: "notLoaded",
message:
"There are know current active loading orders please start one and try again.",
});
return returnFunc({ return returnFunc({
success: true, success: true,
level: "error", level: "error",
@@ -68,6 +81,30 @@ const loadUnit = async (data: Data) => {
room: `dockDoorLoading:${data.dockId}`, room: `dockDoorLoading:${data.dockId}`,
}); });
} }
// check if its a valids an sscc
if (data.sscc === "noread") {
postScan({
dockId: data.dockId,
loadingOrder: dock[0]?.currentLoadingOrder,
loadingUnit: unitToScan,
loadingUnitStatus: "noread",
message:
"Failed to load the unit to the truck, there was no pallet read.",
});
return returnFunc({
success: false,
level: "error",
module: "dockdoor",
subModule: "loadUnit",
message:
"Failed to load the unit to the truck, there was no pallet read.",
data: [],
notify: false,
room: `dockDoorLoading:${data.dockId}`,
});
}
// TODO: pallet validation, check if we are on hold, then check if we have been in the staging warehouse for more than x time. // TODO: pallet validation, check if we are on hold, then check if we have been in the staging warehouse for more than x time.
@@ -77,10 +114,6 @@ const loadUnit = async (data: Data) => {
// add the loading units // add the loading units
try { try {
const unitToScan = data.sscc
? { sscc: data.sscc?.slice(2) }
: { runningNo: Number(data.runningNo) };
const prod = (await runProdApi({ const prod = (await runProdApi({
method: "post", method: "post",
endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${dock[0]?.currentLoadingOrder}/LoadUnit`, endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${dock[0]?.currentLoadingOrder}/LoadUnit`,
@@ -90,6 +123,13 @@ const loadUnit = async (data: Data) => {
//emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data ?? []); //emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data ?? []);
if (!prod?.success) { if (!prod?.success) {
postScan({
dockId: data.dockId,
loadingOrder: dock[0]?.currentLoadingOrder,
loadingUnit: unitToScan,
loadingUnitStatus: "notLoaded",
message: prod?.data.errors[0].message,
});
emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data.errors[0]); emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data.errors[0]);
return returnFunc({ return returnFunc({
success: false, success: false,
@@ -107,6 +147,14 @@ const loadUnit = async (data: Data) => {
data: prod.data, data: prod.data,
code: 0, code: 0,
}; };
postScan({
dockId: data.dockId,
loadingOrder: dock[0]?.currentLoadingOrder,
loadingUnit: unitToScan,
loadingUnitStatus: "loaded",
message: emitData.message,
});
emitToRoom(`dockDoorLoading:${data.dockId}`, emitData as any); emitToRoom(`dockDoorLoading:${data.dockId}`, emitData as any);
return returnFunc({ return returnFunc({
success: true, success: true,

View File

@@ -22,7 +22,6 @@ r.patch(
requirePermission({ notifications: ["update"] }), requirePermission({ notifications: ["update"] }),
async (req, res: Response) => { async (req, res: Response) => {
const { id } = req.params; const { id } = req.params;
try { try {
const validated = updateNote.parse(req.body); const validated = updateNote.parse(req.body);
@@ -37,6 +36,7 @@ r.patch(
await modifiedNotification(id as string); await modifiedNotification(id as string);
if (nError) { if (nError) {
return apiReturn(res, { return apiReturn(res, {
success: false, success: false,
level: "error", level: "error",
@@ -58,6 +58,7 @@ r.patch(
status: 200, status: 200,
}); });
} catch (err) { } catch (err) {
if (err instanceof z.ZodError) { if (err instanceof z.ZodError) {
const flattened = z.flattenError(err); const flattened = z.flattenError(err);
// return res.status(400).json({ // return res.status(400).json({

View File

@@ -137,7 +137,7 @@ r.delete("/:id", async (req, res) => {
.where(eq(opendockArticleSetup.id, id)) .where(eq(opendockArticleSetup.id, id))
.returning(); .returning();
return apiReturn(res, { return apiReturn(res, {
success: false, success: true,
level: "info", //connect.success ? "info" : "error", level: "info", //connect.success ? "info" : "error",
module: "opendock", module: "opendock",
subModule: "articleCheck", subModule: "articleCheck",

View File

@@ -1,7 +1,8 @@
import { desc } from "drizzle-orm"; import { desc, eq } from "drizzle-orm";
import { db } from "../db/db.controller.js"; import { db } from "../db/db.controller.js";
import { logs } from "../db/schema/logs.schema.js"; import { logs } from "../db/schema/logs.schema.js";
import { ppoRun } from "../warehousing/warehousing.ppooMonitor.js"; import { ppoRun } from "../warehousing/warehousing.ppooMonitor.js";
import { dockDoorScans } from "../db/schema/dockdoor.scans.schema.js";
type RoomDefinition<T = unknown> = { type RoomDefinition<T = unknown> = {
seed: (limit: number) => Promise<T[]>; seed: (limit: number) => Promise<T[]>;
@@ -103,7 +104,18 @@ export const roomDefinition: Record<RoomId, RoomDefinition> = {
"dockDoorLoading:2": { "dockDoorLoading:2": {
seed: async (limit) => { seed: async (limit) => {
console.log(limit); console.log(limit);
try {
const rows = await db
.select()
.from(dockDoorScans).where(eq(dockDoorScans.status, "active"))
.orderBy(desc(dockDoorScans.upd_date))
.limit(limit);
return rows; //.reverse();
} catch (e) {
console.error("Failed to seed logs:", e);
return []; return [];
}
}, },
}, },
}; };

View File

@@ -8,6 +8,7 @@ export const statement = {
logistics: ["read", "create", "update", "delete", "readAll"], logistics: ["read", "create", "update", "delete", "readAll"],
mobile: ["read", "create", "update", "delete", "readAll"], mobile: ["read", "create", "update", "delete", "readAll"],
openDock: ["read", "create", "update", "delete"], openDock: ["read", "create", "update", "delete"],
warehouse: ["read", "create", "update", "delete"],
notifications: ["read", "create", "update", "delete", "readAll"], notifications: ["read", "create", "update", "delete", "readAll"],
} as const; } as const;
@@ -17,17 +18,20 @@ export const user = ac.newRole({
app: ["read", "create"], app: ["read", "create"],
notifications: ["read", "create"], notifications: ["read", "create"],
openDock: ["read"], openDock: ["read"],
warehouse: ["read"],
}); });
export const manager = ac.newRole({ export const manager = ac.newRole({
app: ["read", "create", "update"], app: ["read", "create", "update"],
mobile: ["read", "create", "update"], mobile: ["read", "create", "update"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create"],
}); });
export const transport = ac.newRole({ export const transport = ac.newRole({
app: ["read", "create", "update"], app: ["read", "create", "update"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create"],
}); });
export const admin = ac.newRole({ export const admin = ac.newRole({
@@ -35,6 +39,7 @@ export const admin = ac.newRole({
mobile: ["read", "create", "update"], mobile: ["read", "create", "update"],
user: ["create", "update", "ban"], user: ["create", "update", "ban"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create", "update"],
}); });
export const systemAdmin = ac.newRole({ export const systemAdmin = ac.newRole({
@@ -44,6 +49,7 @@ export const systemAdmin = ac.newRole({
mobile: ["read", "create", "update", "delete", "readAll"], mobile: ["read", "create", "update", "delete", "readAll"],
logistics: ["read", "create", "update", "delete", "readAll"], logistics: ["read", "create", "update", "delete", "readAll"],
notifications: ["read", "create", "update", "delete", "readAll"], notifications: ["read", "create", "update", "delete", "readAll"],
warehouse: ["read", "create", "update", "delete"],
openDock: ["read", "create", "update", "delete"], openDock: ["read", "create", "update", "delete"],
}); });

View File

@@ -0,0 +1,94 @@
import { useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { ChevronRight, Link as link } from "lucide-react";
import { permissionQuery } from "../../lib/queries/permsCheck";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../ui/collapsible";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
useSidebar,
} from "../ui/sidebar";
export default function WarehouseBar() {
const { data: canCreate = false } = useQuery(
permissionQuery({
warehouse: ["read"],
}),
);
const { setOpen } = useSidebar();
const items = [
{
title: "Dock Door Scanning",
url: "/warehouse",
//icon,
isActive: canCreate,
items: [
{
title: "DockDoorScanning",
icon: link,
url: "/warehouse/dockdoorscanning",
},
],
},
];
return (
<SidebarGroup>
<SidebarGroupLabel>Warehouse</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<div key={item.title}>
{item.isActive && (
<Collapsible
asChild
//defaultOpen={isNotifications}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
{item.title}
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<Link
to={subItem.url}
onClick={() => setOpen(false)}
>
<subItem.icon />
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
)}
</div>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -13,16 +13,23 @@ import AdminSidebar from "./AdminBar";
import DocBar from "./DocBar"; import DocBar from "./DocBar";
import MobileBar from "./MobileBar"; import MobileBar from "./MobileBar";
import TransportationBar from "./TransportationBar"; import TransportationBar from "./TransportationBar";
import WarehouseBar from "./Warhouse";
export function AppSidebar() { export function AppSidebar() {
const { data: session } = useSession(); const { data: session } = useSession();
const { data: settings, isLoading } = useSuspenseQuery(getSettings()); const { data: settings, isLoading } = useSuspenseQuery(getSettings());
const { data: canRead = false } = useQuery( const { data: canReadOpenDock = false } = useQuery(
permissionQuery({ permissionQuery({
openDock: ["read"], openDock: ["read"],
}), }),
); );
const { data: canReadWarehouse = false } = useQuery(
permissionQuery({
warehouse: ["read"],
}),
);
return ( return (
<Sidebar <Sidebar
variant="sidebar" variant="sidebar"
@@ -42,7 +49,12 @@ export function AppSidebar() {
{!isLoading && {!isLoading &&
settings.filter((n: any) => n.name === "opendock_sync")[0] settings.filter((n: any) => n.name === "opendock_sync")[0]
?.active && ?.active &&
canRead && <TransportationBar />} canReadOpenDock && <TransportationBar />}
{!isLoading &&
settings.filter((n: any) => n.name === "dockDoorScanning")[0]
?.active &&
canReadWarehouse && <WarehouseBar />}
{session && {session &&
(session.user.role === "admin" || (session.user.role === "admin" ||

View File

@@ -6,7 +6,14 @@ import {
} from "better-auth/client/plugins"; } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react"; import { createAuthClient } from "better-auth/react";
import { ac, admin, manager, systemAdmin, user } from "./auth-permissions"; import {
ac,
admin,
manager,
systemAdmin,
transport,
user,
} from "./auth-permissions";
export const authClient = createAuthClient({ export const authClient = createAuthClient({
baseURL: `${window.location.origin}/lst/api/auth`, baseURL: `${window.location.origin}/lst/api/auth`,
@@ -17,6 +24,7 @@ export const authClient = createAuthClient({
admin, admin,
user, user,
manager, manager,
transport,
systemAdmin, systemAdmin,
}, },
}), }),

View File

@@ -14,6 +14,7 @@ export const selectableRoles: SelectableRole[] = [
{ label: "User", value: "user" }, { label: "User", value: "user" },
{ label: "Manager", value: "manager" }, { label: "Manager", value: "manager" },
{ label: "Transport", value: "transport" }, { label: "Transport", value: "transport" },
{ label: "Warehouse", value: "warehouse" },
{ label: "Admin", value: "admin" }, { label: "Admin", value: "admin" },
{ label: "System Admin", value: "systemAdmin" }, { label: "System Admin", value: "systemAdmin" },
]; ];
@@ -25,6 +26,7 @@ export const statement = {
logistics: ["read", "create", "update", "delete", "readAll"], logistics: ["read", "create", "update", "delete", "readAll"],
mobile: ["read", "create", "update", "delete", "readAll"], mobile: ["read", "create", "update", "delete", "readAll"],
openDock: ["read", "create", "update", "delete"], openDock: ["read", "create", "update", "delete"],
warehouse: ["read", "create", "update", "delete"],
notifications: ["read", "create", "update", "delete", "readAll"], notifications: ["read", "create", "update", "delete", "readAll"],
} as const; } as const;
@@ -34,17 +36,20 @@ export const user = ac.newRole({
app: ["read", "create"], app: ["read", "create"],
notifications: ["read", "create"], notifications: ["read", "create"],
openDock: ["read"], openDock: ["read"],
warehouse: ["read"],
}); });
export const manager = ac.newRole({ export const manager = ac.newRole({
app: ["read", "create", "update"], app: ["read", "create", "update"],
mobile: ["read", "create", "update"], mobile: ["read", "create", "update"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create"],
}); });
export const transport = ac.newRole({ export const transport = ac.newRole({
app: ["read", "create", "update"], app: ["read", "create", "update"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create"],
}); });
export const admin = ac.newRole({ export const admin = ac.newRole({
@@ -52,6 +57,7 @@ export const admin = ac.newRole({
mobile: ["read", "create", "update"], mobile: ["read", "create", "update"],
user: ["create", "update", "ban"], user: ["create", "update", "ban"],
openDock: ["read", "create", "update"], openDock: ["read", "create", "update"],
warehouse: ["read", "create", "update"],
}); });
export const systemAdmin = ac.newRole({ export const systemAdmin = ac.newRole({
@@ -61,6 +67,7 @@ export const systemAdmin = ac.newRole({
mobile: ["read", "create", "update", "delete", "readAll"], mobile: ["read", "create", "update", "delete", "readAll"],
logistics: ["read", "create", "update", "delete", "readAll"], logistics: ["read", "create", "update", "delete", "readAll"],
notifications: ["read", "create", "update", "delete", "readAll"], notifications: ["read", "create", "update", "delete", "readAll"],
warehouse: ["read", "create", "update", "delete"],
openDock: ["read", "create", "update", "delete"], openDock: ["read", "create", "update", "delete"],
}); });

View File

@@ -0,0 +1,21 @@
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
import { api } from "../apiHelper";
export function getActiveLoadingOrders() {
return queryOptions({
queryKey: ["getActiveLoadingOrders"],
queryFn: () => dataFetch(),
staleTime: 5000,
refetchOnWindowFocus: true,
placeholderData: keepPreviousData,
});
}
const dataFetch = async () => {
const { data } = await api.get("/dockDoor/activeLoadingOrders");
if (!data.success) {
throw new Error(data.message ?? "Failed to load articles");
}
return data.data ?? [];
};

View File

@@ -0,0 +1,25 @@
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
import { api } from "../apiHelper";
export function getActiveDockScanners() {
return queryOptions({
queryKey: ["getActiveDockScanners"],
queryFn: () => dataFetch(),
staleTime: 5000,
refetchOnWindowFocus: true,
placeholderData: keepPreviousData,
});
}
const dataFetch = async () => {
if (window.location.hostname === "localhost") {
await new Promise((res) => setTimeout(res, 1500));
}
const { data } = await api.get("/dockDoor/scanners");
if (!data.success) {
throw new Error(data.message ?? "Failed to load articles");
}
return data.data ?? [];
};

View File

@@ -22,7 +22,10 @@ import { Route as AdminScanUsersRouteImport } from './routes/admin/scanUsers'
import { Route as AdminNotificationsRouteImport } from './routes/admin/notifications' import { Route as AdminNotificationsRouteImport } from './routes/admin/notifications'
import { Route as AdminLogsRouteImport } from './routes/admin/logs' import { Route as AdminLogsRouteImport } from './routes/admin/logs'
import { Route as authLoginRouteImport } from './routes/(auth)/login' import { Route as authLoginRouteImport } from './routes/(auth)/login'
import { Route as WarehouseDockdoorscanningIndexRouteImport } from './routes/warehouse/dockdoorscanning/index'
import { Route as TransportationOpendockIndexRouteImport } from './routes/transportation/opendock/index' import { Route as TransportationOpendockIndexRouteImport } from './routes/transportation/opendock/index'
import { Route as WarehouseDockdoorscanningDockScansRouteImport } from './routes/warehouse/dockdoorscanning/$dockScans'
import { Route as WarehouseDockdoorscanningDockRouteImport } from './routes/warehouse/dockdoorscanning/$dock'
import { Route as authUserSignupRouteImport } from './routes/(auth)/user.signup' import { Route as authUserSignupRouteImport } from './routes/(auth)/user.signup'
import { Route as authUserResetpasswordRouteImport } from './routes/(auth)/user.resetpassword' import { Route as authUserResetpasswordRouteImport } from './routes/(auth)/user.resetpassword'
import { Route as authUserProfileRouteImport } from './routes/(auth)/user.profile' import { Route as authUserProfileRouteImport } from './routes/(auth)/user.profile'
@@ -92,12 +95,30 @@ const authLoginRoute = authLoginRouteImport.update({
path: '/login', path: '/login',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const WarehouseDockdoorscanningIndexRoute =
WarehouseDockdoorscanningIndexRouteImport.update({
id: '/warehouse/dockdoorscanning/',
path: '/warehouse/dockdoorscanning/',
getParentRoute: () => rootRouteImport,
} as any)
const TransportationOpendockIndexRoute = const TransportationOpendockIndexRoute =
TransportationOpendockIndexRouteImport.update({ TransportationOpendockIndexRouteImport.update({
id: '/transportation/opendock/', id: '/transportation/opendock/',
path: '/transportation/opendock/', path: '/transportation/opendock/',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const WarehouseDockdoorscanningDockScansRoute =
WarehouseDockdoorscanningDockScansRouteImport.update({
id: '/warehouse/dockdoorscanning/$dockScans',
path: '/warehouse/dockdoorscanning/$dockScans',
getParentRoute: () => rootRouteImport,
} as any)
const WarehouseDockdoorscanningDockRoute =
WarehouseDockdoorscanningDockRouteImport.update({
id: '/warehouse/dockdoorscanning/$dock',
path: '/warehouse/dockdoorscanning/$dock',
getParentRoute: () => rootRouteImport,
} as any)
const authUserSignupRoute = authUserSignupRouteImport.update({ const authUserSignupRoute = authUserSignupRouteImport.update({
id: '/(auth)/user/signup', id: '/(auth)/user/signup',
path: '/user/signup', path: '/user/signup',
@@ -131,7 +152,10 @@ export interface FileRoutesByFullPath {
'/user/profile': typeof authUserProfileRoute '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute '/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute '/user/signup': typeof authUserSignupRoute
'/warehouse/dockdoorscanning/$dock': typeof WarehouseDockdoorscanningDockRoute
'/warehouse/dockdoorscanning/$dockScans': typeof WarehouseDockdoorscanningDockScansRoute
'/transportation/opendock/': typeof TransportationOpendockIndexRoute '/transportation/opendock/': typeof TransportationOpendockIndexRoute
'/warehouse/dockdoorscanning/': typeof WarehouseDockdoorscanningIndexRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
@@ -150,7 +174,10 @@ export interface FileRoutesByTo {
'/user/profile': typeof authUserProfileRoute '/user/profile': typeof authUserProfileRoute
'/user/resetpassword': typeof authUserResetpasswordRoute '/user/resetpassword': typeof authUserResetpasswordRoute
'/user/signup': typeof authUserSignupRoute '/user/signup': typeof authUserSignupRoute
'/warehouse/dockdoorscanning/$dock': typeof WarehouseDockdoorscanningDockRoute
'/warehouse/dockdoorscanning/$dockScans': typeof WarehouseDockdoorscanningDockScansRoute
'/transportation/opendock': typeof TransportationOpendockIndexRoute '/transportation/opendock': typeof TransportationOpendockIndexRoute
'/warehouse/dockdoorscanning': typeof WarehouseDockdoorscanningIndexRoute
} }
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRouteImport __root__: typeof rootRouteImport
@@ -170,7 +197,10 @@ export interface FileRoutesById {
'/(auth)/user/profile': typeof authUserProfileRoute '/(auth)/user/profile': typeof authUserProfileRoute
'/(auth)/user/resetpassword': typeof authUserResetpasswordRoute '/(auth)/user/resetpassword': typeof authUserResetpasswordRoute
'/(auth)/user/signup': typeof authUserSignupRoute '/(auth)/user/signup': typeof authUserSignupRoute
'/warehouse/dockdoorscanning/$dock': typeof WarehouseDockdoorscanningDockRoute
'/warehouse/dockdoorscanning/$dockScans': typeof WarehouseDockdoorscanningDockScansRoute
'/transportation/opendock/': typeof TransportationOpendockIndexRoute '/transportation/opendock/': typeof TransportationOpendockIndexRoute
'/warehouse/dockdoorscanning/': typeof WarehouseDockdoorscanningIndexRoute
} }
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
@@ -191,7 +221,10 @@ export interface FileRouteTypes {
| '/user/profile' | '/user/profile'
| '/user/resetpassword' | '/user/resetpassword'
| '/user/signup' | '/user/signup'
| '/warehouse/dockdoorscanning/$dock'
| '/warehouse/dockdoorscanning/$dockScans'
| '/transportation/opendock/' | '/transportation/opendock/'
| '/warehouse/dockdoorscanning/'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: to:
| '/' | '/'
@@ -210,7 +243,10 @@ export interface FileRouteTypes {
| '/user/profile' | '/user/profile'
| '/user/resetpassword' | '/user/resetpassword'
| '/user/signup' | '/user/signup'
| '/warehouse/dockdoorscanning/$dock'
| '/warehouse/dockdoorscanning/$dockScans'
| '/transportation/opendock' | '/transportation/opendock'
| '/warehouse/dockdoorscanning'
id: id:
| '__root__' | '__root__'
| '/' | '/'
@@ -229,7 +265,10 @@ export interface FileRouteTypes {
| '/(auth)/user/profile' | '/(auth)/user/profile'
| '/(auth)/user/resetpassword' | '/(auth)/user/resetpassword'
| '/(auth)/user/signup' | '/(auth)/user/signup'
| '/warehouse/dockdoorscanning/$dock'
| '/warehouse/dockdoorscanning/$dockScans'
| '/transportation/opendock/' | '/transportation/opendock/'
| '/warehouse/dockdoorscanning/'
fileRoutesById: FileRoutesById fileRoutesById: FileRoutesById
} }
export interface RootRouteChildren { export interface RootRouteChildren {
@@ -249,7 +288,10 @@ export interface RootRouteChildren {
authUserProfileRoute: typeof authUserProfileRoute authUserProfileRoute: typeof authUserProfileRoute
authUserResetpasswordRoute: typeof authUserResetpasswordRoute authUserResetpasswordRoute: typeof authUserResetpasswordRoute
authUserSignupRoute: typeof authUserSignupRoute authUserSignupRoute: typeof authUserSignupRoute
WarehouseDockdoorscanningDockRoute: typeof WarehouseDockdoorscanningDockRoute
WarehouseDockdoorscanningDockScansRoute: typeof WarehouseDockdoorscanningDockScansRoute
TransportationOpendockIndexRoute: typeof TransportationOpendockIndexRoute TransportationOpendockIndexRoute: typeof TransportationOpendockIndexRoute
WarehouseDockdoorscanningIndexRoute: typeof WarehouseDockdoorscanningIndexRoute
} }
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@@ -345,6 +387,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof authLoginRouteImport preLoaderRoute: typeof authLoginRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/warehouse/dockdoorscanning/': {
id: '/warehouse/dockdoorscanning/'
path: '/warehouse/dockdoorscanning'
fullPath: '/warehouse/dockdoorscanning/'
preLoaderRoute: typeof WarehouseDockdoorscanningIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/transportation/opendock/': { '/transportation/opendock/': {
id: '/transportation/opendock/' id: '/transportation/opendock/'
path: '/transportation/opendock' path: '/transportation/opendock'
@@ -352,6 +401,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof TransportationOpendockIndexRouteImport preLoaderRoute: typeof TransportationOpendockIndexRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/warehouse/dockdoorscanning/$dockScans': {
id: '/warehouse/dockdoorscanning/$dockScans'
path: '/warehouse/dockdoorscanning/$dockScans'
fullPath: '/warehouse/dockdoorscanning/$dockScans'
preLoaderRoute: typeof WarehouseDockdoorscanningDockScansRouteImport
parentRoute: typeof rootRouteImport
}
'/warehouse/dockdoorscanning/$dock': {
id: '/warehouse/dockdoorscanning/$dock'
path: '/warehouse/dockdoorscanning/$dock'
fullPath: '/warehouse/dockdoorscanning/$dock'
preLoaderRoute: typeof WarehouseDockdoorscanningDockRouteImport
parentRoute: typeof rootRouteImport
}
'/(auth)/user/signup': { '/(auth)/user/signup': {
id: '/(auth)/user/signup' id: '/(auth)/user/signup'
path: '/user/signup' path: '/user/signup'
@@ -393,7 +456,11 @@ const rootRouteChildren: RootRouteChildren = {
authUserProfileRoute: authUserProfileRoute, authUserProfileRoute: authUserProfileRoute,
authUserResetpasswordRoute: authUserResetpasswordRoute, authUserResetpasswordRoute: authUserResetpasswordRoute,
authUserSignupRoute: authUserSignupRoute, authUserSignupRoute: authUserSignupRoute,
WarehouseDockdoorscanningDockRoute: WarehouseDockdoorscanningDockRoute,
WarehouseDockdoorscanningDockScansRoute:
WarehouseDockdoorscanningDockScansRoute,
TransportationOpendockIndexRoute: TransportationOpendockIndexRoute, TransportationOpendockIndexRoute: TransportationOpendockIndexRoute,
WarehouseDockdoorscanningIndexRoute: WarehouseDockdoorscanningIndexRoute,
} }
export const routeTree = rootRouteImport export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren) ._addFileChildren(rootRouteChildren)

View File

@@ -170,11 +170,11 @@ const NotificationTable = () => {
try { try {
const res = await api.patch( const res = await api.patch(
`/lst/api/notification/${i.row.original.id}`, `/notification/${i.row.original.id}`,
{ {
active: !activeToggle, active: !activeToggle,
}, },
{ withCredentials: true },
); );
if (res.data.success) { if (res.data.success) {

View File

@@ -180,7 +180,7 @@ const ScanUserTable = () => {
try { try {
const res = await api.delete( const res = await api.delete(
`/lst/api/mobile/auth/user/${i.row.original.id}`, `/mobile/auth/user/${i.row.original.id}`,
{ {
withCredentials: true, withCredentials: true,

View File

@@ -1,13 +1,18 @@
import { useSuspenseQuery } from "@tanstack/react-query"; import { useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute, redirect } from "@tanstack/react-router"; import { createFileRoute, redirect } from "@tanstack/react-router";
import { createColumnHelper } from "@tanstack/react-table"; import { createColumnHelper } from "@tanstack/react-table";
import { Suspense } from "react"; import { Suspense, useState } from "react";
import { authClient } from "../../../lib/auth-client"; import { authClient } from "../../../lib/auth-client";
import { getArticleLinks } from "../../../lib/queries/getArticleLinks"; import { getArticleLinks } from "../../../lib/queries/getArticleLinks";
import LstTable from "../../../lib/tableStuff/LstTable"; import LstTable from "../../../lib/tableStuff/LstTable";
import SearchableHeader from "../../../lib/tableStuff/SearchableHeader"; import SearchableHeader from "../../../lib/tableStuff/SearchableHeader";
import SkellyTable from "../../../lib/tableStuff/SkellyTable"; import SkellyTable from "../../../lib/tableStuff/SkellyTable";
import NewArticleLink from "./-components/NewArticleLink"; import NewArticleLink from "./-components/NewArticleLink";
import { api } from "../../../lib/apiHelper";
import { toast } from "sonner";
import { Button } from "../../../components/ui/button";
import { Spinner } from "../../../components/ui/spinner";
import { Trash } from "lucide-react";
export const Route = createFileRoute("/transportation/opendock/")({ export const Route = createFileRoute("/transportation/opendock/")({
beforeLoad: async ({ location }) => { beforeLoad: async ({ location }) => {
@@ -91,6 +96,75 @@ const ArticleLinkTable = () => {
filterFn: "includesString", filterFn: "includesString",
cell: (i) => i.getValue(), cell: (i) => i.getValue(),
}), }),
columnHelper.accessor("deleteUser", {
header: ({ column }) => (
<SearchableHeader
column={column}
title="Delete Link"
searchable={false}
/>
),
filterFn: "includesString",
cell: (i) => {
// biome-ignore lint: just removing the lint for now to get this going will maybe fix later
const [activeToggle, setActiveToggle] = useState(false);
const onTrigger = async () => {
setActiveToggle(true);
try {
const res = await api.delete(
`/opendock/articleCheck/${i.row.original.id}`,
{
withCredentials: true,
timeout: 5000,
validateStatus: () => true,
},
);
if (res.data.success) {
toast.success(`AV: ${i.row.original.av} was deleted.`);
refetch();
setActiveToggle(false);
}
if (!res.data.success) {
toast.error(
`AV: ${i.row.original.av} encountered an error when trying to delete: ${res.data.message}`,
);
refetch();
setActiveToggle(false);
}
} catch (error) {
setActiveToggle(false);
console.error(error);
}
};
return (
<div>
<div className="flex items-center space-x-2">
<Button
variant="destructive"
disabled={activeToggle}
onClick={onTrigger}
>
{activeToggle ? (
<span>
<Spinner />
</span>
) : (
<span>
<Trash />
</span>
)}
</Button>
</div>
</div>
);
},
}),
]; ];
return ( return (
<div> <div>

View File

@@ -0,0 +1,118 @@
import { useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { format } from "date-fns";
import { Button } from "../../../components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
} from "../../../components/ui/card";
import { Separator } from "../../../components/ui/separator";
import { getActiveLoadingOrders } from "../../../lib/queries/getActiveDockScanners";
import { getActiveDockScanners } from "../../../lib/queries/getActiveLoadingOrders";
import { finishLoadingOrder} from './index'
import { api } from "../../../lib/apiHelper";
import { toast } from "sonner";
export const Route = createFileRoute("/warehouse/dockdoorscanning/$dock")({
component: RouteComponent,
});
export const startOrder = async (loadingOrder: string, dockId: string, refetch:any, refetchActiveLoading:any) => {
try {
const res = await api.post("/dockDoor/startLoad", {
"loadingOrder":String(loadingOrder),
"dockId":dockId
},{validateStatus: (status) => status < 500,})
if (!res.data.success) {
toast.error(res.data.message)
refetch()
refetchActiveLoading()
return
}
toast.success(res.data.message)
refetch()
refetchActiveLoading()
} catch (error) {
console.log(error)
toast.error(`Encountered error: ${JSON.stringify(error)}`)
}
}
function RouteComponent() {
const { dock } = Route.useParams();
const { data, refetch } = useSuspenseQuery(getActiveDockScanners());
const { data: loadingPlanItems, refetch: refetchActiveLoading } = useSuspenseQuery(getActiveLoadingOrders());
const dockData = data.filter((i: any) => i.dockId === dock);
const loadingPlans = loadingPlanItems.filter(
(l: any) => l.dockId === Number(dock),
);
console.log(dockData[0].currentLoadingOrder === "");
return (
<div className="flex flex-col">
<div className="flex justify-center">
<p>Please select an active loading order for {dockData[0].name}</p>
</div>
<div className="flex justify-center mt-5 gap-2">
{loadingPlans &&
loadingPlans.length > 0 &&
loadingPlans.map((i: any) => {
return (
<Card key={i.id} className="max-w-96">
<CardHeader>
Loading order ID: {i.id}{" "}
<p>
Loading Date: {format(i.loadingDate, "MM/dd/yyyy HH:mm")}
</p>
</CardHeader>
<CardDescription className="">
<p className="p-3">
Below are the loading order details please validate the
loading order you are selecting before pressing start.
</p>
</CardDescription>
{/* Mapping the items out so in case we have more than 1 it will display correctly */}
<CardContent>
{i?.loadingPlanItems?.map((l: any) => {
return (
<div key={i.id}>
<p>Loading Sequence {l.loadingSequence}</p>
<p>
Article: {l.articleId} - {l.articleDescription}
</p>
<p>
Current loaded: {l.loadedQuantityLUs}/
{l.plannedQuantityLUs}
</p>
{l.remark !== "" && <p>Remark: {l.remark}</p>}
{i?.loadingPlanItems.length > 1 && (
<Separator className="m-2" />
)}
</div>
);
})}
</CardContent>
<CardFooter>
<div className="flex flex-row justify-between">
<Button
onClick={()=> startOrder(i.id, dock, refetch, refetchActiveLoading)}
disabled={dockData[0].currentLoadingOrder !== "" ? true : false}>Start Loading</Button>
<Button>Scan progress</Button>
<Button onClick={()=> finishLoadingOrder(String(i.id), dock, refetch, refetchActiveLoading)}
disabled={dockData[0].currentLoadingOrder === "" ? true : false}>Finish Loading</Button>
</div>
</CardFooter>
</Card>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,10 @@
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/warehouse/dockdoorscanning/$dockScans")({
component: RouteComponent,
});
function RouteComponent() {
const { dockScans } = Route.useParams();
return <div>Hello "/warehouse/dockdoorscanning/$docScans"! {dockScans}</div>;
}

View File

@@ -0,0 +1,120 @@
import { useSuspenseQuery } from "@tanstack/react-query";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { Card, CardContent, CardDescription, CardHeader } from "../../../components/ui/card";
import { getActiveLoadingOrders } from "../../../lib/queries/getActiveDockScanners";
import { getActiveDockScanners } from "../../../lib/queries/getActiveLoadingOrders";
import { Button } from "../../../components/ui/button";
import { api } from "../../../lib/apiHelper";
import { toast } from "sonner";
export const Route = createFileRoute("/warehouse/dockdoorscanning/")({
component: RouteComponent,
});
export const finishLoadingOrder = async (loadingOrder: string, dockId: string, refetch:any, refetchActiveLoading:any) => {
try {
const res = await api.post("/dockDoor/finishOrder", {
"loadingOrder":loadingOrder,
"dockId":dockId
},{validateStatus: (status) => status < 500,})
if (!res.data.success) {
toast.error(res.data.message)
refetch()
refetchActiveLoading()
return
}
toast.info(res.data.message)
refetch()
refetchActiveLoading()
} catch (error) {
console.log(error)
toast.error(`Encountered error: ${JSON.stringify(error)}`)
}
}
function RouteComponent() {
const { data, isLoading, refetch } = useSuspenseQuery(getActiveDockScanners());
const { data: loadingPlanItems, refetch : refetchActiveLoading } = useSuspenseQuery(getActiveLoadingOrders());
const navigate = useNavigate();
if (isLoading) {
return (
<div className="flex justify-center-safe">
<p>Loading active dock scanners....</p>
</div>
);
}
return (
<div className="flex flex-col ">
<div className="flex justify-center-safe">
<p className="text-2xl underline">
Select a dock you would like to work with
</p>
</div>
<div className="flex justify-center gap-3 mt-5">
{!isLoading &&
data.length > 0 &&
data.map((i: any) => {
const loadingPlan =
i.currentLoadingOrder !== ""
? loadingPlanItems.filter(
(x: any) => x.id === Number(i.currentLoadingOrder),
)
: [];
return (
<Card
key={i.id}
className="max-w-96"
onClick={() => {
if (i.currentLoadingOrder === "") {
navigate({
to: "/warehouse/dockdoorscanning/$dock",
params: { dock: i.dockId },
search: {
name: i.name,
},
});
}
}}
>
<CardHeader className="text-center">{i.name}</CardHeader>
<CardDescription>For Abbott loads reminder: 3 lots per load max.</CardDescription>
{i.currentLoadingOrder !== "" ? (
<CardContent>
<div className="flex flex-col gap-2">
Current loading order: {i.currentLoadingOrder}
</div>
<div>
{loadingPlan && loadingPlan.length !== 0 ? (<>
<p>{`${loadingPlan[0].loadingPlanItems[0].articleId} - ${loadingPlan[0].loadingPlanItems[0].articleDescription}`}</p>
<p>Current Loaded :{" "}
{loadingPlan[0].loadingPlanItems[0].loadedQuantityLUs} /{" "}
{loadingPlan[0].loadingPlanItems[0].plannedQuantityLUs}</p></>
): <><p>The Current Loading order is invalid</p>
<p> It appears it could have been finished via another process</p>
<div className="m-2 flex justify-center">
<Button onClick={()=> finishLoadingOrder(i.currentLoadingOrder, i.dockId, refetch, refetchActiveLoading)}>Clear {i.currentLoadingOrder}</Button></div></>}</div>
</CardContent>
) : (
<CardContent>
No active loading orders please click me to select an order
</CardContent>
)}
</Card>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,13 @@
CREATE TABLE "dock_door_scans" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"loading_order" text NOT NULL,
"loading_Unit" text,
"loading_unit_status" text DEFAULT 'loaded',
"message" text,
"status" text DEFAULT 'active',
"add_date" timestamp with time zone DEFAULT now(),
"add_user" text DEFAULT 'lst-system',
"upd_date" timestamp with time zone DEFAULT now(),
"upd_user" text DEFAULT 'lst-system',
CONSTRAINT "dock_door_scans_loading_Unit_unique" UNIQUE("loading_Unit")
);

View File

@@ -0,0 +1 @@
ALTER TABLE "dock_door_scans" ADD COLUMN "dock_id" text NOT NULL;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -421,6 +421,20 @@
"when": 1780349486711, "when": 1780349486711,
"tag": "0059_sparkling_joystick", "tag": "0059_sparkling_joystick",
"breakpoints": true "breakpoints": true
},
{
"idx": 60,
"version": "7",
"when": 1780584999699,
"tag": "0060_freezing_hercules",
"breakpoints": true
},
{
"idx": 61,
"version": "7",
"when": 1780692629119,
"tag": "0061_wide_marrow",
"breakpoints": true
} }
] ]
} }