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
|
# 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
|
lstV2/frontend/.tanstack
|
||||||
|
|
||||||
mobileLst
|
|
||||||
keys
|
keys
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|||||||
6
.vscode/settings.json
vendored
@@ -55,5 +55,9 @@
|
|||||||
"intiallally",
|
"intiallally",
|
||||||
"ppoo",
|
"ppoo",
|
||||||
"prodlabels"
|
"prodlabels"
|
||||||
]
|
],
|
||||||
|
"gitea.token": "8456def90e1c651a761a8711763d6ef225d6b2db",
|
||||||
|
"gitea.instanceURL": "https://git.tuffraid.net",
|
||||||
|
"gitea.owner": "cowch",
|
||||||
|
"gitea.repo": "lst"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,8 +100,8 @@
|
|||||||
"category": "quality",
|
"category": "quality",
|
||||||
"active": false,
|
"active": false,
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"link": "",
|
"link": "/lst/app/old/quality",
|
||||||
"roles": ["admin", "systemAdmin", "manager", "viewer", "tester"]
|
"roles": ["admin", "systemAdmin", "manager", "supervisor", "tester"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "eom",
|
"name": "eom",
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function TableNoExpand({
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="w-fit">
|
<div className="">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{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 OldOldIndexRouteImport } from './routes/_old/old/index'
|
||||||
import { Route as AppauthLoginRouteImport } from './routes/_app/(auth)/login'
|
import { Route as AppauthLoginRouteImport } from './routes/_app/(auth)/login'
|
||||||
import { Route as OldOldRfidIndexRouteImport } from './routes/_old/old/rfid/index'
|
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 OldOldOcpIndexRouteImport } from './routes/_old/old/ocp/index'
|
||||||
import { Route as MobileMobileLayoutMIndexRouteImport } from './routes/_mobile/_mobileLayout/m/index'
|
import { Route as MobileMobileLayoutMIndexRouteImport } from './routes/_mobile/_mobileLayout/m/index'
|
||||||
import { Route as AppForkliftsForkliftsIndexRouteImport } from './routes/_app/_forklifts/forklifts/index'
|
import { Route as AppForkliftsForkliftsIndexRouteImport } from './routes/_app/_forklifts/forklifts/index'
|
||||||
@@ -107,6 +108,11 @@ const OldOldRfidIndexRoute = OldOldRfidIndexRouteImport.update({
|
|||||||
path: '/rfid/',
|
path: '/rfid/',
|
||||||
getParentRoute: () => OldOldRouteRoute,
|
getParentRoute: () => OldOldRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const OldOldQualityIndexRoute = OldOldQualityIndexRouteImport.update({
|
||||||
|
id: '/quality/',
|
||||||
|
path: '/quality/',
|
||||||
|
getParentRoute: () => OldOldRouteRoute,
|
||||||
|
} as any)
|
||||||
const OldOldOcpIndexRoute = OldOldOcpIndexRouteImport.update({
|
const OldOldOcpIndexRoute = OldOldOcpIndexRouteImport.update({
|
||||||
id: '/ocp/',
|
id: '/ocp/',
|
||||||
path: '/ocp/',
|
path: '/ocp/',
|
||||||
@@ -303,6 +309,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
||||||
'/m': typeof MobileMobileLayoutMIndexRoute
|
'/m': typeof MobileMobileLayoutMIndexRoute
|
||||||
'/old/ocp': typeof OldOldOcpIndexRoute
|
'/old/ocp': typeof OldOldOcpIndexRoute
|
||||||
|
'/old/quality': typeof OldOldQualityIndexRoute
|
||||||
'/old/rfid': typeof OldOldRfidIndexRoute
|
'/old/rfid': typeof OldOldRfidIndexRoute
|
||||||
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||||
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||||
@@ -339,6 +346,7 @@ export interface FileRoutesByTo {
|
|||||||
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
'/forklifts': typeof AppForkliftsForkliftsIndexRoute
|
||||||
'/m': typeof MobileMobileLayoutMIndexRoute
|
'/m': typeof MobileMobileLayoutMIndexRoute
|
||||||
'/old/ocp': typeof OldOldOcpIndexRoute
|
'/old/ocp': typeof OldOldOcpIndexRoute
|
||||||
|
'/old/quality': typeof OldOldQualityIndexRoute
|
||||||
'/old/rfid': typeof OldOldRfidIndexRoute
|
'/old/rfid': typeof OldOldRfidIndexRoute
|
||||||
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
'/admin/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||||
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
'/admin/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||||
@@ -383,6 +391,7 @@ export interface FileRoutesById {
|
|||||||
'/_app/_forklifts/forklifts/': typeof AppForkliftsForkliftsIndexRoute
|
'/_app/_forklifts/forklifts/': typeof AppForkliftsForkliftsIndexRoute
|
||||||
'/_mobile/_mobileLayout/m/': typeof MobileMobileLayoutMIndexRoute
|
'/_mobile/_mobileLayout/m/': typeof MobileMobileLayoutMIndexRoute
|
||||||
'/_old/old/ocp/': typeof OldOldOcpIndexRoute
|
'/_old/old/ocp/': typeof OldOldOcpIndexRoute
|
||||||
|
'/_old/old/quality/': typeof OldOldQualityIndexRoute
|
||||||
'/_old/old/rfid/': typeof OldOldRfidIndexRoute
|
'/_old/old/rfid/': typeof OldOldRfidIndexRoute
|
||||||
'/_app/_adminLayout/admin/_system/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
'/_app/_adminLayout/admin/_system/modules': typeof AppAdminLayoutAdminSystemModulesRoute
|
||||||
'/_app/_adminLayout/admin/_system/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
'/_app/_adminLayout/admin/_system/settings': typeof AppAdminLayoutAdminSystemSettingsRoute
|
||||||
@@ -422,6 +431,7 @@ export interface FileRouteTypes {
|
|||||||
| '/forklifts'
|
| '/forklifts'
|
||||||
| '/m'
|
| '/m'
|
||||||
| '/old/ocp'
|
| '/old/ocp'
|
||||||
|
| '/old/quality'
|
||||||
| '/old/rfid'
|
| '/old/rfid'
|
||||||
| '/admin/modules'
|
| '/admin/modules'
|
||||||
| '/admin/settings'
|
| '/admin/settings'
|
||||||
@@ -458,6 +468,7 @@ export interface FileRouteTypes {
|
|||||||
| '/forklifts'
|
| '/forklifts'
|
||||||
| '/m'
|
| '/m'
|
||||||
| '/old/ocp'
|
| '/old/ocp'
|
||||||
|
| '/old/quality'
|
||||||
| '/old/rfid'
|
| '/old/rfid'
|
||||||
| '/admin/modules'
|
| '/admin/modules'
|
||||||
| '/admin/settings'
|
| '/admin/settings'
|
||||||
@@ -501,6 +512,7 @@ export interface FileRouteTypes {
|
|||||||
| '/_app/_forklifts/forklifts/'
|
| '/_app/_forklifts/forklifts/'
|
||||||
| '/_mobile/_mobileLayout/m/'
|
| '/_mobile/_mobileLayout/m/'
|
||||||
| '/_old/old/ocp/'
|
| '/_old/old/ocp/'
|
||||||
|
| '/_old/old/quality/'
|
||||||
| '/_old/old/rfid/'
|
| '/_old/old/rfid/'
|
||||||
| '/_app/_adminLayout/admin/_system/modules'
|
| '/_app/_adminLayout/admin/_system/modules'
|
||||||
| '/_app/_adminLayout/admin/_system/settings'
|
| '/_app/_adminLayout/admin/_system/settings'
|
||||||
@@ -602,6 +614,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof OldOldRfidIndexRouteImport
|
preLoaderRoute: typeof OldOldRfidIndexRouteImport
|
||||||
parentRoute: typeof OldOldRouteRoute
|
parentRoute: typeof OldOldRouteRoute
|
||||||
}
|
}
|
||||||
|
'/_old/old/quality/': {
|
||||||
|
id: '/_old/old/quality/'
|
||||||
|
path: '/quality'
|
||||||
|
fullPath: '/old/quality'
|
||||||
|
preLoaderRoute: typeof OldOldQualityIndexRouteImport
|
||||||
|
parentRoute: typeof OldOldRouteRoute
|
||||||
|
}
|
||||||
'/_old/old/ocp/': {
|
'/_old/old/ocp/': {
|
||||||
id: '/_old/old/ocp/'
|
id: '/_old/old/ocp/'
|
||||||
path: '/ocp'
|
path: '/ocp'
|
||||||
@@ -949,6 +968,7 @@ const MobileMobileLayoutRouteRouteWithChildren =
|
|||||||
interface OldOldRouteRouteChildren {
|
interface OldOldRouteRouteChildren {
|
||||||
OldOldIndexRoute: typeof OldOldIndexRoute
|
OldOldIndexRoute: typeof OldOldIndexRoute
|
||||||
OldOldOcpIndexRoute: typeof OldOldOcpIndexRoute
|
OldOldOcpIndexRoute: typeof OldOldOcpIndexRoute
|
||||||
|
OldOldQualityIndexRoute: typeof OldOldQualityIndexRoute
|
||||||
OldOldRfidIndexRoute: typeof OldOldRfidIndexRoute
|
OldOldRfidIndexRoute: typeof OldOldRfidIndexRoute
|
||||||
OldOldlogisticsSiloAdjustmentsHistRoute: typeof OldOldlogisticsSiloAdjustmentsHistRoute
|
OldOldlogisticsSiloAdjustmentsHistRoute: typeof OldOldlogisticsSiloAdjustmentsHistRoute
|
||||||
OldOldlogisticsBarcodegenIndexRoute: typeof OldOldlogisticsBarcodegenIndexRoute
|
OldOldlogisticsBarcodegenIndexRoute: typeof OldOldlogisticsBarcodegenIndexRoute
|
||||||
@@ -964,6 +984,7 @@ interface OldOldRouteRouteChildren {
|
|||||||
const OldOldRouteRouteChildren: OldOldRouteRouteChildren = {
|
const OldOldRouteRouteChildren: OldOldRouteRouteChildren = {
|
||||||
OldOldIndexRoute: OldOldIndexRoute,
|
OldOldIndexRoute: OldOldIndexRoute,
|
||||||
OldOldOcpIndexRoute: OldOldOcpIndexRoute,
|
OldOldOcpIndexRoute: OldOldOcpIndexRoute,
|
||||||
|
OldOldQualityIndexRoute: OldOldQualityIndexRoute,
|
||||||
OldOldRfidIndexRoute: OldOldRfidIndexRoute,
|
OldOldRfidIndexRoute: OldOldRfidIndexRoute,
|
||||||
OldOldlogisticsSiloAdjustmentsHistRoute:
|
OldOldlogisticsSiloAdjustmentsHistRoute:
|
||||||
OldOldlogisticsSiloAdjustmentsHistRoute,
|
OldOldlogisticsSiloAdjustmentsHistRoute,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export function AddCards() {
|
|||||||
<Cards name={"ppoo"} inventory />
|
<Cards name={"ppoo"} inventory />
|
||||||
<Cards name={"inv-empty"} rowType={"empty"} />
|
<Cards name={"inv-empty"} rowType={"empty"} />
|
||||||
<Cards name={"inv-fg"} rowType={"fg"} />
|
<Cards name={"inv-fg"} rowType={"fg"} />
|
||||||
|
<Cards name={"qualityCard"} />
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
<Cards name={"inv-materials"} rowType={"materials"} />
|
<Cards name={"inv-materials"} rowType={"materials"} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCardStore } from "../../-lib/store/useCardStore";
|
import { useCardStore } from "../../-lib/store/useCardStore";
|
||||||
|
import Quality from "../../quality/-components/WarehouseCard";
|
||||||
import INVCheckCard from "../logistics/warehouse/InventoryCard";
|
import INVCheckCard from "../logistics/warehouse/InventoryCard";
|
||||||
import OpenOrders from "../logistics/warehouse/openOrders";
|
import OpenOrders from "../logistics/warehouse/openOrders";
|
||||||
import PPOO from "../logistics/warehouse/PPOOCard";
|
import PPOO from "../logistics/warehouse/PPOOCard";
|
||||||
@@ -7,6 +8,7 @@ const componentsMap: any = {
|
|||||||
ppoo: PPOO,
|
ppoo: PPOO,
|
||||||
inv: INVCheckCard,
|
inv: INVCheckCard,
|
||||||
openOrder: OpenOrders,
|
openOrder: OpenOrders,
|
||||||
|
qualityCard: Quality,
|
||||||
//QualityRequest,
|
//QualityRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,14 +28,21 @@ export default function DashBoard() {
|
|||||||
<Component age={a.age} type={a.rowType} />
|
<Component age={a.age} type={a.rowType} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (name === "qualityCard") {
|
||||||
|
return (
|
||||||
|
<div key={a.name} className="col-span-6">
|
||||||
|
<Component />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
//console.log(name.split("-")[0], a);
|
//console.log(name.split("-")[0], a);
|
||||||
return (
|
return (
|
||||||
<div key={a.name} className="col-span-3">
|
<div key={a.name} className="col-span-3">
|
||||||
<Component data={a} />
|
<Component data={a} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { AdminSideBar } from "./side-components/admin";
|
|||||||
import { Header } from "./side-components/header";
|
import { Header } from "./side-components/header";
|
||||||
import { LogisticsSideBar } from "./side-components/logistics";
|
import { LogisticsSideBar } from "./side-components/logistics";
|
||||||
import { ProductionSideBar } from "./side-components/production";
|
import { ProductionSideBar } from "./side-components/production";
|
||||||
|
import { QualitySideBar } from "./side-components/quality";
|
||||||
|
|
||||||
export function AppSidebar() {
|
export function AppSidebar() {
|
||||||
const { session } = useAuth();
|
const { session } = useAuth();
|
||||||
@@ -31,11 +32,12 @@ export function AppSidebar() {
|
|||||||
|
|
||||||
{/* userAccess("logistics", ["systemAdmin", "admin","manager","viewer"]) */}
|
{/* userAccess("logistics", ["systemAdmin", "admin","manager","viewer"]) */}
|
||||||
<LogisticsSideBar user={session?.user as any} userRoles={userRoles} />
|
<LogisticsSideBar user={session?.user as any} userRoles={userRoles} />
|
||||||
|
<QualitySideBar user={session?.user as any} userRoles={userRoles} />
|
||||||
{userAccess(null, ["systemAdmin"]) && (
|
{userAccess(null, ["systemAdmin"]) && (
|
||||||
<>
|
<>
|
||||||
{/* <ForkliftSideBar />
|
{/* <ForkliftSideBar />
|
||||||
<EomSideBar />
|
<EomSideBar />
|
||||||
<QualitySideBar /> */}
|
*/}
|
||||||
<AdminSideBar />
|
<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 {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
SidebarGroupContent,
|
SidebarGroupContent,
|
||||||
@@ -7,31 +9,49 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from "../../../../../../components/ui/sidebar";
|
} from "../../../../../../components/ui/sidebar";
|
||||||
|
import { useModuleStore } from "../../../-lib/store/useModuleStore";
|
||||||
|
import { hasPageAccess } from "../../../-utils/userAccess";
|
||||||
|
|
||||||
const items = [
|
const iconMap: any = {
|
||||||
{
|
Printer: Printer,
|
||||||
title: "Qaulity Request",
|
Cat: Cat,
|
||||||
url: "#",
|
};
|
||||||
icon: Printer,
|
|
||||||
},
|
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 (
|
return (
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupLabel>Quality</SidebarGroupLabel>
|
<SidebarGroupLabel>Quality</SidebarGroupLabel>
|
||||||
<SidebarGroupContent>
|
<SidebarGroupContent>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
{items.map((item) => (
|
{items.map((item) => {
|
||||||
<SidebarMenuItem key={item.title}>
|
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>
|
<SidebarMenuButton asChild>
|
||||||
<a href={item.url}>
|
<a href={item.link}>
|
||||||
<item.icon />
|
<Icon />
|
||||||
<span>{item.title}</span>
|
<span>{item.name}</span>
|
||||||
</a>
|
</a>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</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,
|
"when": 1762983466464,
|
||||||
"tag": "0075_tan_unicorn",
|
"tag": "0075_tan_unicorn",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 76,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1763134709763,
|
||||||
|
"tag": "0076_stiff_champions",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
text,
|
|
||||||
pgTable,
|
|
||||||
numeric,
|
|
||||||
index,
|
|
||||||
timestamp,
|
|
||||||
boolean,
|
boolean,
|
||||||
uuid,
|
index,
|
||||||
uniqueIndex,
|
|
||||||
integer,
|
integer,
|
||||||
|
numeric,
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
uniqueIndex,
|
||||||
|
uuid,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -31,6 +31,7 @@ export const qualityRequest = pgTable(
|
|||||||
palletStatus: integer("palletStatus"),
|
palletStatus: integer("palletStatus"),
|
||||||
palletStatusText: text("palletStatusText"),
|
palletStatusText: text("palletStatusText"),
|
||||||
palletRequest: integer("palletRequest"),
|
palletRequest: integer("palletRequest"),
|
||||||
|
priority: integer("priority").default(4), // 1,2,3,4
|
||||||
add_date: timestamp("add_date").defaultNow(),
|
add_date: timestamp("add_date").defaultNow(),
|
||||||
add_user: text("add_user").default("LST"),
|
add_user: text("add_user").default("LST"),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date").defaultNow(),
|
||||||
@@ -39,7 +40,7 @@ export const qualityRequest = pgTable(
|
|||||||
(table) => [
|
(table) => [
|
||||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||||
// uniqueIndex("role_name").on(table.name),
|
// uniqueIndex("role_name").on(table.name),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schema for inserting a user - can be used to validate API requests
|
// Schema for inserting a user - can be used to validate API requests
|
||||||
|
|||||||
@@ -6,9 +6,26 @@ import {
|
|||||||
parseISO,
|
parseISO,
|
||||||
startOfWeek,
|
startOfWeek,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
|
import { db } from "../../../../../database/dbclient.js";
|
||||||
|
import { invHistoricalData } from "../../../../../database/schema/historicalINV.js";
|
||||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||||
import { query } from "../../../sqlServer/prodSqlServer.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) {
|
function toDate(val: any) {
|
||||||
if (val instanceof Date) return val;
|
if (val instanceof Date) return val;
|
||||||
@@ -16,7 +33,7 @@ function toDate(val: any) {
|
|||||||
return new Date(val);
|
return new Date(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sumByMaterialAndWeek(data: any) {
|
function sumByMaterialAndWeek(data: any) {
|
||||||
/** @type {Record<string, Record<string, number>>} */
|
/** @type {Record<string, Record<string, number>>} */
|
||||||
const grouped: any = {};
|
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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "material data",
|
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 { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
|
||||||
import { apiHit } from "../../../globalUtils/apiHits.js";
|
import { apiHit } from "../../../globalUtils/apiHits.js";
|
||||||
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
import { responses } from "../../../globalUtils/routeDefs/responses.js";
|
||||||
|
import materialPerDayCheck from "../controller/materialsPerDay/materialPerDay.js";
|
||||||
import fifoIndexCheck from "../controller/notifications/fifoIndex.js";
|
import fifoIndexCheck from "../controller/notifications/fifoIndex.js";
|
||||||
import materialPerDayCheck from "../controller/notifications/materialPerDay.js";
|
|
||||||
|
|
||||||
const app = new OpenAPIHono({ strict: false });
|
const app = new OpenAPIHono({ strict: false });
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { differenceInMinutes } from "date-fns";
|
import { differenceInMinutes } from "date-fns";
|
||||||
import { eq, sql } from "drizzle-orm";
|
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 { db } from "../../../../database/dbclient.js";
|
||||||
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
||||||
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
|
import { timeZoneFix } from "../../../globalUtils/timeZoneFix.js";
|
||||||
@@ -39,12 +40,14 @@ export const addNewPallet = async (data: any) => {
|
|||||||
const palletData: any = c;
|
const palletData: any = c;
|
||||||
// if the pallet exist then tell the user to check on it
|
// if the pallet exist then tell the user to check on it
|
||||||
const pStatus = [1, 4, 6];
|
const pStatus = [1, 4, 6];
|
||||||
|
if (!data.priority) {
|
||||||
if (palletData && pStatus.includes(palletData[0]?.palletStatus)) {
|
if (palletData && pStatus.includes(palletData[0]?.palletStatus)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `Running number ${data.runningNr} is already pending or reactivated please follow up with the warehouse team on status to be moved.`,
|
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
|
// update the existing pallet if already in the system
|
||||||
if (palletData.length > 0) {
|
if (palletData.length > 0) {
|
||||||
@@ -61,7 +64,17 @@ export const addNewPallet = async (data: any) => {
|
|||||||
data: pe,
|
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,
|
warehouseAtRequest: p[0].warehouseAtRequest,
|
||||||
locationAtRequest: p[0].locationAtRequest,
|
locationAtRequest: p[0].locationAtRequest,
|
||||||
warehouseMovedTo: null,
|
warehouseMovedTo: null,
|
||||||
@@ -80,6 +93,7 @@ export const addNewPallet = async (data: any) => {
|
|||||||
upd_user: data.user,
|
upd_user: data.user,
|
||||||
upd_date: sql`NOW()`,
|
upd_date: sql`NOW()`,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const { data: u, error } = await tryCatch(
|
const { data: u, error } = await tryCatch(
|
||||||
db
|
db
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { desc } from "drizzle-orm";
|
import { asc, desc } from "drizzle-orm";
|
||||||
import { db } from "../../../../database/dbclient.js";
|
import { db } from "../../../../database/dbclient.js";
|
||||||
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
import { qualityRequest } from "../../../../database/schema/qualityRequest.js";
|
||||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||||
@@ -6,7 +6,10 @@ import qualityBlockingMonitor from "../../notifications/controller/notifications
|
|||||||
|
|
||||||
export const getRequest = async () => {
|
export const getRequest = async () => {
|
||||||
const { data, error } = await tryCatch(
|
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) {
|
if (error) {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const qualityCycle = async () => {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
createLog(
|
createLog(
|
||||||
"info",
|
"debug",
|
||||||
"lst",
|
"lst",
|
||||||
"quality",
|
"quality",
|
||||||
`Pallet ${
|
`Pallet ${
|
||||||
@@ -119,7 +119,7 @@ export const qualityCycle = async () => {
|
|||||||
}
|
}
|
||||||
await delay(150);
|
await delay(150);
|
||||||
} else {
|
} else {
|
||||||
createLog("info", "lst", "quality", "nothing to update");
|
//createLog("info", "lst", "quality", "nothing to update");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
5 * 60 * 1000,
|
5 * 60 * 1000,
|
||||||
|
|||||||
@@ -113,3 +113,21 @@ ORDER BY
|
|||||||
n.ProductionLotHumanReadableId
|
n.ProductionLotHumanReadableId
|
||||||
OPTION (MAXRECURSION 0);
|
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"
|
||||||
|
]
|
||||||
|
}
|
||||||