@fctc/interface-logic 4.2.7 → 4.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/provider.mjs CHANGED
@@ -63,7 +63,8 @@ var initialState2 = {
63
63
  allowed_company_ids: [],
64
64
  lang: "vi_VN",
65
65
  tz: "Asia/Saigon"
66
- }
66
+ },
67
+ isLocalMode: false
67
68
  };
68
69
  var envSlice = createSlice2({
69
70
  name: "env",
@@ -2272,9 +2273,9 @@ function applyBinaryOp(ast, context) {
2272
2273
  var DICT = {
2273
2274
  get(...args) {
2274
2275
  const { key, defValue } = parseArgs(args, ["key", "defValue"]);
2275
- const self = this;
2276
- if (key in self) {
2277
- return self[key];
2276
+ const self2 = this;
2277
+ if (key in self2) {
2278
+ return self2[key];
2278
2279
  } else if (defValue !== void 0) {
2279
2280
  return defValue;
2280
2281
  }
@@ -5883,7 +5884,226 @@ var handleClosingSessionService = (env) => {
5883
5884
 
5884
5885
  // src/services/pos-service/load-data-pos-session.ts
5885
5886
  import { useCallback as useCallback31 } from "react";
5887
+
5888
+ // src/services/filesystem-service/file-service.ts
5889
+ import { Directory, Encoding, Filesystem } from "@capacitor/filesystem";
5890
+ var fileService = {
5891
+ async read(path) {
5892
+ try {
5893
+ const res = await Filesystem.readFile({
5894
+ path,
5895
+ directory: Directory.Data,
5896
+ encoding: Encoding.UTF8
5897
+ });
5898
+ if (typeof res.data === "string") return res.data;
5899
+ if (res.data instanceof Blob) return await res.data.text();
5900
+ return null;
5901
+ } catch {
5902
+ return null;
5903
+ }
5904
+ },
5905
+ async write(path, data) {
5906
+ await Filesystem.writeFile({
5907
+ path,
5908
+ data,
5909
+ directory: Directory.Data,
5910
+ encoding: Encoding.UTF8,
5911
+ recursive: true
5912
+ });
5913
+ },
5914
+ async writeAtomic(path, data) {
5915
+ const tempPath = path + ".tmp";
5916
+ await Filesystem.writeFile({
5917
+ path: tempPath,
5918
+ data,
5919
+ directory: Directory.Data,
5920
+ encoding: Encoding.UTF8,
5921
+ recursive: true
5922
+ });
5923
+ try {
5924
+ await Filesystem.deleteFile({
5925
+ path,
5926
+ directory: Directory.Data
5927
+ });
5928
+ } catch {
5929
+ }
5930
+ await Filesystem.rename({
5931
+ from: tempPath,
5932
+ to: path,
5933
+ directory: Directory.Data
5934
+ });
5935
+ },
5936
+ async delete(path) {
5937
+ try {
5938
+ await Filesystem.deleteFile({
5939
+ path,
5940
+ directory: Directory.Data
5941
+ });
5942
+ } catch {
5943
+ }
5944
+ },
5945
+ async exists(path) {
5946
+ try {
5947
+ await Filesystem.stat({
5948
+ path,
5949
+ directory: Directory.Data
5950
+ });
5951
+ return true;
5952
+ } catch {
5953
+ return false;
5954
+ }
5955
+ },
5956
+ async mkdir(path) {
5957
+ try {
5958
+ await Filesystem.mkdir({
5959
+ path,
5960
+ directory: Directory.Data,
5961
+ recursive: true
5962
+ });
5963
+ } catch (e) {
5964
+ if (!String(e?.message).includes("Exists")) {
5965
+ throw e;
5966
+ }
5967
+ }
5968
+ },
5969
+ async list(path) {
5970
+ return Filesystem.readdir({
5971
+ path,
5972
+ directory: Directory.Data
5973
+ });
5974
+ },
5975
+ async getUri(path) {
5976
+ return Filesystem.getUri({
5977
+ path,
5978
+ directory: Directory.Data
5979
+ });
5980
+ }
5981
+ };
5982
+
5983
+ // src/services/filesystem-service/json-worker.ts
5984
+ self.addEventListener("message", async (ev) => {
5985
+ const { id, cmd, payload } = ev.data;
5986
+ try {
5987
+ if (cmd === "parse") {
5988
+ const parsed = JSON.parse(payload);
5989
+ self.postMessage({ id, ok: true, result: parsed });
5990
+ } else if (cmd === "stringify") {
5991
+ const str = JSON.stringify(payload);
5992
+ self.postMessage({ id, ok: true, result: str });
5993
+ }
5994
+ } catch (err) {
5995
+ self.postMessage({ id, ok: false, error: err?.message || String(err) });
5996
+ }
5997
+ });
5998
+ function spawnParseWorker(raw) {
5999
+ return new Promise((resolve, reject) => {
6000
+ const worker = new Worker(new URL("./json-worker.ts", import.meta.url), {
6001
+ type: "module"
6002
+ });
6003
+ const id = Math.random().toString(36).slice(2);
6004
+ worker.onmessage = (ev) => {
6005
+ const { ok, result, error } = ev.data;
6006
+ if (ok) {
6007
+ resolve(result);
6008
+ } else {
6009
+ reject(new Error(error));
6010
+ }
6011
+ worker.terminate();
6012
+ };
6013
+ worker.postMessage({ id, cmd: "parse", payload: raw });
6014
+ });
6015
+ }
6016
+ function spawnStringifyWorker(obj) {
6017
+ return new Promise((resolve, reject) => {
6018
+ const worker = new Worker(new URL("./json-worker.ts", import.meta.url), {
6019
+ type: "module"
6020
+ });
6021
+ worker.onmessage = (ev) => {
6022
+ const { ok, result, error } = ev.data;
6023
+ if (ok) resolve(result);
6024
+ else reject(new Error(error));
6025
+ worker.terminate();
6026
+ };
6027
+ worker.postMessage({ cmd: "stringify", payload: obj });
6028
+ });
6029
+ }
6030
+
6031
+ // src/services/filesystem-service/memory-cache.ts
6032
+ var MemoryCache = class {
6033
+ map = /* @__PURE__ */ new Map();
6034
+ get(k) {
6035
+ const e = this.map.get(k);
6036
+ if (!e) return null;
6037
+ if (e.ttl && Date.now() - e.t > e.ttl) {
6038
+ this.map.delete(k);
6039
+ return null;
6040
+ }
6041
+ return e.value;
6042
+ }
6043
+ set(k, v, ttl = 5 * 60 * 1e3) {
6044
+ this.map.set(k, { value: v, t: Date.now(), ttl });
6045
+ }
6046
+ del(k) {
6047
+ this.map.delete(k);
6048
+ }
6049
+ clear() {
6050
+ this.map.clear();
6051
+ }
6052
+ };
6053
+ var memoryCache = new MemoryCache();
6054
+
6055
+ // src/services/filesystem-service/model-loader.ts
6056
+ var MODELS_DIR = "pos/models";
6057
+ var MODELS_META_DIR = "pos/models_meta";
6058
+ async function loadData(includeMeta = true) {
6059
+ const exists = await fileService.exists(MODELS_DIR);
6060
+ if (!exists) {
6061
+ console.warn("[loadData] MODELS_DIR not exists:", MODELS_DIR);
6062
+ return {};
6063
+ }
6064
+ let listResult;
6065
+ try {
6066
+ listResult = await fileService.list(MODELS_DIR);
6067
+ } catch (e) {
6068
+ console.error("[loadData] list failed:", e);
6069
+ return {};
6070
+ }
6071
+ if (!listResult || !Array.isArray(listResult.files)) {
6072
+ return {};
6073
+ }
6074
+ const result = {};
6075
+ for (const file of listResult.files) {
6076
+ if (file.type !== "file") continue;
6077
+ if (!file.name.endsWith(".json")) continue;
6078
+ const modelName = file.name.replace(/\.json$/, "");
6079
+ const dataPath = `${MODELS_DIR}/${file.name}`;
6080
+ const metaPath = `${MODELS_META_DIR}/${encodeURIComponent(
6081
+ modelName
6082
+ )}.meta.json`;
6083
+ const rawData = await fileService.read(dataPath);
6084
+ if (!rawData) continue;
6085
+ const parsedData = await spawnParseWorker(rawData);
6086
+ const data = Array.isArray(parsedData) ? parsedData : [];
6087
+ if (!includeMeta) {
6088
+ result[modelName] = { data };
6089
+ continue;
6090
+ }
6091
+ const rawMeta = await fileService.read(metaPath);
6092
+ let fields = [];
6093
+ let relations = {};
6094
+ if (rawMeta) {
6095
+ const parsedMeta = await spawnParseWorker(rawMeta);
6096
+ fields = parsedMeta?.fields ?? [];
6097
+ relations = parsedMeta?.relations ?? {};
6098
+ }
6099
+ result[modelName] = { data, fields, relations };
6100
+ }
6101
+ return result;
6102
+ }
6103
+
6104
+ // src/services/pos-service/load-data-pos-session.ts
5886
6105
  var loadDataPosSessionService = (env) => {
6106
+ const isLocalMode = env?.isLocalMode;
5887
6107
  const loadDataPosSession = useCallback31(
5888
6108
  ({
5889
6109
  model,
@@ -5894,6 +6114,14 @@ var loadDataPosSessionService = (env) => {
5894
6114
  modelsToLoad = [],
5895
6115
  searchParams
5896
6116
  }) => {
6117
+ console.log("isLocalMode", isLocalMode);
6118
+ if (isLocalMode) {
6119
+ return loadData().then((localDb) => ({
6120
+ data: {
6121
+ data: localDb
6122
+ }
6123
+ }));
6124
+ }
5897
6125
  const jsonData = {
5898
6126
  model,
5899
6127
  method: "load_data" /* LOAD_DATA */,
@@ -5920,7 +6148,7 @@ var loadDataPosSessionService = (env) => {
5920
6148
  service
5921
6149
  );
5922
6150
  },
5923
- [env]
6151
+ [env, isLocalMode]
5924
6152
  );
5925
6153
  return {
5926
6154
  loadDataPosSession
@@ -6253,6 +6481,145 @@ var usePosService = () => {
6253
6481
  return service;
6254
6482
  };
6255
6483
 
6484
+ // src/services/filesystem-service/manifest.ts
6485
+ var MANIFEST_PATH = "pos/manifest.json";
6486
+ var MANIFEST_BAK_PATH = "pos/manifest.bak.json";
6487
+ async function writeManifest(manifest) {
6488
+ const oldRaw = await fileService.read(MANIFEST_PATH);
6489
+ if (oldRaw !== null) {
6490
+ await fileService.writeAtomic(MANIFEST_BAK_PATH, oldRaw);
6491
+ }
6492
+ await fileService.writeAtomic(MANIFEST_PATH, JSON.stringify(manifest));
6493
+ try {
6494
+ await fileService.delete(MANIFEST_BAK_PATH);
6495
+ } catch {
6496
+ }
6497
+ }
6498
+
6499
+ // src/services/filesystem-service/import-snapshot.ts
6500
+ var DATA_DIR = "pos";
6501
+ var MODELS_DIR2 = `${DATA_DIR}/models`;
6502
+ var MODELS_META_DIR2 = `${DATA_DIR}/models_meta`;
6503
+ var importSnapshot = async ({ data, onProgress }) => {
6504
+ onProgress?.(1, "Parsing snapshot");
6505
+ const parsed = await spawnParseWorker(data);
6506
+ const modelNames = Object.keys(parsed);
6507
+ const total = modelNames.length;
6508
+ const manifest = { version: (/* @__PURE__ */ new Date()).toISOString(), models: {} };
6509
+ const TMP_PREFIX = `pos/data/tmp_import_${Date.now()}`;
6510
+ await fileService.writeAtomic(`${TMP_PREFIX}/.marker`, "1");
6511
+ let i = 0;
6512
+ for (const model of modelNames) {
6513
+ i++;
6514
+ onProgress?.(
6515
+ Math.round(i / total * 100),
6516
+ `Processing ${model} (${i}/${total})`
6517
+ );
6518
+ const block = parsed[model];
6519
+ const dataPart = block?.data ?? block ?? [];
6520
+ const fields = block?.fields ?? [];
6521
+ const relations = block?.relations ?? {};
6522
+ const serialized = await spawnStringifyWorker(dataPart);
6523
+ const tmpModelPath = `${TMP_PREFIX}/${encodeURIComponent(model)}.json`;
6524
+ await fileService.writeAtomic(tmpModelPath, serialized);
6525
+ const meta = {
6526
+ fields,
6527
+ relations,
6528
+ count: Array.isArray(dataPart) ? dataPart.length : 0,
6529
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString()
6530
+ };
6531
+ const tmpMetaPath = `${TMP_PREFIX}/${encodeURIComponent(model)}.meta.json`;
6532
+ await fileService.writeAtomic(tmpMetaPath, JSON.stringify(meta));
6533
+ manifest.models[model] = {
6534
+ file: `${MODELS_DIR2}/${encodeURIComponent(model)}.json`,
6535
+ metaFile: `${MODELS_META_DIR2}/${encodeURIComponent(model)}.meta.json`,
6536
+ count: meta.count,
6537
+ updatedAt: meta.writtenAt
6538
+ };
6539
+ }
6540
+ onProgress?.(95, "Committing import (moving files)");
6541
+ for (const model of modelNames) {
6542
+ const tmpModelPath = `${TMP_PREFIX}/${encodeURIComponent(model)}.json`;
6543
+ const finalModelPath = `${MODELS_DIR2}/${encodeURIComponent(model)}.json`;
6544
+ const tmpMetaPath = `${TMP_PREFIX}/${encodeURIComponent(model)}.meta.json`;
6545
+ const finalMetaPath = `${MODELS_META_DIR2}/${encodeURIComponent(
6546
+ model
6547
+ )}.meta.json`;
6548
+ const tmpRaw = await fileService.read(tmpModelPath);
6549
+ if (tmpRaw !== null) await fileService.writeAtomic(finalModelPath, tmpRaw);
6550
+ const tmpMetaRaw = await fileService.read(tmpMetaPath);
6551
+ if (tmpMetaRaw !== null)
6552
+ await fileService.writeAtomic(finalMetaPath, tmpMetaRaw);
6553
+ onProgress?.(
6554
+ 95 + Math.round(
6555
+ (Object.keys(manifest.models).indexOf(model) + 1) / modelNames.length * 5
6556
+ ),
6557
+ `Committed ${model}`
6558
+ );
6559
+ }
6560
+ await writeManifest(manifest);
6561
+ try {
6562
+ for (const model of modelNames) {
6563
+ await fileService.delete(
6564
+ `${TMP_PREFIX}/${encodeURIComponent(model)}.json`
6565
+ );
6566
+ await fileService.delete(
6567
+ `${TMP_PREFIX}/${encodeURIComponent(model)}.meta.json`
6568
+ );
6569
+ }
6570
+ await fileService.delete(`${TMP_PREFIX}/.marker`);
6571
+ } catch (e) {
6572
+ console.log("Failed to cleanup tmp import files:", e);
6573
+ }
6574
+ onProgress?.(100, "Import complete");
6575
+ return manifest;
6576
+ };
6577
+ var import_snapshot_default = importSnapshot;
6578
+
6579
+ // src/services/filesystem-service/init-snapshot.ts
6580
+ var isSnapshotReady = async () => {
6581
+ try {
6582
+ const raw = await fileService.read("pos/manifest.json");
6583
+ if (!raw) return false;
6584
+ const manifest = JSON.parse(raw);
6585
+ if (!manifest.models || typeof manifest.models !== "object") {
6586
+ return false;
6587
+ }
6588
+ const modelEntries = Object.values(manifest.models);
6589
+ if (modelEntries.length === 0) return false;
6590
+ const firstModel = modelEntries[0];
6591
+ if (!firstModel.file) return false;
6592
+ const modelExists = await fileService.exists(firstModel.file);
6593
+ return modelExists;
6594
+ } catch {
6595
+ return false;
6596
+ }
6597
+ };
6598
+ async function initSnapshot(onProgress) {
6599
+ const ready = await isSnapshotReady();
6600
+ if (ready) {
6601
+ console.log("skip initialization.");
6602
+ return;
6603
+ }
6604
+ console.log("initializing from data.json...");
6605
+ const jsonData = await fetch("/data.json").then((r) => r.text());
6606
+ if (!jsonData) {
6607
+ console.error("cannot load data.json");
6608
+ return;
6609
+ }
6610
+ await import_snapshot_default({
6611
+ data: jsonData,
6612
+ onProgress
6613
+ });
6614
+ }
6615
+
6616
+ // src/services/filesystem-service/index.ts
6617
+ var useFileSystemService = () => {
6618
+ return {
6619
+ initSnapshot
6620
+ };
6621
+ };
6622
+
6256
6623
  // src/provider/version-gate-provider.tsx
6257
6624
  import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
6258
6625
  var VersionGate = ({ children }) => {
@@ -6640,7 +7007,8 @@ var initialEnvState = {
6640
7007
  lang: "vi_VN",
6641
7008
  tz: "Asia/Saigon"
6642
7009
  },
6643
- excludeLanguages: []
7010
+ excludeLanguages: [],
7011
+ isLocalMode: false
6644
7012
  };
6645
7013
  var EnvContext = createContext(null);
6646
7014
  function EnvProvider({
@@ -8723,6 +9091,16 @@ var useUpdateOrderStatus = () => {
8723
9091
  };
8724
9092
  var use_update_order_status_default = useUpdateOrderStatus;
8725
9093
 
9094
+ // src/hooks/pos/use-init-snapshot.ts
9095
+ import { useMutation as useMutation86 } from "@tanstack/react-query";
9096
+ var useInitSnapshot = () => {
9097
+ const fileSystem = useFileSystemService();
9098
+ return useMutation86({
9099
+ mutationFn: fileSystem.initSnapshot
9100
+ });
9101
+ };
9102
+ var use_init_snapshot_default = useInitSnapshot;
9103
+
8726
9104
  // src/provider/service-provider.tsx
8727
9105
  import { jsx as jsx6 } from "react/jsx-runtime";
8728
9106
  var ServiceContext = createContext2(null);
@@ -8841,7 +9219,8 @@ var ServiceProvider = ({
8841
9219
  useGetCity: use_get_city_default,
8842
9220
  useGetWard: use_get_ward_default,
8843
9221
  useGetCountry: use_get_country_default,
8844
- useGetPartnerTitle: use_get_partner_title_default
9222
+ useGetPartnerTitle: use_get_partner_title_default,
9223
+ useInitSnapshot: use_init_snapshot_default
8845
9224
  };
8846
9225
  return /* @__PURE__ */ jsx6(ServiceContext.Provider, { value: services, children });
8847
9226
  };
@@ -1,4 +1,5 @@
1
1
  import { C as ContextApi, L as LoginCredentialBody, R as ResetPasswordRequest, U as UpdatePasswordRequest, T as TThreadData, h as GetExternalTab, b as GetListParams, a as GetDetailParams, S as SaveParams, D as DeleteParams, O as OnChangeParams, V as ViewData, f as GetViewParams, c as GetSelectionType } from './view-type-CfcWWR0w.mjs';
2
+ import { P as ProgressCb } from './import-snapshot-Ds0gqFFm.mjs';
2
3
 
3
4
  declare function useActionService(): {
4
5
  loadAction: ({ idAction, context, service, xNode, searchParams, }: {
@@ -611,4 +612,10 @@ type MergedService = UnionToIntersection<ServiceReturn<ServiceFactories>>;
611
612
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
612
613
  declare const usePosService: () => MergedService;
613
614
 
614
- export { useActionService, useAuthService, useCompanyService, useDashboardService, useExcelService, useFormService, useKanbanService, useModelService, usePosService, useUserService, useViewService };
615
+ declare function initSnapshot(onProgress?: ProgressCb): Promise<void>;
616
+
617
+ declare const useFileSystemService: () => {
618
+ initSnapshot: typeof initSnapshot;
619
+ };
620
+
621
+ export { useActionService, useAuthService, useCompanyService, useDashboardService, useExcelService, useFileSystemService, useFormService, useKanbanService, useModelService, usePosService, useUserService, useViewService };
@@ -1,4 +1,5 @@
1
1
  import { C as ContextApi, L as LoginCredentialBody, R as ResetPasswordRequest, U as UpdatePasswordRequest, T as TThreadData, h as GetExternalTab, b as GetListParams, a as GetDetailParams, S as SaveParams, D as DeleteParams, O as OnChangeParams, V as ViewData, f as GetViewParams, c as GetSelectionType } from './view-type-CfcWWR0w.js';
2
+ import { P as ProgressCb } from './import-snapshot-Ds0gqFFm.js';
2
3
 
3
4
  declare function useActionService(): {
4
5
  loadAction: ({ idAction, context, service, xNode, searchParams, }: {
@@ -611,4 +612,10 @@ type MergedService = UnionToIntersection<ServiceReturn<ServiceFactories>>;
611
612
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
612
613
  declare const usePosService: () => MergedService;
613
614
 
614
- export { useActionService, useAuthService, useCompanyService, useDashboardService, useExcelService, useFormService, useKanbanService, useModelService, usePosService, useUserService, useViewService };
615
+ declare function initSnapshot(onProgress?: ProgressCb): Promise<void>;
616
+
617
+ declare const useFileSystemService: () => {
618
+ initSnapshot: typeof initSnapshot;
619
+ };
620
+
621
+ export { useActionService, useAuthService, useCompanyService, useDashboardService, useExcelService, useFileSystemService, useFormService, useKanbanService, useModelService, usePosService, useUserService, useViewService };