Compare commits
4 Commits
c509c7fe28
...
564f0b5add
| Author | SHA1 | Date | |
|---|---|---|---|
| 564f0b5add | |||
| 7b630d5c0b | |||
| eb6b9ce388 | |||
| c777395b03 |
1
.gitignore
vendored
@@ -13,7 +13,6 @@ controllerBuilds
|
||||
# ignoring the old app that will be built into this one to make deploying faster and more easy as we do the migration
|
||||
lstV2/frontend/.tanstack
|
||||
|
||||
mobileLst
|
||||
keys
|
||||
# Logs
|
||||
logs
|
||||
|
||||
6
.vscode/settings.json
vendored
@@ -55,5 +55,9 @@
|
||||
"intiallally",
|
||||
"ppoo",
|
||||
"prodlabels"
|
||||
]
|
||||
],
|
||||
"gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db",
|
||||
"gitea.instanceURL": "https://git.tuffraid.net",
|
||||
"gitea.owner": "cowch",
|
||||
"gitea.repo": "lst"
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@
|
||||
"category": "quality",
|
||||
"active": false,
|
||||
"icon": "",
|
||||
"link": "",
|
||||
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
|
||||
"link": "/lst/app/old/quality",
|
||||
"roles": ["admin", "systemAdmin", "manager", "supervisor", "tester"]
|
||||
},
|
||||
{
|
||||
"name": "eom",
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function TableNoExpand({
|
||||
});
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="w-fit">
|
||||
<div className="">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Route as AppAdminLayoutRouteRouteImport } from './routes/_app/_adminLay
|
||||
import { Route as OldOldIndexRouteImport } from './routes/_old/old/index'
|
||||
import { Route as AppauthLoginRouteImport } from './routes/_app/(auth)/login'
|
||||
import { Route as OldOldRfidIndexRouteImport } from './routes/_old/old/rfid/index'
|
||||
import { Route as OldOldQualityIndexRouteImport } from './routes/_old/old/quality/index'
|
||||
import { Route as OldOldOcpIndexRouteImport } from './routes/_old/old/ocp/index'
|
||||
import { Route as MobileMobileLayoutMIndexRouteImport } from './routes/_mobile/_mobileLayout/m/index'
|
||||
import { Route as AppForkliftsForkliftsIndexRouteImport } from './routes/_app/_forklifts/forklifts/index'
|
||||
@@ -107,6 +108,11 @@ const OldOldRfidIndexRoute = OldOldRfidIndexRouteImport.update({
|
||||
path: '/rfid/',
|
||||
getParentRoute: () => OldOldRouteRoute,
|
||||
} as any)
|
||||
const OldOldQualityIndexRoute = OldOldQualityIndexRouteImport.update({
|
||||
id: '/quality/',
|
||||
path: '/quality/',
|
||||
getParentRoute: () => OldOldRouteRoute,
|
||||
} as any)
|
||||
const OldOldOcpIndexRoute = OldOldOcpIndexRouteImport.update({
|
||||
id: '/ocp/',
|
||||
path: '/ocp/',
|
||||
@@ -303,6 +309,7 @@ export interface FileRoutesByFullPath {
|
||||
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
||||
'/m': typeof MobileMobileLayoutMIndexRoute
|
||||
'/old/ocp': typeof OldOldOcpIndexRoute
|
||||
'/old/quality': typeof OldOldQualityIndexRoute
|
||||
'/old/rfid': typeof OldOldRfidIndexRoute
|
||||
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||
@@ -339,6 +346,7 @@ export interface FileRoutesByTo {
|
||||
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
||||
'/m': typeof MobileMobileLayoutMIndexRoute
|
||||
'/old/ocp': typeof OldOldOcpIndexRoute
|
||||
'/old/quality': typeof OldOldQualityIndexRoute
|
||||
'/old/rfid': typeof OldOldRfidIndexRoute
|
||||
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||
@@ -383,6 +391,7 @@ export interface FileRoutesById {
|
||||
'/_app/_forklifts/forklifts/': typeof AppForkliftsForkliftsIndexRoute
|
||||
'/_mobile/_mobileLayout/m/': typeof MobileMobileLayoutMIndexRoute
|
||||
'/_old/old/ocp/': typeof OldOldOcpIndexRoute
|
||||
'/_old/old/quality/': typeof OldOldQualityIndexRoute
|
||||
'/_old/old/rfid/': typeof OldOldRfidIndexRoute
|
||||
'/_app/_adminLayout/admin/_system/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||
'/_app/_adminLayout/admin/_system/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||
@@ -422,6 +431,7 @@ export interface FileRouteTypes {
|
||||
| '/forklifts'
|
||||
| '/m'
|
||||
| '/old/ocp'
|
||||
| '/old/quality'
|
||||
| '/old/rfid'
|
||||
| '/admin/modules'
|
||||
| '/admin/settings'
|
||||
@@ -458,6 +468,7 @@ export interface FileRouteTypes {
|
||||
| '/forklifts'
|
||||
| '/m'
|
||||
| '/old/ocp'
|
||||
| '/old/quality'
|
||||
| '/old/rfid'
|
||||
| '/admin/modules'
|
||||
| '/admin/settings'
|
||||
@@ -501,6 +512,7 @@ export interface FileRouteTypes {
|
||||
| '/_app/_forklifts/forklifts/'
|
||||
| '/_mobile/_mobileLayout/m/'
|
||||
| '/_old/old/ocp/'
|
||||
| '/_old/old/quality/'
|
||||
| '/_old/old/rfid/'
|
||||
| '/_app/_adminLayout/admin/_system/modules'
|
||||
| '/_app/_adminLayout/admin/_system/settings'
|
||||
@@ -602,6 +614,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof OldOldRfidIndexRouteImport
|
||||
parentRoute: typeof OldOldRouteRoute
|
||||
}
|
||||
'/_old/old/quality/': {
|
||||
id: '/_old/old/quality/'
|
||||
path: '/quality'
|
||||
fullPath: '/old/quality'
|
||||
preLoaderRoute: typeof OldOldQualityIndexRouteImport
|
||||
parentRoute: typeof OldOldRouteRoute
|
||||
}
|
||||
'/_old/old/ocp/': {
|
||||
id: '/_old/old/ocp/'
|
||||
path: '/ocp'
|
||||
@@ -949,6 +968,7 @@ const MobileMobileLayoutRouteRouteWithChildren =
|
||||
interface OldOldRouteRouteChildren {
|
||||
OldOldIndexRoute: typeof OldOldIndexRoute
|
||||
OldOldOcpIndexRoute: typeof OldOldOcpIndexRoute
|
||||
OldOldQualityIndexRoute: typeof OldOldQualityIndexRoute
|
||||
OldOldRfidIndexRoute: typeof OldOldRfidIndexRoute
|
||||
OldOldlogisticsSiloAdjustmentsHistRoute: typeof OldOldlogisticsSiloAdjustmentsHistRoute
|
||||
OldOldlogisticsBarcodegenIndexRoute: typeof OldOldlogisticsBarcodegenIndexRoute
|
||||
@@ -964,6 +984,7 @@ interface OldOldRouteRouteChildren {
|
||||
const OldOldRouteRouteChildren: OldOldRouteRouteChildren = {
|
||||
OldOldIndexRoute: OldOldIndexRoute,
|
||||
OldOldOcpIndexRoute: OldOldOcpIndexRoute,
|
||||
OldOldQualityIndexRoute: OldOldQualityIndexRoute,
|
||||
OldOldRfidIndexRoute: OldOldRfidIndexRoute,
|
||||
OldOldlogisticsSiloAdjustmentsHistRoute:
|
||||
OldOldlogisticsSiloAdjustmentsHistRoute,
|
||||
|
||||
@@ -29,6 +29,7 @@ export function AddCards() {
|
||||
<Cards name={"ppoo"} inventory />
|
||||
<Cards name={"inv-empty"} rowType={"empty"} />
|
||||
<Cards name={"inv-fg"} rowType={"fg"} />
|
||||
<Cards name={"qualityCard"} />
|
||||
</div>
|
||||
<div className="">
|
||||
<Cards name={"inv-materials"} rowType={"materials"} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCardStore } from "../../-lib/store/useCardStore";
|
||||
import Quality from "../../quality/-components/WarehouseCard";
|
||||
import INVCheckCard from "../logistics/warehouse/InventoryCard";
|
||||
import OpenOrders from "../logistics/warehouse/openOrders";
|
||||
import PPOO from "../logistics/warehouse/PPOOCard";
|
||||
@@ -7,6 +8,7 @@ const componentsMap: any = {
|
||||
ppoo: PPOO,
|
||||
inv: INVCheckCard,
|
||||
openOrder: OpenOrders,
|
||||
qualityCard: Quality,
|
||||
//QualityRequest,
|
||||
};
|
||||
|
||||
@@ -26,14 +28,21 @@ export default function DashBoard() {
|
||||
<Component age={a.age} type={a.rowType} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (name === "qualityCard") {
|
||||
return (
|
||||
<div key={a.name} className="col-span-6">
|
||||
<Component />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//console.log(name.split("-")[0], a);
|
||||
return (
|
||||
<div key={a.name} className="col-span-3">
|
||||
<Component data={a} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { AdminSideBar } from "./side-components/admin";
|
||||
import { Header } from "./side-components/header";
|
||||
import { LogisticsSideBar } from "./side-components/logistics";
|
||||
import { ProductionSideBar } from "./side-components/production";
|
||||
import { QualitySideBar } from "./side-components/quality";
|
||||
|
||||
export function AppSidebar() {
|
||||
const { session } = useAuth();
|
||||
@@ -31,11 +32,12 @@ export function AppSidebar() {
|
||||
|
||||
{/* userAccess("logistics", ["systemAdmin", "admin","manager","viewer"]) */}
|
||||
<LogisticsSideBar user={session?.user as any} userRoles={userRoles} />
|
||||
<QualitySideBar user={session?.user as any} userRoles={userRoles} />
|
||||
{userAccess(null, ["systemAdmin"]) && (
|
||||
<>
|
||||
{/* <ForkliftSideBar />
|
||||
<EomSideBar />
|
||||
<QualitySideBar /> */}
|
||||
*/}
|
||||
<AdminSideBar />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Printer } from "lucide-react";
|
||||
import { Cat, Printer } from "lucide-react";
|
||||
import type { UserRoles } from "@/lib/authClient";
|
||||
import type { User } from "@/types";
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
@@ -7,31 +9,49 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "../../../../../../components/ui/sidebar";
|
||||
import { useModuleStore } from "../../../-lib/store/useModuleStore";
|
||||
import { hasPageAccess } from "../../../-utils/userAccess";
|
||||
|
||||
const items = [
|
||||
{
|
||||
title: "Qaulity Request",
|
||||
url: "#",
|
||||
icon: Printer,
|
||||
},
|
||||
];
|
||||
const iconMap: any = {
|
||||
Printer: Printer,
|
||||
Cat: Cat,
|
||||
};
|
||||
|
||||
export function QualitySideBar({
|
||||
user,
|
||||
userRoles,
|
||||
}: {
|
||||
user: User | null;
|
||||
userRoles: UserRoles[] | null;
|
||||
}) {
|
||||
const { modules } = useModuleStore();
|
||||
|
||||
const items = modules?.filter((m) => m.category === "quality" && m.active);
|
||||
const userUpdate = { ...user, roles: userRoles };
|
||||
|
||||
export function QualitySideBar() {
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Quality</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
{items.map((item) => {
|
||||
if (!item.active) return;
|
||||
const Icon = iconMap[item.icon === "" ? "Cat" : item.icon];
|
||||
return (
|
||||
<SidebarMenuItem key={item.module_id}>
|
||||
<>
|
||||
{hasPageAccess(userUpdate as any, item.roles, item.name) && (
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
<a href={item.link}>
|
||||
<Icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
</>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
export function getPallets() {
|
||||
return queryOptions({
|
||||
queryKey: ["getPallets"],
|
||||
queryFn: () => fetch(),
|
||||
//enabled:
|
||||
staleTime: 1000,
|
||||
refetchInterval: 60 * 1000,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
}
|
||||
|
||||
const fetch = async () => {
|
||||
const { data } = await axios.get(`/lst/old/api/quality/getrequest`);
|
||||
// if we are not localhost ignore the devDir setting.
|
||||
//const url: string = window.location.host.split(":")[0];
|
||||
return data.data ?? [];
|
||||
};
|
||||
@@ -0,0 +1,331 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import axios from "axios";
|
||||
import { ArrowDown, ArrowUp } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useAuth, userAccess } from "@/lib/authClient";
|
||||
import TableNoExpand from "@/lib/tableStuff/TableNoExpand";
|
||||
import { getPallets } from "../../-utils/querys/quality/getPallets";
|
||||
|
||||
type Pallets = {
|
||||
request_id: string;
|
||||
article: string;
|
||||
description: string;
|
||||
runningNr: string;
|
||||
lotNr: string;
|
||||
warehouseAtRequest: string;
|
||||
locationAtRequest: string;
|
||||
warehouseMovedTo: string;
|
||||
locationMovedTo: string;
|
||||
durationToMove: null;
|
||||
qualityDurationToInspect: number;
|
||||
returnDurationToInspect: number;
|
||||
locationDropOff: string;
|
||||
palletStatus: number;
|
||||
palletStatusText: string;
|
||||
palletRequest: number;
|
||||
priority: number;
|
||||
add_date: Date;
|
||||
add_user: string;
|
||||
upd_date: Date;
|
||||
upd_user: string;
|
||||
};
|
||||
|
||||
export default function QualityRequest() {
|
||||
const { data, isLoading, refetch } = useQuery(getPallets());
|
||||
const columnHelper = createColumnHelper<Pallets>();
|
||||
const { session } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const router = useRouterState();
|
||||
const currentPath = router.location.href;
|
||||
|
||||
const palletCompleted = async (e: any) => {
|
||||
if (!session || !session.user) {
|
||||
toast.error("You are allowed to do this unless you are logged in");
|
||||
navigate({ to: "/login", search: { redirect: currentPath } });
|
||||
return;
|
||||
}
|
||||
const data = {
|
||||
username: session?.user.username,
|
||||
runningNr: Number(e.original.runningNr),
|
||||
palletStatusText: "return",
|
||||
};
|
||||
try {
|
||||
const res = await axios.post("/lst/old/api/quality/newrequest", data);
|
||||
|
||||
//console.log(res.data);
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
refetch();
|
||||
}
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Encountered and error please try again");
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("article", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Article</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("description", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Alias</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("runningNr", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Running Number</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("lotNr", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Lot Number</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("locationAtRequest", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Location At Request</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("locationDropOff", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Drop off Location</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ getValue }) => {
|
||||
return (
|
||||
<>
|
||||
<span>{getValue() ? getValue() : "missing dropoff location"}</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("palletStatusText", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Pallet Status</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("palletRequest", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Check Completed</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
// if pending
|
||||
const okToCompleteStats = [2, 4, 5];
|
||||
return (
|
||||
<>
|
||||
{okToCompleteStats.includes(row.original.palletStatus) ? (
|
||||
<Button variant="outline" onClick={() => palletCompleted(row)}>
|
||||
Check Complete
|
||||
</Button>
|
||||
) : (
|
||||
<span>Pending to be completed</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
let adminColumns: any = [];
|
||||
if (userAccess("quality", ["systemAdmin", "admin", "supervisor"])) {
|
||||
adminColumns = [
|
||||
...columns,
|
||||
columnHelper.accessor("priority", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Priority</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row, getValue }) => {
|
||||
// if pending
|
||||
|
||||
const [p, setP] = useState(`${getValue()}`);
|
||||
console.log(getValue());
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
value={p}
|
||||
onValueChange={async (value) => {
|
||||
setP(value);
|
||||
const data = {
|
||||
username: session?.user.username,
|
||||
runningNr: Number(row.original.runningNr),
|
||||
priority: value,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios.post(
|
||||
"/lst/old/api/quality/newrequest",
|
||||
data,
|
||||
);
|
||||
|
||||
//console.log(res.data);
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
refetch();
|
||||
}
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Encountered and error please try again");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger
|
||||
// className={cn(
|
||||
// "w-[100px]",
|
||||
// active
|
||||
// ? "border-green-500 text-green-600"
|
||||
// : "border-gray-400 text-gray-500",
|
||||
// )}
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1">Urgent</SelectItem>
|
||||
<SelectItem value="2">High</SelectItem>
|
||||
<SelectItem value="3">Medium</SelectItem>
|
||||
<SelectItem value="4">Low</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</>
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
if (isLoading) {
|
||||
return <div className="m-auto">Loading user data</div>;
|
||||
}
|
||||
return <TableNoExpand data={data} columns={adminColumns} />;
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
import axios from "axios";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useAuth } from "@/lib/authClient";
|
||||
import { useAppForm } from "@/lib/formStuff";
|
||||
import { getPallets } from "../../-utils/querys/quality/getPallets";
|
||||
|
||||
const areaSelection = [
|
||||
{ value: "rework", label: "Rework" },
|
||||
{ value: "holdArea", label: "Hold Area" },
|
||||
{ value: "inspectionArea", label: "Inspection Area" },
|
||||
];
|
||||
export default function RequestPallet() {
|
||||
const { session } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const router = useRouterState();
|
||||
const { refetch } = useQuery(getPallets());
|
||||
const currentPath = router.location.href;
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
username: "",
|
||||
runningNr: 0,
|
||||
moveTo: "",
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
if (!session || !session.user) {
|
||||
toast.error("You are allowed to do this unless you are logged in");
|
||||
navigate({ to: "/login", search: { redirect: currentPath } });
|
||||
return;
|
||||
}
|
||||
if (value.runningNr === 0) {
|
||||
return toast.error(
|
||||
"You did not change the running number, please add a valid running number",
|
||||
);
|
||||
}
|
||||
const postData = {
|
||||
runningNr: Number(value.runningNr),
|
||||
moveTo: value.moveTo,
|
||||
username: session?.user.username,
|
||||
};
|
||||
|
||||
console.log(postData);
|
||||
try {
|
||||
const res = await axios.post(
|
||||
"/lst/old/api/quality/newrequest",
|
||||
postData,
|
||||
);
|
||||
|
||||
console.log(res.data);
|
||||
|
||||
if (res.data.success) {
|
||||
toast.success(res.data.message);
|
||||
form.reset();
|
||||
refetch();
|
||||
}
|
||||
|
||||
if (!res.data.success) {
|
||||
toast.error(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error("Encountered and error please try again");
|
||||
}
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<Button variant="outline">Request Pallet</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-5/6 m-2">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Request a pallet for the warehouse to retrieve{" "}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
This form is used to request a pallet to be brought up to be
|
||||
inspected, reworked, placed on hold, or please fill out and the
|
||||
warehouse will see and bring to the designated location. Once
|
||||
finished click done or cancel.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<div className="w-5/6 m-2">
|
||||
<form.AppField
|
||||
name="runningNr"
|
||||
children={(field) => (
|
||||
<field.InputField
|
||||
label="Pallet Running Number"
|
||||
inputType="number"
|
||||
required={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-5/6 m-2">
|
||||
<form.AppField
|
||||
name="moveTo"
|
||||
// listeners={{
|
||||
// onChange: ({ value }) => {
|
||||
// onValueChange(value);
|
||||
// },
|
||||
// }}
|
||||
children={(field) => (
|
||||
<field.SelectField
|
||||
label="Select Area"
|
||||
placeholder="Quality Areas"
|
||||
options={areaSelection}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-5/6 m-2">
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
form.reset();
|
||||
}}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit">Submit</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
//import { LstCard } from "@/components/extendedUI/LstCard";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { ArrowDown, ArrowUp } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardHeader } from "@/components/ui/card";
|
||||
import TableNoExpand from "@/lib/tableStuff/TableNoExpand";
|
||||
import { LstCard } from "../../-components/extendedUi/LstCard";
|
||||
import { getPallets } from "../../-utils/querys/quality/getPallets";
|
||||
|
||||
//import { CircleX } from "lucide-react";
|
||||
//import { Suspense } from "react";
|
||||
//import { toast } from "sonner";
|
||||
|
||||
type Pallets = {
|
||||
article: string;
|
||||
runningNr: string;
|
||||
warehouseAtRequest: string;
|
||||
locationAtRequest: string;
|
||||
locationDropOff: string;
|
||||
};
|
||||
|
||||
export default function PPOO() {
|
||||
//{ style = {} }
|
||||
const { data, isError, isLoading } = useQuery(getPallets());
|
||||
const columnHelper = createColumnHelper<Pallets>();
|
||||
|
||||
if (isLoading) return <div>Loading pallet data...</div>;
|
||||
if (isError) {
|
||||
return (
|
||||
<div>
|
||||
<p>There was an error getting the pallets.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const monitoring = [1, 4, 6, 7];
|
||||
const filteredData = data.filter((n: any) =>
|
||||
monitoring.includes(n.palletStatus),
|
||||
);
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("article", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Article</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("runningNr", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Running Number</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("locationAtRequest", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Location</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("locationDropOff", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Drop off at</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
return (
|
||||
<LstCard className="w-fit">
|
||||
<CardHeader>
|
||||
Quality Request Card Total Pallets {filteredData.length}
|
||||
</CardHeader>
|
||||
<TableNoExpand data={filteredData} columns={columns} />
|
||||
</LstCard>
|
||||
);
|
||||
}
|
||||
44
frontend/src/routes/_old/old/quality/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
import { checkUserAccess } from "@/lib/authClient";
|
||||
import QualityRequest from "./-components/QualityRequest";
|
||||
import RequestPallet from "./-components/RequestPallet";
|
||||
|
||||
export const Route = createFileRoute("/_old/old/quality/")({
|
||||
component: RouteComponent,
|
||||
beforeLoad: async () => {
|
||||
const auth = await checkUserAccess({
|
||||
allowedRoles: [
|
||||
"systemAdmin",
|
||||
"technician",
|
||||
"admin",
|
||||
"manager",
|
||||
"supervisor",
|
||||
],
|
||||
moduleName: "quality", // optional
|
||||
});
|
||||
|
||||
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 className="flex flex-col m-2">
|
||||
<div className="flex justify-items-start ml-2">
|
||||
<RequestPallet />
|
||||
</div>
|
||||
|
||||
<QualityRequest />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1
lstV2/database/migrations/0076_stiff_champions.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "qualityRequest" ADD COLUMN "priority" integer DEFAULT 4;
|
||||
2278
lstV2/database/migrations/meta/0076_snapshot.json
Normal file
@@ -533,6 +533,13 @@
|
||||
"when": 1762983466464,
|
||||
"tag": "0075_tan_unicorn",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 76,
|
||||
"version": "7",
|
||||
"when": 1763134709763,
|
||||
"tag": "0076_stiff_champions",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
text,
|
||||
pgTable,
|
||||
numeric,
|
||||
index,
|
||||
timestamp,
|
||||
boolean,
|
||||
uuid,
|
||||
uniqueIndex,
|
||||
index,
|
||||
integer,
|
||||
numeric,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
@@ -31,6 +31,7 @@ export const qualityRequest = pgTable(
|
||||
palletStatus: integer("palletStatus"),
|
||||
palletStatusText: text("palletStatusText"),
|
||||
palletRequest: integer("palletRequest"),
|
||||
priority: integer("priority").default(4), // 1,2,3,4
|
||||
add_date: timestamp("add_date").defaultNow(),
|
||||
add_user: text("add_user").default("LST"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
@@ -39,7 +40,7 @@ export const qualityRequest = pgTable(
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
// uniqueIndex("role_name").on(table.name),
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
// Schema for inserting a user - can be used to validate API requests
|
||||
|
||||
@@ -6,9 +6,26 @@ import {
|
||||
parseISO,
|
||||
startOfWeek,
|
||||
} from "date-fns";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { invHistoricalData } from "../../../../../database/schema/historicalINV.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../../sqlServer/prodSqlServer.js";
|
||||
import { materialPerDay } from "../../../sqlServer/querys/dataMart/materialPerDay.js";
|
||||
import {
|
||||
materialPerDay,
|
||||
materialPurchasesPerDay,
|
||||
} from "../../../sqlServer/querys/dataMart/materialPerDay.js";
|
||||
import { materialPurchases } from "./materialPurchases.js";
|
||||
import { buildInventoryTimeline } from "./materialWithInv.js";
|
||||
|
||||
const getInv = async () => {
|
||||
const { data, error } = await tryCatch(db.select().from(invHistoricalData));
|
||||
|
||||
if (error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
function toDate(val: any) {
|
||||
if (val instanceof Date) return val;
|
||||
@@ -16,7 +33,7 @@ function toDate(val: any) {
|
||||
return new Date(val);
|
||||
}
|
||||
|
||||
export function sumByMaterialAndWeek(data: any) {
|
||||
function sumByMaterialAndWeek(data: any) {
|
||||
/** @type {Record<string, Record<string, number>>} */
|
||||
const grouped: any = {};
|
||||
|
||||
@@ -80,9 +97,45 @@ export default async function materialPerDayCheck() {
|
||||
};
|
||||
}
|
||||
|
||||
// purchases
|
||||
|
||||
const { data: p, error: pe } = (await tryCatch(
|
||||
query(
|
||||
materialPurchasesPerDay
|
||||
.replace("[startDate]", startDate)
|
||||
.replace("[endDate]", endDate),
|
||||
"material check",
|
||||
),
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the material data",
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
message: data.message,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
const openingInventory: Record<string, number> = {};
|
||||
const inventoryRows = await getInv();
|
||||
for (const row of inventoryRows) {
|
||||
openingInventory[row.article] = Number(row.total_QTY) || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "material data",
|
||||
data: sumByMaterialAndWeek(data.data),
|
||||
data: buildInventoryTimeline(
|
||||
sumByMaterialAndWeek(data.data) as any,
|
||||
openingInventory,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { formatISO, parseISO, startOfWeek } from "date-fns";
|
||||
|
||||
function toDate(val: any) {
|
||||
if (val instanceof Date) return val;
|
||||
if (typeof val === "string") return parseISO(val.replace(" ", "T"));
|
||||
return new Date(val);
|
||||
}
|
||||
|
||||
export const materialPurchases = async (data: any) => {
|
||||
/** @type {Record<string, Record<string, number>>} */
|
||||
const grouped: any = {};
|
||||
|
||||
for (const r of data) {
|
||||
const mat = String(r.MaterialHumanReadableId);
|
||||
const d = toDate(r.CalDate);
|
||||
const week = formatISO(startOfWeek(d, { weekStartsOn: 1 }), {
|
||||
representation: "date",
|
||||
});
|
||||
|
||||
grouped[mat] ??= {};
|
||||
grouped[mat][week] ??= 0;
|
||||
grouped[mat][week] += Number(r.DailyMaterialDemand) || 0;
|
||||
}
|
||||
|
||||
const result = [];
|
||||
for (const [mat, weeks] of Object.entries(grouped)) {
|
||||
// @ts-ignore
|
||||
for (const [week, total] of Object.entries(weeks)) {
|
||||
result.push({
|
||||
MaterialHumanReadableId: mat,
|
||||
WeekStart: week,
|
||||
WeeklyDemand: Number(total).toFixed(2),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
export const buildInventoryTimeline = (
|
||||
weeklyDemand: Array<{
|
||||
MaterialHumanReadableId: string;
|
||||
WeekStart: string;
|
||||
WeeklyDemand: number;
|
||||
}>,
|
||||
opening: Record<string, number>,
|
||||
) => {
|
||||
// group weekly demand by material
|
||||
const grouped: Record<
|
||||
string,
|
||||
Array<{ WeekStart: string; Demand: number }>
|
||||
> = {};
|
||||
|
||||
for (const d of weeklyDemand) {
|
||||
const mat = d.MaterialHumanReadableId;
|
||||
grouped[mat] ??= [];
|
||||
grouped[mat].push({
|
||||
WeekStart: d.WeekStart,
|
||||
Demand: Number(d.WeeklyDemand),
|
||||
});
|
||||
}
|
||||
|
||||
// sort weeks chronologically per material
|
||||
for (const mat of Object.keys(grouped)) {
|
||||
grouped[mat].sort(
|
||||
(a, b) =>
|
||||
new Date(a.WeekStart).getTime() - new Date(b.WeekStart).getTime(),
|
||||
);
|
||||
}
|
||||
|
||||
const result: Array<{
|
||||
MaterialHumanReadableId: string;
|
||||
WeekStart: string;
|
||||
OpeningInventory: number;
|
||||
Consumption: number;
|
||||
ClosingInventory: number;
|
||||
}> = [];
|
||||
|
||||
for (const [mat, weeks] of Object.entries(grouped)) {
|
||||
// get starting inventory from the ERP result
|
||||
let inv = opening[mat] ?? 0;
|
||||
|
||||
for (const w of weeks) {
|
||||
const week = w.WeekStart;
|
||||
const demand = Number(w.Demand);
|
||||
|
||||
const openingInv = inv;
|
||||
const closingInv = openingInv - demand;
|
||||
|
||||
result.push({
|
||||
MaterialHumanReadableId: mat,
|
||||
WeekStart: week,
|
||||
OpeningInventory: Number(openingInv.toFixed(2)),
|
||||
Consumption: Number(demand.toFixed(2)),
|
||||
ClosingInventory: Number(closingInv.toFixed(2)),
|
||||
});
|
||||
|
||||
inv = closingInv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -2,8 +2,8 @@
|
||||
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||
import materialPerDayCheck from "../controller/materialsPerDay/materialPerDay.js";
|
||||
import fifoIndexCheck from "../controller/notifications/fifoIndex.js";
|
||||
import materialPerDayCheck from "../controller/notifications/materialPerDay.js";
|
||||
|
||||
const app = new OpenAPIHono({ strict: false });
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { differenceInMinutes } from "date-fns";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { priority } from "st-ethernet-ip/dist/enip/cip/connection-manager/index.js";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
||||
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
|
||||
@@ -39,12 +40,14 @@ export const addNewPallet = async (data: any) => {
|
||||
const palletData: any = c;
|
||||
// if the pallet exist then tell the user to check on it
|
||||
const pStatus = [1, 4, 6];
|
||||
if (!data.priority) {
|
||||
if (palletData && pStatus.includes(palletData[0]?.palletStatus)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Running number ${data.runningNr} is already pending or reactivated please follow up with the warehouse team on status to be moved.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// update the existing pallet if already in the system
|
||||
if (palletData.length > 0) {
|
||||
@@ -61,7 +64,17 @@ export const addNewPallet = async (data: any) => {
|
||||
data: pe,
|
||||
};
|
||||
}
|
||||
const pData = {
|
||||
|
||||
let pData = {};
|
||||
|
||||
if (data.priority) {
|
||||
pData = {
|
||||
priority: data.priority,
|
||||
upd_user: data.user,
|
||||
upd_date: sql`NOW()`,
|
||||
};
|
||||
} else {
|
||||
pData = {
|
||||
warehouseAtRequest: p[0].warehouseAtRequest,
|
||||
locationAtRequest: p[0].locationAtRequest,
|
||||
warehouseMovedTo: null,
|
||||
@@ -80,6 +93,7 @@ export const addNewPallet = async (data: any) => {
|
||||
upd_user: data.user,
|
||||
upd_date: sql`NOW()`,
|
||||
};
|
||||
}
|
||||
|
||||
const { data: u, error } = await tryCatch(
|
||||
db
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { desc } from "drizzle-orm";
|
||||
import { asc, desc } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
@@ -6,7 +6,10 @@ import qualityBlockingMonitor from "../../notifications/controller/notifications
|
||||
|
||||
export const getRequest = async () => {
|
||||
const { data, error } = await tryCatch(
|
||||
db.select().from(qualityRequest).orderBy(desc(qualityRequest.add_date))
|
||||
db
|
||||
.select()
|
||||
.from(qualityRequest)
|
||||
.orderBy(asc(qualityRequest.priority), asc(qualityRequest.add_date)),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -105,7 +105,7 @@ export const qualityCycle = async () => {
|
||||
);
|
||||
} else {
|
||||
createLog(
|
||||
"info",
|
||||
"debug",
|
||||
"lst",
|
||||
"quality",
|
||||
`Pallet ${
|
||||
@@ -119,7 +119,7 @@ export const qualityCycle = async () => {
|
||||
}
|
||||
await delay(150);
|
||||
} else {
|
||||
createLog("info", "lst", "quality", "nothing to update");
|
||||
//createLog("info", "lst", "quality", "nothing to update");
|
||||
}
|
||||
},
|
||||
5 * 60 * 1000,
|
||||
|
||||
@@ -113,3 +113,21 @@ ORDER BY
|
||||
n.ProductionLotHumanReadableId
|
||||
OPTION (MAXRECURSION 0);
|
||||
`;
|
||||
|
||||
export const materialPurchasesPerDay = `
|
||||
use AlplaPROD_test1
|
||||
declare @startDate nvarchar(max) = '[startDate]'
|
||||
declare @endDate nvarchar(max) = '[endDate]'
|
||||
SELECT
|
||||
[IdBestellung] as purhcaseOrder
|
||||
|
||||
,[IdArtikelVarianten]
|
||||
,convert(date, [BestellDatum], 120) as orderDate
|
||||
,convert(date, [Lieferdatum], 120) as deliveryDate
|
||||
,[BestellMenge] as qty
|
||||
,[BestellMengeVPK] as pallets
|
||||
|
||||
FROM [dbo].[T_Bestellpositionen]
|
||||
|
||||
where PositionsStatus = 2 and convert(date, [Lieferdatum], 120) between @startDate and @endDate --and IdBestellung in( 5535,5534,5513)
|
||||
`;
|
||||
|
||||
43
mobileLst/.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
expo-env.d.ts
|
||||
|
||||
# Native
|
||||
.kotlin/
|
||||
*.orig.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
|
||||
# Metro
|
||||
.metro-health-check*
|
||||
|
||||
# debug
|
||||
npm-debug.*
|
||||
yarn-debug.*
|
||||
yarn-error.*
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
app-example
|
||||
|
||||
# generated native folders
|
||||
/ios
|
||||
/android
|
||||
50
mobileLst/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Welcome to your Expo app 👋
|
||||
|
||||
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
||||
|
||||
## Get started
|
||||
|
||||
1. Install dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the app
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
In the output, you'll find options to open the app in a
|
||||
|
||||
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
||||
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
||||
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
||||
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
||||
|
||||
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
||||
|
||||
## Get a fresh project
|
||||
|
||||
When you're ready, run:
|
||||
|
||||
```bash
|
||||
npm run reset-project
|
||||
```
|
||||
|
||||
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about developing your project with Expo, look at the following resources:
|
||||
|
||||
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
||||
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
||||
|
||||
## Join the community
|
||||
|
||||
Join our community of developers creating universal apps.
|
||||
|
||||
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
||||
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
||||
71
mobileLst/app.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "LSTScanner",
|
||||
"slug": "lst-scanner-app",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"scheme": "lstscanner",
|
||||
"userInterfaceStyle": "automatic",
|
||||
"newArchEnabled": true,
|
||||
"ios": {
|
||||
"supportsTablet": true
|
||||
},
|
||||
"updates": {
|
||||
"enabled": true,
|
||||
"url": "http://10.193.0.56:4000/api/mobile/updates",
|
||||
"fallbackToCacheTimeout": 30000,
|
||||
"codeSigningCertificate": "./certs/certificate.pem",
|
||||
"codeSigningMetadata": {
|
||||
"keyid": "self-hosted",
|
||||
"alg": "rsa-v1_5-sha256"
|
||||
}
|
||||
},
|
||||
"runtimeVersion": "1.0.0",
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"backgroundColor": "#E6F4FE",
|
||||
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
||||
"backgroundImage": "./assets/images/android-icon-background.png",
|
||||
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||
},
|
||||
"package": "com.company.lstscanner",
|
||||
"versionCode": 1,
|
||||
"edgeToEdgeEnabled": true,
|
||||
"predictiveBackGestureEnabled": false
|
||||
},
|
||||
"web": {
|
||||
"output": "static",
|
||||
"favicon": "./assets/images/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
"./plugins/androidCustomizations",
|
||||
[
|
||||
"expo-splash-screen",
|
||||
{
|
||||
"image": "./assets/icon.png",
|
||||
"imageWidth": 200,
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff",
|
||||
"dark": {
|
||||
"backgroundColor": "#000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"expo-build-properties",
|
||||
{
|
||||
"android": {
|
||||
"usesCleartextTraffic": true
|
||||
},
|
||||
"ios": {}
|
||||
}
|
||||
]
|
||||
],
|
||||
"experiments": {
|
||||
"typedRoutes": true,
|
||||
"reactCompiler": true
|
||||
}
|
||||
}
|
||||
}
|
||||
5
mobileLst/app/_layout.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function RootLayout() {
|
||||
return <Stack />;
|
||||
}
|
||||
122
mobileLst/app/index.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
FlatList,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from "react-native";
|
||||
import * as Updates from 'expo-updates';
|
||||
|
||||
export default function App() {
|
||||
const [buffer, setBuffer] = useState("");
|
||||
const [items, setItems] = useState<string[]>([]);
|
||||
const [status, setStatus] = useState("");
|
||||
|
||||
const [updates, setUpdates] = useState("")
|
||||
|
||||
const handleChange = (text: string) => {
|
||||
// Scanner "types" characters then appends Enter (\n)
|
||||
if (text.endsWith("\n")) {
|
||||
const code = text.trim(); // Remove newline
|
||||
handleScan(code);
|
||||
setBuffer(""); // Reset for next scan
|
||||
} else {
|
||||
setBuffer(text);
|
||||
}
|
||||
};
|
||||
|
||||
async function checkServerUpdate() {
|
||||
setUpdates('Checking for updates...');
|
||||
try {
|
||||
const res = await Updates.checkForUpdateAsync();
|
||||
if (res.isAvailable) {
|
||||
setUpdates('Update available! Fetching and reloading...');
|
||||
await Updates.fetchUpdateAsync();
|
||||
await Updates.reloadAsync();
|
||||
} else {
|
||||
setUpdates('No new update available.');
|
||||
}
|
||||
} catch (e:any) {
|
||||
setUpdates(`Update check failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const handleScan = (code: string) => {
|
||||
//console.log("Scanned:", code);
|
||||
|
||||
if (code.toUpperCase().startsWith("LOC")) {
|
||||
console.log("Triggered relocate to lane");
|
||||
performRelocate(code);
|
||||
} else {
|
||||
setItems((prev) => [...prev, code]);
|
||||
setStatus(`Added: ${code}`);
|
||||
}
|
||||
};
|
||||
|
||||
const performRelocate = (locationCode: string) => {
|
||||
if (items.length === 0) {
|
||||
setStatus(`Relocate to ${locationCode} requested, but no items`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do your API call or whatever the "relocate" means
|
||||
console.log("Relocating", items, "to", locationCode);
|
||||
|
||||
setStatus(`Moved ${items.length} items to ${locationCode}`);
|
||||
setItems([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button
|
||||
title="Clear Items"
|
||||
onPress={() => {
|
||||
setItems([]);
|
||||
}}
|
||||
/>
|
||||
<Text style={styles.title}>Scan Barcodes</Text>
|
||||
|
||||
{/* The working input you already have */}
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={buffer}
|
||||
onChangeText={handleChange}
|
||||
showSoftInputOnFocus={false}
|
||||
keyboardType="visible-password"
|
||||
style={{
|
||||
position: "absolute",
|
||||
opacity: 0, // invisible
|
||||
height: 0, // takes no space
|
||||
width: 0,
|
||||
}}
|
||||
/>
|
||||
<Pressable onPress={checkServerUpdate}style={{
|
||||
marginVertical: 12,
|
||||
padding: 12,
|
||||
backgroundColor: '#007AFF',
|
||||
borderRadius: 6,
|
||||
}}>
|
||||
<Text>Check Update</Text>
|
||||
</Pressable>
|
||||
<Text>Update Data</Text>
|
||||
<Text>{updates}</Text>
|
||||
<FlatList
|
||||
data={items}
|
||||
keyExtractor={(_, i) => i.toString()}
|
||||
renderItem={({ item }) => <Text style={styles.item}>{item}</Text>}
|
||||
/>
|
||||
|
||||
<Text style={styles.status}>{status}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, padding: 16 },
|
||||
title: { fontWeight: "bold", fontSize: 20 },
|
||||
item: { paddingVertical: 4, fontSize: 16 },
|
||||
status: { marginTop: 20, fontStyle: "italic" },
|
||||
});
|
||||
BIN
mobileLst/assets/icon.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
mobileLst/assets/images/android-icon-background.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
mobileLst/assets/images/android-icon-foreground.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
mobileLst/assets/images/android-icon-monochrome.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
mobileLst/assets/images/favicon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
mobileLst/assets/images/icon.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
mobileLst/assets/images/partial-react-logo.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
mobileLst/assets/images/react-logo.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
mobileLst/assets/images/react-logo@2x.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mobileLst/assets/images/react-logo@3x.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
mobileLst/assets/images/splash-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
31
mobileLst/certFiles/raw/alpla_root_ca.crt
Normal file
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRzCCAy+gAwIBAgIQF/BpAuRICIFJboP3h6vDRzANBgkqhkiG9w0BAQ0FADA1
|
||||
MQswCQYDVQQGEwJBVDEOMAwGA1UEChMFQUxQTEExFjAUBgNVBAMTDUFMUExBIFJv
|
||||
b3QgQ0EwIBcNMjAxMDI3MTIzNTUxWhgPMjA1MDEwMjcxMjQ1NTBaMDUxCzAJBgNV
|
||||
BAYTAkFUMQ4wDAYDVQQKEwVBTFBMQTEWMBQGA1UEAxMNQUxQTEEgUm9vdCBDQTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKt7ZejwqZz7AvCN7zCSAxVZ
|
||||
Htw/WKX3UezouDBdWY6UQh54POCTEwv5BZ8wiAp1GvkJSbUVbB/9VBNZtopAGFkp
|
||||
pB5MAjoPY/PWir01vxymgDXDOEn8Big/ahUt7MQI02UwpQ/j0fwxcVtWOxyoKN1O
|
||||
E1yR3vJfXOwGZ4y8zIN2ErBg+z1CUXUS/UxfpmAdQ6vJgYL6pjcCxKOHPdYo7a2H
|
||||
RD3Ck6qEcD0K89/Cx3R5Q3poc3YKM3A3xnIb43wUhzIXDdPQtIxMA4dA/Tz8LAWE
|
||||
ZQ/6bkOpDFPZMxy4x5mcIMo6Ak0CIOFLoWfnukcpUbzgfcDInot3X5YX5NboDdPV
|
||||
yidWzyT3pM5AO4Kk6by3Y9KiiRmj83k3F7TaiwqU9o9Q5M3U14ZCV2D4coCrMYzw
|
||||
VHBFSV2LiH8qEaV4DXBO36Ty6TQBGbFUJUtxbD9pDOoOab3KEda7JtEbFolZyQAk
|
||||
21hyoPs7DmhbVmV5ih6QEKNHOt+7U9UeGy39kbEShVVoF0VaC+7J8SyRjO7/QawW
|
||||
JjFOkF8QSPGAH7l8iGKwtvcZ39VmbJhas16KlxmrlHxRL5Gms4IXYuaqSPsLIAcr
|
||||
SfOdf2Xq2Up4SYnPHW5Kg2FjHTMRFF0XlMRoXcqD4tTXa9w2IGpQ+EoHeEfNga49
|
||||
Xw8AaWx6EDzLdyzwXWr1AgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8E
|
||||
BTADAQH/MB0GA1UdDgQWBBTVTBh/edUJ6IgeZ29ZguyqoYipdjAQBgkrBgEEAYI3
|
||||
FQEEAwIBADANBgkqhkiG9w0BAQ0FAAOCAgEAm/HO1AIJSX4ic8Soph2pak6ztxNo
|
||||
crd8jVkGsRI2l1vmVBhvsSZkDUGiCnTVm3bYrHEEgmZlp2uTdMd8t5ufjM55VWZ2
|
||||
gdOuuqha8cXDuwqsOzhX/IGdARrRJFABHO4amdQpMBDxPGswIjXQfKc0UeFQ+/lr
|
||||
Fe0lmqegIKlpogEydz6qMQ6BnsUI8WQiakggVkl9flpsCR7lk8gWoBHQebGs2TWt
|
||||
7IA/RRWm5S2OSToTh2E38MuRaiAwwaJNch2WflixJyWUVi5FbZ0l8ZGzxTvCcDgn
|
||||
UC4KpyeId3fONANQrSmCz6X+ORyPbzLiIojG+y65h/cKw7cMI99+sU3s2akVAGpo
|
||||
rYH1MU4KAqyChp71Xr+V0bvGMLQKuFFn/6xQ/MF24Btxjcp3ODVEyQ+lNMFfg5pB
|
||||
RuhXiYAMZJSUyJGRLOI5RFBB2l8rBRRe7epsjPKrfwt30YXX7hO2Uo1JoL1ov1Um
|
||||
Vt2SpB3b2Kk4xHwp2PCAfA+VtB8nJVC49JdXi7rlYh5IUAUexrsvMPLeHlDrldYb
|
||||
1TcSeglLTIGFQBxElG4I7yDaWnIzTmlY/zDiK4h4l/rzyWo0NVu2KLnT0iAZ2/w5
|
||||
AjAeIyKnFP7PXYQwAdiW/B/cwfZTrsDaxq8PkZjiMznVzG+EQSgmcqT0z6aNJmoU
|
||||
3z/B7cuoI5Eu0OU=
|
||||
-----END CERTIFICATE-----
|
||||
9
mobileLst/certFiles/xml/network_security_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
41
mobileLst/components/HiddenScanner.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Text, TextInput, View } from "react-native";
|
||||
|
||||
export default function HiddenScannerListener({ onScan }: { onScan: any }) {
|
||||
const [buffer, setBuffer] = useState("");
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
// Keep focusing the invisible input
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
if (inputRef.current) inputRef.current.focus();
|
||||
}, 500);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
const handleChange = (text: string) => {
|
||||
// Most scanners append '\n' (Enter) or '\t'; trim and send
|
||||
if (text.endsWith("\n") || text.endsWith("\t")) {
|
||||
const code = text.trim();
|
||||
if (code) onScan?.(code);
|
||||
setBuffer("");
|
||||
} else {
|
||||
setBuffer(text);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={buffer}
|
||||
onChangeText={handleChange}
|
||||
autoFocus
|
||||
style={{
|
||||
position: "absolute",
|
||||
opacity: 0, // invisible
|
||||
height: 0, // takes no space
|
||||
width: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
10
mobileLst/eslint.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// https://docs.expo.dev/guides/using-eslint/
|
||||
const { defineConfig } = require('eslint/config');
|
||||
const expoConfig = require('eslint-config-expo/flat');
|
||||
|
||||
module.exports = defineConfig([
|
||||
expoConfig,
|
||||
{
|
||||
ignores: ['dist/*'],
|
||||
},
|
||||
]);
|
||||
13666
mobileLst/package-lock.json
generated
Normal file
58
mobileLst/package.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "mobilelst",
|
||||
"main": "expo-router/entry",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"reset-project": "node ./scripts/reset-project.js",
|
||||
"android": "expo run:android",
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "expo lint",
|
||||
"build:clean": "expo prebuild --clean && cd android && gradlew.bat assembleRelease && npm run ",
|
||||
"build:android": "cd android && gradlew.bat assembleRelease",
|
||||
"build:ota": "set EXPO_NO_EAS_UPDATE=1 && npx expo export --platform android --output-dir dist --experimental-bundle",
|
||||
"update": "adb install android/app/build/outputs/apk/release/app-release.apk",
|
||||
"debug": "adb logcat | Select-String \"Expo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^15.0.3",
|
||||
"@react-native-async-storage/async-storage": "2.2.0",
|
||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.8",
|
||||
"axios": "^1.13.2",
|
||||
"expo": "~54.0.23",
|
||||
"expo-build-properties": "~1.0.9",
|
||||
"expo-camera": "~17.0.9",
|
||||
"expo-constants": "~18.0.10",
|
||||
"expo-font": "~14.0.9",
|
||||
"expo-haptics": "~15.0.7",
|
||||
"expo-image": "~3.0.10",
|
||||
"expo-linking": "~8.0.8",
|
||||
"expo-router": "~6.0.14",
|
||||
"expo-splash-screen": "~31.0.10",
|
||||
"expo-status-bar": "~3.0.8",
|
||||
"expo-symbols": "~1.0.7",
|
||||
"expo-system-ui": "~6.0.8",
|
||||
"expo-updates": "~29.0.12",
|
||||
"expo-web-browser": "~15.0.9",
|
||||
"expo-zebra-scanner": "^6.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-reanimated": "~4.1.1",
|
||||
"react-native-safe-area-context": "~5.6.0",
|
||||
"react-native-screens": "~4.16.0",
|
||||
"react-native-web": "~0.21.0",
|
||||
"react-native-worklets": "0.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "~19.1.0",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-config-expo": "~10.0.0",
|
||||
"typescript": "~5.9.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
94
mobileLst/plugins/androidCustomizations.js
Normal file
@@ -0,0 +1,94 @@
|
||||
const {
|
||||
withAndroidManifest,
|
||||
withDangerousMod,
|
||||
withGradleProperties,
|
||||
} = require("@expo/config-plugins");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function withCustomAndroidConfig(config) {
|
||||
// Copy cert files
|
||||
config = withDangerousMod(config, [
|
||||
"android",
|
||||
async (config) => {
|
||||
const projectRoot = config.modRequest.projectRoot;
|
||||
const certSourceDir = path.join(projectRoot, "certFiles");
|
||||
const rawDestDir = path.join(
|
||||
projectRoot,
|
||||
"android/app/src/main/res/raw"
|
||||
);
|
||||
const xmlDestDir = path.join(
|
||||
projectRoot,
|
||||
"android/app/src/main/res/xml"
|
||||
);
|
||||
|
||||
// Create directories if they don't exist
|
||||
if (!fs.existsSync(rawDestDir)) {
|
||||
fs.mkdirSync(rawDestDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(xmlDestDir)) {
|
||||
fs.mkdirSync(xmlDestDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Copy your cert files
|
||||
if (fs.existsSync(path.join(certSourceDir, "raw"))) {
|
||||
fs.cpSync(path.join(certSourceDir, "raw"), rawDestDir, {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.join(certSourceDir, "xml"))) {
|
||||
fs.cpSync(path.join(certSourceDir, "xml"), xmlDestDir, {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("✅ Copied cert files to android/app/src/main/res");
|
||||
return config;
|
||||
},
|
||||
]);
|
||||
|
||||
// Modify AndroidManifest
|
||||
config = withAndroidManifest(config, (config) => {
|
||||
const mainApplication = config.modResults.manifest.application[0];
|
||||
|
||||
// Add network security config if not already present
|
||||
if (!mainApplication.$["android:networkSecurityConfig"]) {
|
||||
mainApplication.$["android:networkSecurityConfig"] =
|
||||
"@xml/network_security_config";
|
||||
}
|
||||
|
||||
console.log("✅ Modified AndroidManifest.xml");
|
||||
return config;
|
||||
});
|
||||
|
||||
// Update gradle.properties for better performance
|
||||
config = withGradleProperties(config, (config) => {
|
||||
// Remove existing jvmargs if present
|
||||
config.modResults = config.modResults.filter(
|
||||
(item) =>
|
||||
!(item.type === "property" && item.key === "org.gradle.jvmargs")
|
||||
);
|
||||
|
||||
// Add your custom jvmargs
|
||||
config.modResults.push(
|
||||
{
|
||||
type: "property",
|
||||
key: "org.gradle.jvmargs",
|
||||
value: "-Xmx16384m -XX:MaxMetaspaceSize=2024m",
|
||||
},
|
||||
{
|
||||
type: "property",
|
||||
key: "org.gradle.daemon",
|
||||
value: "false",
|
||||
}
|
||||
);
|
||||
|
||||
console.log("✅ Updated gradle.properties with 16GB heap");
|
||||
return config;
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = withCustomAndroidConfig;
|
||||
15
mobileLst/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
]
|
||||
}
|
||||