diff --git a/backend/db/schema/dockdoor.schema.ts b/backend/db/schema/dockdoor.schema.ts new file mode 100644 index 0000000..a5c2b48 --- /dev/null +++ b/backend/db/schema/dockdoor.schema.ts @@ -0,0 +1,22 @@ +import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core"; +import { createInsertSchema, createSelectSchema } from "drizzle-zod"; +import type { z } from "zod"; + +export const dockDoorScanners = pgTable("dock_door_scanners", { + id: uuid("id").defaultRandom().primaryKey(), + ip: text("ip").notNull(), + name: text("name").unique(), + dockId: text("dock_id"), + active: boolean("active").default(true), + currentLoadingOrder: text("current_loading_order").default(""), + add_date: timestamp("add_date").defaultNow(), + add_user: text("add_user").default("lst-system"), + upd_date: timestamp("upd_date").defaultNow(), + upd_user: text("upd_user").default("lst-system"), +}); + +export const dockDoorScannersSchema = createSelectSchema(dockDoorScanners); +export const newDockDoorScannersSchema = createInsertSchema(dockDoorScanners); + +export type DockDoorScanners = z.infer; +export type NewDockDoorScanners = z.infer; diff --git a/backend/dockdoorScanning/dockdoor.activeLoadingOrders.route.ts b/backend/dockdoorScanning/dockdoor.activeLoadingOrders.route.ts new file mode 100644 index 0000000..3aae5c8 --- /dev/null +++ b/backend/dockdoorScanning/dockdoor.activeLoadingOrders.route.ts @@ -0,0 +1,35 @@ +import { addDays, subDays } from "date-fns"; +import { format } from "date-fns-tz"; +import { Router } from "express"; +import { runProdApi } from "../utils/prodEndpoint.utils.js"; +import { apiReturn } from "../utils/returnHelper.utils.js"; + +const r = Router(); + +r.get("/", async (_, res) => { + const orders = await runProdApi({ + method: "post", + endpoint: "/public/v1.0/OutboundDeliveries/LoadingOrders/Search", + data: [ + { + loadingDateFrom: format(subDays(new Date(Date.now()), 3), "yyyy-MM-dd"), + loadingDateTo: format(addDays(new Date(Date.now()), 3), "yyyy-MM-dd"), + states: [ + 1, // planned + ], + //isCommissioned: true, + }, + ], + }); + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "current Active loading orders", + message: `Current active loading loaders.`, + data: orders?.data ?? [], + status: 200, + }); +}); + +export default r; diff --git a/backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts b/backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts new file mode 100644 index 0000000..8bcc123 --- /dev/null +++ b/backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts @@ -0,0 +1,18 @@ +import { Router } from "express"; +import { apiReturn } from "../utils/returnHelper.utils.js"; + +const r = Router(); + +r.post("/", async (req, res) => { + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "lane check", + message: `Release x is being closed now. the bol should come out at the default printer.`, + data: req.body ?? [], + status: 200, + }); +}); + +export default r; diff --git a/backend/dockdoorScanning/dockdoor.loadUnits.ts b/backend/dockdoorScanning/dockdoor.loadUnits.ts new file mode 100644 index 0000000..e21e780 --- /dev/null +++ b/backend/dockdoorScanning/dockdoor.loadUnits.ts @@ -0,0 +1,89 @@ +// sends the units from the dock door scanner here. + +import { eq } from "drizzle-orm"; +import { db } from "../db/db.controller.js"; +import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; +import { runProdApi } from "../utils/prodEndpoint.utils.js"; +import { returnFunc } from "../utils/returnHelper.utils.js"; + +// validate we are active + +type Data = { + dockId?: string; + sscc?: string; + runningNr?: string; +}; + +export const loadUnit = async (data: Data) => { + // are we even active at this time? + const dockDoorActive = await db.query.settings.findFirst({ + where: (u, { eq }) => eq(u.name, "dockDoorScanning"), + }); + + if (!dockDoorActive?.active) { + return returnFunc({ + success: false, + level: "error", + module: "dockdoor", + subModule: "loadunit", + message: "Dock door scanning feature is not active.", + data: [], + notify: false, + room: "", + }); + } + // check if its a valids an sscc + + if (data.sscc === "noread") { + return returnFunc({ + success: false, + level: "error", + module: "dockdoor", + subModule: "loadUnit", + message: + "Failed to load the unit to the truck, there was no pallet read.", + data: [], + notify: false, + room: `dockDoorLoading${data.dockId}`, + }); + } + + // check if we currently have a loading order attached to the dock door. + const dock = await db + .select() + .from(dockDoorScanners) + .where(eq(dockDoorScanners.dockId, data.dockId as string)); + + if (dock[0]?.currentLoadingOrder === "") { + return returnFunc({ + success: true, + level: "error", + module: "dockdoor", + subModule: "loadingOrders", + message: + "There are know current active loading orders please start one and try again.", + data: [], + notify: false, + room: `dockDoorLoading${data.dockId}`, + }); + } + + // TODO: pallet validation, check if we are on hold, then check if we have been in the staging warehouse for more than x time. + + // if on hold stop the scan and send a bad read with the reason its on hold and what its on hold for, including coa. + + // if precheck is active then check if we have a warehouse, then check if the pallet was in the warehouse for greater than the define min, all fails send a warning and still do the scan + + // add the loading units + try { + const prod = await runProdApi({ + method: "post", + endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${dock[0]?.currentLoadingOrder}/LoadUnit`, + data: [{ sscc: data.sscc?.slice(2) }], + }); + + console.log(prod?.data); + } catch (error) { + console.log(error); + } +}; diff --git a/backend/dockdoorScanning/dockdoor.routes.ts b/backend/dockdoorScanning/dockdoor.routes.ts new file mode 100644 index 0000000..843a80f --- /dev/null +++ b/backend/dockdoorScanning/dockdoor.routes.ts @@ -0,0 +1,43 @@ +import type { Express } from "express"; +import { featureCheck } from "../middleware/featureActive.middleware.js"; +import activeLoadingOrders from "./dockdoor.activeLoadingOrders.route.js"; +import closeLoadingOrder from "./dockdoor.closeLoadingOrder.route.js"; +import startLoad from "./dockdoor.startLoad.route.js"; +import prodDocks from "./dockdoors.docks.route.js"; +import docks from "./dockdoors.route.js"; + +export const setupDockDoorRoutes = (baseUrl: string, app: Express) => { + //stats will be like this as we dont need to change this + + app.use( + `${baseUrl}/api/dockDoor/scanners`, + featureCheck("dockDoorScanning"), + + docks, + ); + + app.use( + `${baseUrl}/api/dockDoor/closeLoadingOrder`, + featureCheck("dockDoorScanning"), + closeLoadingOrder, + ); + app.use( + `${baseUrl}/api/dockDoor/activeLoadingOrders`, + featureCheck("dockDoorScanning"), + activeLoadingOrders, + ); + app.use( + `${baseUrl}/api/dockDoor/startLoad`, + featureCheck("dockDoorScanning"), + startLoad, + ); + app.use( + `${baseUrl}/api/dockDoor/docks`, + featureCheck("dockDoorScanning"), + prodDocks, + ); + + // TODO : add manual way to add pallets + + // all other system should be under /api/system/* +}; diff --git a/backend/dockdoorScanning/dockdoor.startLoad.route.ts b/backend/dockdoorScanning/dockdoor.startLoad.route.ts new file mode 100644 index 0000000..d2fa5b0 --- /dev/null +++ b/backend/dockdoorScanning/dockdoor.startLoad.route.ts @@ -0,0 +1,65 @@ +import { sql } from "drizzle-orm"; +import { Router } from "express"; +import z from "zod"; +import { db } from "../db/db.controller.js"; +import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; +import { apiReturn } from "../utils/returnHelper.utils.js"; +import { tryCatch } from "../utils/trycatch.utils.js"; + +const r = Router(); + +const startLoading = z.object({ + loadingOrder: z.string(), + dockId: z.string(), +}); + +r.post("/", async (req, res) => { + try { + const validated = startLoading.parse(req.body); + + const { data, error } = await tryCatch( + db + .update(dockDoorScanners) + .set({ + currentLoadingOrder: validated.loadingOrder, + upd_date: sql`NOW()`, + upd_user: req.user?.username, + }) + .returning(), + ); + + if (error) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "loadingOrder", + message: `Failed to updating the dock.`, + data: (error as any) ?? [], + status: 400, + }); + } + + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "loadingOrder", + message: `Loading order ${validated.loadingOrder} was just added to dockId ${validated.dockId}.`, + data: data ?? [], + status: 200, + }); + } catch (error) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "loadingOrder", + message: `Failed to start loading order.`, + data: (error as any) ?? [], + status: 400, + }); + } +}); + +export default r; diff --git a/backend/dockdoorScanning/dockdoors.docks.route.ts b/backend/dockdoorScanning/dockdoors.docks.route.ts new file mode 100644 index 0000000..fa19521 --- /dev/null +++ b/backend/dockdoorScanning/dockdoors.docks.route.ts @@ -0,0 +1,54 @@ +import { Router } from "express"; +import { prodQuery } from "../prodSql/prodSqlQuery.controller.js"; +import { + type SqlQuery, + sqlQuerySelector, +} from "../prodSql/prodSqlQuerySelector.utils.js"; +import { apiReturn } from "../utils/returnHelper.utils.js"; +import { tryCatch } from "../utils/trycatch.utils.js"; + +const r = Router(); + +r.get("/", async (_, res) => { + const activeDocks = sqlQuerySelector(`outbound.docks`) as SqlQuery; + + if (!activeDocks.success) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "docks", + message: `There was an error getting the docks query.`, + data: [], + status: 400, + }); + } + + const { data, error } = await tryCatch( + prodQuery(activeDocks.query, "Current Active Docks"), + ); + + if (error) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "newDock", + message: `There was an error getting the docks.`, + data: (error as any) ?? ([] as any), + status: 400, + }); + } + + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "docks", + message: `Current active docks.`, + data: (data.data as any) ?? ([] as any), + status: 200, + }); +}); + +export default r; diff --git a/backend/dockdoorScanning/dockdoors.route.ts b/backend/dockdoorScanning/dockdoors.route.ts new file mode 100644 index 0000000..319c72b --- /dev/null +++ b/backend/dockdoorScanning/dockdoors.route.ts @@ -0,0 +1,76 @@ +import { Router } from "express"; +import z from "zod"; +import { db } from "../db/db.controller.js"; +import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; +import { requireAuth } from "../middleware/auth.middleware.js"; +import { apiReturn } from "../utils/returnHelper.utils.js"; + +const r = Router(); + +const newDockScanner = z.object({ + ip: z.string(), + name: z.string(), + dockId: z.string(), +}); + +r.get("/", async (_, res) => { + try { + const docks = await db + .select() + .from(dockDoorScanners) + .orderBy(dockDoorScanners.name); + + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "lane check", + message: `All dock Doors.`, + data: docks ?? [], + status: 200, + }); + } catch (error) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "newDock", + message: `There was an error adding in the new dock.`, + data: error ?? ([] as any), + status: 200, + }); + } +}); + +r.post("/", requireAuth, async (req, res) => { + try { + const validated = newDockScanner.parse(req.body); + + const newDock = await db + .insert(dockDoorScanners) + .values(validated) + .returning(); + + return apiReturn(res, { + success: true, + level: "info", + module: "dockdoor", + subModule: "lane check", + message: `${validated.name} was just added.`, + data: newDock ?? [], + status: 200, + }); + } catch (error) { + return apiReturn(res, { + success: false, + level: "error", + module: "dockdoor", + subModule: "newDock", + message: `There was an error adding in the new dock.`, + data: error ?? ([] as any), + status: 200, + }); + } +}); + +export default r; diff --git a/backend/prodSql/queries/outbound.docks.sql b/backend/prodSql/queries/outbound.docks.sql new file mode 100644 index 0000000..7c69d5b --- /dev/null +++ b/backend/prodSql/queries/outbound.docks.sql @@ -0,0 +1,6 @@ +USE [test1_AlplaPROD2.0_Read] + +SELECT * + FROM [masterData].[Dock] (nolock) + where active = 1 + order by Description desc diff --git a/backend/routeHandler.routes.ts b/backend/routeHandler.routes.ts index 382c5db..fc6a5ae 100644 --- a/backend/routeHandler.routes.ts +++ b/backend/routeHandler.routes.ts @@ -4,6 +4,7 @@ import { setupAuthRoutes } from "./auth/auth.routes.js"; // import the routes and route setups import { setupApiDocsRoutes } from "./configs/scaler.config.js"; import { setupDatamartRoutes } from "./datamart/datamart.routes.js"; +import { setupDockDoorRoutes } from "./dockdoorScanning/dockdoor.routes.js"; import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js"; import { setupMobileRoutes } from "./mobile/mobile.routes.js"; import { setupNotificationRoutes } from "./notification/notification.routes.js"; @@ -29,4 +30,5 @@ export const setupRoutes = (baseUrl: string, app: Express) => { setupNotificationRoutes(baseUrl, app); setupOCPRoutes(baseUrl, app); setupTCPRoutes(baseUrl, app); + setupDockDoorRoutes(baseUrl, app); }; diff --git a/backend/socket.io/types.socket.ts b/backend/socket.io/types.socket.ts deleted file mode 100644 index 671c9d1..0000000 --- a/backend/socket.io/types.socket.ts +++ /dev/null @@ -1 +0,0 @@ -export type RoomId = "logs" | "labels" | "admin" | "admin:build"; //| "alerts" | "metrics"; diff --git a/backend/system/serverData.controller.ts b/backend/system/serverData.controller.ts index 3f81295..5d161b0 100644 --- a/backend/system/serverData.controller.ts +++ b/backend/system/serverData.controller.ts @@ -162,6 +162,17 @@ const servers: NewServerData[] = [ serverLoc: "D$\\LST_V3", buildNumber: 1, }, + { + name: "Salt Lake City", + server: "USSLC1VMS006", + plantToken: "usslc1", + idAddress: "10.202.0.26", + greatPlainsPlantCode: "70", + contactEmail: "", + contactPhone: "", + serverLoc: "D$\\LST_V3", + buildNumber: 1, + }, ]; // notes here for when it comes time to pull in all the server address info [test1_AlplaPROD2.0_Read].[masterData].[Plant] has everything from every server :D diff --git a/backend/system/settingsBase.controller.ts b/backend/system/settingsBase.controller.ts index 80ce217..80dc001 100644 --- a/backend/system/settingsBase.controller.ts +++ b/backend/system/settingsBase.controller.ts @@ -86,8 +86,40 @@ const newSettings: NewSetting[] = [ roles: ["admin"], seedVersion: 1, }, + { + name: "dockDoorScanning", + value: "0", + active: false, + description: "dock door scanning", + moduleName: "dockDoorScanning", + settingType: "feature", + roles: ["admin"], + seedVersion: 1, + }, // standard settings + { + name: "stagingWarehouse", + value: "30218", + active: true, + description: + "The warehouse we will use for staging, validation that we did our prechecks if required", + moduleName: "dockDoorScanning", + settingType: "standard", + roles: ["admin"], + seedVersion: 1, + }, + { + name: "precheck", + value: "5", + active: false, + description: + "Precheck is required, the value is in minute, 5 min should be 5", + moduleName: "dockDoorScanning", + settingType: "standard", + roles: ["admin"], + seedVersion: 1, + }, { name: "prolinkCheck", value: "1", diff --git a/backend/tcpServer/tcp.server.ts b/backend/tcpServer/tcp.server.ts index 18f9652..2703999 100644 --- a/backend/tcpServer/tcp.server.ts +++ b/backend/tcpServer/tcp.server.ts @@ -1,7 +1,9 @@ import net from "node:net"; import { eq } from "drizzle-orm"; import { db } from "../db/db.controller.js"; +import { dockDoorScanners } from "../db/schema/dockdoor.schema.js"; import { printerData } from "../db/schema/printers.schema.js"; +import { loadUnit } from "../dockdoorScanning/dockdoor.loadUnits.js"; import { createLogger } from "../logger/logger.controller.js"; import { delay } from "../utils/delay.utils.js"; import { returnFunc } from "../utils/returnHelper.utils.js"; @@ -14,6 +16,7 @@ export let isServerRunning = false; const port = parseInt(process.env.TCP_PORT ?? "2222", 10); +// This is the parser for zebra scanners const parseTcpAlert = (input: string) => { // guard const colonIndex = input.indexOf(":"); @@ -74,6 +77,24 @@ export const startTCPServer = async () => { printerListen(printerData as PrinterData); } + + // check if its a dock door scanner + // TODO: move to the db and get real info lol + const dockdoorScanners = await db.select().from(dockDoorScanners); + + if (dockdoorScanners.some((s) => s.ip === ip.replace("::ffff:", ""))) { + console.log("dock door logic"); + + const currentDock = dockdoorScanners.filter( + (s) => s.ip === ip.replace("::ffff:", ""), + ); + + // send the data + dock scan over + loadUnit({ + dockId: currentDock[0]?.dockId ?? "0", + sscc: data.toString(), + }); + } }); socket.on("end", () => { diff --git a/backend/utils/returnHelper.utils.ts b/backend/utils/returnHelper.utils.ts index d1b77e7..f151baa 100644 --- a/backend/utils/returnHelper.utils.ts +++ b/backend/utils/returnHelper.utils.ts @@ -16,7 +16,8 @@ export interface ReturnHelper { | "tcp" | "logistics" | "admin" - | "mobile"; + | "mobile" + | "dockdoor"; subModule: string; level: "info" | "error" | "debug" | "fatal" | "warn"; diff --git a/lstMobile/app.json b/lstMobile/app.json index 767125f..c25a4a1 100644 --- a/lstMobile/app.json +++ b/lstMobile/app.json @@ -3,7 +3,7 @@ "name": "LST mobile", "slug": "lst-mobile", "version": "0.11.1-alpha", - "orientation": "portrait", + "orientation": "default", "icon": "./assets/icon_white.png", "scheme": "lstmobile", "userInterfaceStyle": "automatic", @@ -15,10 +15,14 @@ "foregroundImage": "./assets/adaptive-icon-white.png", "backgroundColor": "#ffffff" }, - "versionCode": 37, + "versionCode": 39, "minSupportedVersionCode": 33, "predictiveBackGestureEnabled": false, - "package": "net.alpla.lst.mobile" + "package": "net.alpla.lst.mobile", + "permissions": [ + "android.permission.WRITE_EXTERNAL_STORAGE", + "android.permission.READ_EXTERNAL_STORAGE" + ] }, "web": { "output": "static", diff --git a/lstMobile/package-lock.json b/lstMobile/package-lock.json index d38ead1..31e65e4 100644 --- a/lstMobile/package-lock.json +++ b/lstMobile/package-lock.json @@ -35,6 +35,7 @@ "expo-image": "~55.0.8", "expo-linking": "~55.0.13", "expo-router": "~55.0.12", + "expo-screen-orientation": "~55.0.16", "expo-splash-screen": "~55.0.18", "expo-status-bar": "~55.0.5", "expo-symbols": "~55.0.7", @@ -46,6 +47,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-native": "0.83.4", + "react-native-blob-util": "^0.24.9", "react-native-gesture-handler": "~2.30.0", "react-native-reanimated": "4.2.1", "react-native-safe-area-context": "~5.6.2", @@ -6132,6 +6134,11 @@ } } }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8171,6 +8178,16 @@ "node": ">=10" } }, + "node_modules/expo-screen-orientation": { + "version": "55.0.16", + "resolved": "https://registry.npmjs.org/expo-screen-orientation/-/expo-screen-orientation-55.0.16.tgz", + "integrity": "sha512-I9NIqb2zAkHsK/CxdmMdmgSFP7E1v++8z/Mj2X9j1AuK6l55yOma/JHo905KU3x2zPm9/l1BTzmMA320tiBebg==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react-native": "*" + } + }, "node_modules/expo-server": { "version": "55.0.9", "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-55.0.9.tgz", @@ -12821,6 +12838,77 @@ } } }, + "node_modules/react-native-blob-util": { + "version": "0.24.9", + "resolved": "https://registry.npmjs.org/react-native-blob-util/-/react-native-blob-util-0.24.9.tgz", + "integrity": "sha512-tG3+m0WhVdBGifvxSFxZDVqtr85D0fGBJU6E4UxmK3tU+RabJZTumXEn8k7jn5/NFe8OhQhPjtBEZ11ZJ6L7Vw==", + "license": "MIT", + "dependencies": { + "base-64": "0.1.0", + "glob": "13.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ronradtke" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-blob-util/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/react-native-blob-util/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/react-native-blob-util/node_modules/glob": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.1.tgz", + "integrity": "sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.2", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/react-native-blob-util/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/react-native-css-interop": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz", diff --git a/lstMobile/package.json b/lstMobile/package.json index 3002278..3548a62 100644 --- a/lstMobile/package.json +++ b/lstMobile/package.json @@ -45,6 +45,7 @@ "expo-image": "~55.0.8", "expo-linking": "~55.0.13", "expo-router": "~55.0.12", + "expo-screen-orientation": "~55.0.16", "expo-splash-screen": "~55.0.18", "expo-status-bar": "~55.0.5", "expo-symbols": "~55.0.7", @@ -56,6 +57,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-native": "0.83.4", + "react-native-blob-util": "^0.24.9", "react-native-gesture-handler": "~2.30.0", "react-native-reanimated": "4.2.1", "react-native-safe-area-context": "~5.6.2",