@fctc/widget-logic 1.8.6 → 1.8.7

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/index.mjs CHANGED
@@ -5237,53 +5237,65 @@ var statusDropdownController = (props) => {
5237
5237
 
5238
5238
  // src/widget/basic/many2one-field/controller.ts
5239
5239
  import { useCallback as useCallback2, useEffect as useEffect10, useMemo as useMemo9, useState as useState7 } from "react";
5240
- import { useGetSelection } from "@fctc/interface-logic/hooks";
5241
- import {
5242
- useAppSelector as useAppSelector3,
5243
- selectNavbar,
5244
- selectEnv,
5245
- setListSubject
5246
- } from "@fctc/interface-logic/store";
5247
- import { evalJSONDomain as evalJSONDomain2, evalJSONContext as evalJSONContext2 } from "@fctc/interface-logic/utils";
5240
+
5241
+ // src/utils.ts
5242
+ var utils_exports = {};
5243
+ __export(utils_exports, {
5244
+ API_APP_URL: () => API_APP_URL,
5245
+ API_PRESCHOOL_URL: () => API_PRESCHOOL_URL,
5246
+ STORAGES: () => STORAGES,
5247
+ combineContexts: () => combineContexts,
5248
+ convertFieldsToArray: () => convertFieldsToArray,
5249
+ countSum: () => countSum,
5250
+ getDateRange: () => getDateRange,
5251
+ languages: () => languages,
5252
+ mergeButtons: () => mergeButtons,
5253
+ setStorageItemAsync: () => setStorageItemAsync,
5254
+ useGetRowIds: () => useGetRowIds,
5255
+ useSelectionState: () => useSelectionState,
5256
+ useStorageState: () => useStorageState
5257
+ });
5258
+ __reExport(utils_exports, utils_star2);
5259
+ import * as utils_star2 from "@fctc/interface-logic/utils";
5260
+
5261
+ // src/provider.ts
5262
+ var provider_exports = {};
5263
+ __reExport(provider_exports, provider_star);
5264
+ import * as provider_star from "@fctc/interface-logic/provider";
5265
+
5266
+ // src/widget/basic/many2one-field/controller.ts
5248
5267
  var many2oneFieldController = (props) => {
5249
5268
  const {
5250
- name,
5269
+ sessionStorageUtils,
5251
5270
  methods,
5252
- formValues,
5253
- domain,
5254
5271
  relation,
5255
- onChange,
5272
+ domain,
5273
+ formValues,
5256
5274
  value: propValue,
5275
+ onChange,
5276
+ name,
5257
5277
  context: fieldContext,
5258
5278
  options: fieldOptions,
5259
- showDetail = true,
5260
- actionData
5279
+ showDetail
5261
5280
  } = props;
5262
5281
  const [options, setOptions] = useState7([]);
5282
+ const [inputValue, setInputValue] = useState7("");
5283
+ const [debouncedInputValue] = useDebounce(inputValue, 1e3);
5263
5284
  const [isShowModalMany2Many, setIsShowModalMany2Many] = useState7(false);
5264
5285
  const [tempSelectedOption, setTempSelectedOption] = useState7(null);
5265
- const { menuList } = useAppSelector3(selectNavbar);
5266
- const { context } = useAppSelector3(selectEnv);
5267
5286
  const [domainModal, setDomainModal] = useState7(null);
5287
+ const [domainObject, setDomainObject] = useState7(null);
5288
+ const actionData = sessionStorageUtils.getActionData();
5289
+ const { menuList } = (0, store_exports.useAppSelector)(store_exports.selectNavbar);
5290
+ const { context } = (0, store_exports.useAppSelector)(store_exports.selectEnv);
5268
5291
  const initValue = methods?.getValues(name);
5269
- const domainObject = useMemo9(
5270
- () => evalJSONDomain2(domain, JSON.parse(JSON.stringify(formValues)) ?? {}),
5271
- [domain, formValues]
5272
- );
5273
- const optionsObject = evalJSONContext2(fieldOptions) || {};
5292
+ const optionsObject = (0, utils_exports.evalJSONContext)(fieldOptions) || {};
5274
5293
  const contextObject = {
5275
- ...evalJSONContext2(actionData?.context) || {},
5294
+ ...(0, utils_exports.evalJSONContext)(actionData?.context) || {},
5276
5295
  ...fieldContext,
5277
5296
  ...context
5278
5297
  };
5279
- const actionId = useMemo9(
5280
- () => menuList?.flatMap(
5281
- (item) => item?.child_id.filter(
5282
- (childItem) => childItem?.is_display && childItem?.action?.res_model === relation
5283
- )
5284
- )?.[0]?.action?.id,
5285
- [menuList, relation]
5286
- );
5298
+ const { useGetSelection: useGetSelection3 } = (0, provider_exports.useService)();
5287
5299
  const data = {
5288
5300
  model: relation,
5289
5301
  domain: domainObject,
@@ -5297,9 +5309,9 @@ var many2oneFieldController = (props) => {
5297
5309
  const queryKey = [`data_${relation}`, domainObject];
5298
5310
  const {
5299
5311
  data: dataOfSelection,
5300
- // refetch,
5312
+ refetch,
5301
5313
  isFetching
5302
- } = useGetSelection({
5314
+ } = useGetSelection3({
5303
5315
  data,
5304
5316
  queryKey,
5305
5317
  enabled: false
@@ -5313,8 +5325,16 @@ var many2oneFieldController = (props) => {
5313
5325
  useEffect10(() => {
5314
5326
  setOptions(selectOptions);
5315
5327
  setDomainModal(domainObject);
5316
- if (relation === "student.subject") setListSubject(selectOptions);
5328
+ if (relation === "student.subject") (0, store_exports.setListSubject)(selectOptions);
5317
5329
  }, [selectOptions]);
5330
+ useEffect10(() => {
5331
+ setDomainObject(
5332
+ (0, utils_exports.evalJSONDomain)(
5333
+ domain,
5334
+ JSON.parse(JSON.stringify({ ...formValues, context: contextObject })) ?? {}
5335
+ )
5336
+ );
5337
+ }, [domain, formValues]);
5318
5338
  useEffect10(() => {
5319
5339
  if (!propValue && tempSelectedOption) {
5320
5340
  methods.setValue(name, null);
@@ -5326,11 +5346,24 @@ var many2oneFieldController = (props) => {
5326
5346
  });
5327
5347
  }
5328
5348
  }, [propValue]);
5349
+ const fetchMoreOptions = useCallback2(() => {
5350
+ refetch();
5351
+ }, [refetch]);
5329
5352
  useEffect10(() => {
5330
- if (actionId) {
5331
- localStorage.setItem("aid", actionId);
5353
+ if (debouncedInputValue) {
5354
+ const filteredDomain = [...domainObject ?? []]?.filter(
5355
+ (d) => !(Array.isArray(d) && d[0] === "name" && d[1] === "ilike")
5356
+ ) || [];
5357
+ const newDomain = [
5358
+ ...filteredDomain,
5359
+ ...debouncedInputValue ? [["name", "ilike", debouncedInputValue]] : []
5360
+ ];
5361
+ setDomainObject(newDomain);
5362
+ setTimeout(() => {
5363
+ fetchMoreOptions();
5364
+ }, 50);
5332
5365
  }
5333
- }, [actionId]);
5366
+ }, [debouncedInputValue]);
5334
5367
  const handleChooseRecord = useCallback2(
5335
5368
  (idRecord) => {
5336
5369
  const newOption = options.find(
@@ -5382,40 +5415,30 @@ var many2oneFieldController = (props) => {
5382
5415
  [methods, name, onChange]
5383
5416
  );
5384
5417
  const allowShowDetail = showDetail && contextObject?.form_view_ref && (!("no_open" in optionsObject) || optionsObject?.no_open === false);
5385
- const fetchMoreOptions = useCallback2(() => {
5386
- if (typeof dataOfSelection?.refetch === "function") {
5387
- ;
5388
- dataOfSelection.refetch();
5389
- }
5390
- }, [dataOfSelection]);
5391
5418
  return {
5392
- allowShowDetail,
5393
- handleClose,
5419
+ isShowModalMany2Many,
5420
+ isFetching,
5421
+ initValue,
5422
+ menuList,
5394
5423
  handleChooseRecord,
5424
+ handleClose,
5395
5425
  handleSelectChange,
5396
- initValue,
5397
- isFetching,
5398
- isShowModalMany2Many,
5399
- options,
5400
- fetchMoreOptions,
5401
5426
  domainModal,
5402
- tempSelectedOption,
5403
- setTempSelectedOption,
5404
- setDomainModal,
5405
- dataOfSelection,
5406
- refetch: dataOfSelection?.refetch ?? (() => {
5407
- }),
5408
- selectOptions,
5409
- optionsObject,
5427
+ setInputValue,
5428
+ allowShowDetail,
5410
5429
  contextObject,
5411
- actionId,
5412
- setIsShowModalMany2Many
5430
+ tempSelectedOption,
5431
+ options,
5432
+ fetchMoreOptions,
5433
+ domainObject,
5434
+ setIsShowModalMany2Many,
5435
+ setDomainObject
5413
5436
  };
5414
5437
  };
5415
5438
 
5416
5439
  // src/widget/basic/many2one-button-field/controller.ts
5417
5440
  import { getEnv as getEnv6 } from "@fctc/interface-logic/environment";
5418
- import { useGetSelection as useGetSelection2 } from "@fctc/interface-logic/hooks";
5441
+ import { useGetSelection } from "@fctc/interface-logic/hooks";
5419
5442
  import { evalJSONDomain as evalJSONDomain3, evalJSONContext as evalJSONContext3 } from "@fctc/interface-logic/utils";
5420
5443
  var many2oneButtonController = (props) => {
5421
5444
  const { domain, methods, relation } = props;
@@ -5423,7 +5446,7 @@ var many2oneButtonController = (props) => {
5423
5446
  const env = getEnv6();
5424
5447
  const domainObject = evalJSONDomain3(domain, methods?.getValues() || {});
5425
5448
  const actionData = actionDataString && actionDataString !== "undefined" ? JSON.parse(actionDataString) : {};
5426
- const { data: dataOfSelection } = useGetSelection2({
5449
+ const { data: dataOfSelection } = useGetSelection({
5427
5450
  data: {
5428
5451
  model: relation ?? "",
5429
5452
  domain: domainObject,
@@ -5441,220 +5464,492 @@ var many2oneButtonController = (props) => {
5441
5464
  };
5442
5465
 
5443
5466
  // src/widget/basic/many2many-field/controller.ts
5444
- import { useEffect as useEffect15, useMemo as useMemo13, useState as useState11 } from "react";
5445
-
5446
- // src/widget/advance/table/table-body/controller.ts
5447
- import { useAppDispatch as useAppDispatch5, setSelectedRowKeys } from "@fctc/interface-logic/store";
5448
- import { useEffect as useEffect11, useMemo as useMemo10 } from "react";
5449
- var tableBodyController = (props) => {
5467
+ import { useEffect as useEffect11, useMemo as useMemo10, useState as useState8 } from "react";
5468
+ import {
5469
+ useAppDispatch as useAppDispatch5,
5470
+ setFirstDomain,
5471
+ setViewDataStore,
5472
+ setPage,
5473
+ setGroupByDomain
5474
+ } from "@fctc/interface-logic/store";
5475
+ import {
5476
+ evalJSONContext as evalJSONContext4,
5477
+ formatSortingString as formatSortingString2
5478
+ } from "@fctc/interface-logic/utils";
5479
+ var many2manyFieldController = (props) => {
5450
5480
  const {
5451
- checkedAll,
5452
- checkboxRef,
5453
- setIsAutoSelect,
5454
- selectedRowKeys,
5455
- row,
5456
- isAutoSelect,
5457
- selectedRowKeysRef,
5458
- onClickRow
5481
+ relation,
5482
+ domain,
5483
+ context,
5484
+ tab,
5485
+ model,
5486
+ aid,
5487
+ setSelectedRowKeys: setSelectedRowKeys4,
5488
+ fields,
5489
+ setFields,
5490
+ groupByDomain,
5491
+ page,
5492
+ options,
5493
+ sessionStorageUtils
5459
5494
  } = props;
5460
5495
  const appDispatch = useAppDispatch5();
5461
- const checked = useMemo10(() => {
5462
- if (!row?.id) return false;
5463
- if (selectedRowKeys?.includes(row.id)) {
5464
- return true;
5496
+ const actionData = sessionStorageUtils.getActionData();
5497
+ const [debouncedPage] = useDebounce(page, 500);
5498
+ const [order, setOrder] = useState8();
5499
+ const [isLoadedData, setIsLoadedData] = useState8(false);
5500
+ const [domainMany2Many, setDomainMany2Many] = useState8(domain);
5501
+ const { env } = (0, provider_exports.useEnv)();
5502
+ const { useGetView: useGetView2, useGetListData: useGetListData4, useGetFormView } = (0, provider_exports.useService)();
5503
+ const viewParams = {
5504
+ model: relation,
5505
+ views: [
5506
+ [false, "list"],
5507
+ [false, "search"]
5508
+ ],
5509
+ context
5510
+ };
5511
+ const { data: viewResponse } = useGetView2(viewParams, actionData);
5512
+ const baseModel = useMemo10(
5513
+ () => ({
5514
+ name: String(relation),
5515
+ view: viewResponse || {},
5516
+ actContext: context,
5517
+ fields: [
5518
+ ...Object.values(viewResponse?.views?.list?.fields ?? {}),
5519
+ ...tab?.fields ? tab.fields : []
5520
+ ]
5521
+ }),
5522
+ [model, viewResponse]
5523
+ );
5524
+ const initModel = (0, hooks_exports.useModel)();
5525
+ const modelInstance = useMemo10(() => {
5526
+ if (viewResponse) {
5527
+ return initModel.initModel(baseModel);
5465
5528
  }
5466
- return checkedAll;
5467
- }, [row?.id, selectedRowKeys, checkedAll]);
5468
- const handleCheckBoxSingle = (event) => {
5469
- event.stopPropagation();
5470
- if (checkedAll) {
5471
- checkboxRef.current = "uncheck";
5472
- setIsAutoSelect(true);
5473
- return;
5529
+ return null;
5530
+ }, [baseModel, viewResponse]);
5531
+ const specification = useMemo10(() => {
5532
+ if (modelInstance) {
5533
+ return modelInstance.getSpecification();
5534
+ }
5535
+ return null;
5536
+ }, [modelInstance]);
5537
+ const default_order = viewResponse && viewResponse?.views?.list?.default_order;
5538
+ const optionsObject = tab?.options ? evalJSONContext4(tab?.options) : (options ? evalJSONContext4(options) : {}) || {};
5539
+ const fetchData = async () => {
5540
+ try {
5541
+ setDomainMany2Many(domain);
5542
+ appDispatch(setFirstDomain(domain));
5543
+ appDispatch(setViewDataStore(viewResponse));
5544
+ const modalData = viewResponse?.views?.list?.fields.map((field) => ({
5545
+ ...viewResponse?.models?.[String(model)]?.[field?.name],
5546
+ ...field
5547
+ }));
5548
+ if (!fields?.[`${aid}_${relation}_popupmany2many`] && modalData) {
5549
+ setFields({
5550
+ ...fields,
5551
+ [`${aid}_${relation}_popupmany2many`]: modalData
5552
+ });
5553
+ }
5554
+ appDispatch(setPage(0));
5555
+ } catch (err) {
5556
+ console.log(err);
5474
5557
  }
5475
- const newSelectedRowKeys = selectedRowKeys?.includes(row.id) ? selectedRowKeys?.filter((key) => key !== row.id) : [...selectedRowKeys, row.id];
5476
- console.log("newSelectedRowKeys", newSelectedRowKeys);
5477
- appDispatch(setSelectedRowKeys(newSelectedRowKeys));
5478
5558
  };
5479
- const handleClickRow = (col, row2) => {
5480
- onClickRow(col, row2);
5559
+ const queryKey = [
5560
+ `view-${relation}-${aid}`,
5561
+ specification,
5562
+ domainMany2Many,
5563
+ debouncedPage,
5564
+ groupByDomain,
5565
+ order
5566
+ ];
5567
+ const data = {
5568
+ model: relation,
5569
+ specification,
5570
+ domain: domainMany2Many,
5571
+ offset: debouncedPage * 10,
5572
+ limit: 10,
5573
+ context,
5574
+ fields: groupByDomain?.fields,
5575
+ groupby: [groupByDomain?.contexts[0]?.group_by],
5576
+ sort: order ? order : default_order ? formatSortingString2(default_order) : ""
5481
5577
  };
5578
+ const enabled = isLoadedData && !!specification && !!relation && !!domainMany2Many && !!viewResponse;
5579
+ const {
5580
+ data: dataResponse,
5581
+ isLoading: isDataLoading,
5582
+ isFetched: isDataResponseFetched,
5583
+ isPlaceholderData
5584
+ } = useGetListData4(data, queryKey, enabled);
5482
5585
  useEffect11(() => {
5483
- if (!row?.id) return;
5484
- if (isAutoSelect) {
5485
- if (checkboxRef?.current === "uncheck") {
5486
- const filtered = selectedRowKeysRef.current.filter(
5487
- (id) => id !== row.id
5488
- );
5489
- selectedRowKeysRef.current = filtered;
5490
- appDispatch(setSelectedRowKeys(filtered));
5491
- } else {
5492
- const unique = Array.from(
5493
- /* @__PURE__ */ new Set([...selectedRowKeysRef?.current, row?.id])
5494
- );
5495
- selectedRowKeysRef.current = unique;
5496
- appDispatch(setSelectedRowKeys(unique));
5497
- }
5586
+ if (viewResponse) {
5587
+ fetchData();
5498
5588
  }
5499
- }, [isAutoSelect]);
5500
- useEffect11(() => {
5501
- if (!checkedAll) {
5502
- checkboxRef.current = "enabled";
5503
- false;
5589
+ return () => {
5590
+ appDispatch(setGroupByDomain(null));
5591
+ setFields((prevFields) => ({
5592
+ ...prevFields,
5593
+ [`${aid}_${relation}_popupmany2many`]: null
5594
+ }));
5595
+ appDispatch(setPage(0));
5596
+ setSelectedRowKeys4([]);
5597
+ setDomainMany2Many(null);
5598
+ setIsLoadedData(false);
5599
+ };
5600
+ }, [viewResponse]);
5601
+ const { rows, columns, typeTable } = tableController({
5602
+ data: {
5603
+ fields: fields?.[`${aid}_${relation}_popupmany2many`] || viewResponse?.views?.list?.fields,
5604
+ records: dataResponse?.records ?? dataResponse?.groups,
5605
+ dataModel: viewResponse?.models?.[String(relation)],
5606
+ context: { ...env.context, ...context },
5607
+ typeTable: dataResponse?.groups ? "group" : "list"
5504
5608
  }
5505
- }, [checkedAll]);
5506
- return {
5507
- handleCheckBoxSingle,
5508
- checked,
5509
- handleClickRow
5609
+ });
5610
+ const dataFormView = {
5611
+ id: null,
5612
+ model: relation,
5613
+ context
5510
5614
  };
5511
- };
5512
-
5513
- // src/widget/advance/table/table-head/controller.ts
5514
- import {
5515
- useAppDispatch as useAppDispatch6,
5516
- useAppSelector as useAppSelector4,
5517
- selectSearch as selectSearch3,
5518
- setSelectedRowKeys as setSelectedRowKeys2
5519
- } from "@fctc/interface-logic/store";
5520
- var tableHeadController = (props) => {
5521
- const { typeTable, rows, selectedRowKeysRef } = props;
5522
- const appDispatch = useAppDispatch6();
5523
- const { groupByDomain } = useAppSelector4(selectSearch3);
5524
- const handleCheckBoxAll = (event) => {
5525
- if (event?.target?.checked && typeTable === "list") {
5526
- const allRowKeys = Array.isArray(rows) ? rows.map((record) => record?.id) : [];
5527
- appDispatch(setSelectedRowKeys2(allRowKeys));
5528
- } else if (event?.target?.checked && typeTable === "group") {
5529
- const rowsIDs = document.querySelectorAll("tr[data-row-id]");
5530
- const ids = Array.from(rowsIDs)?.map(
5531
- (row) => Number(row?.getAttribute("data-row-id"))
5532
- );
5533
- if (ids?.length > 0) {
5534
- appDispatch(setSelectedRowKeys2(ids));
5535
- } else {
5536
- const sum = countSum(
5537
- rows,
5538
- typeof groupByDomain === "object" ? groupByDomain?.contexts?.[0]?.group_by : void 0
5539
- );
5540
- const keys = Array.from({ length: sum }, (_) => void 0);
5541
- appDispatch(setSelectedRowKeys2(keys));
5542
- }
5543
- if (selectedRowKeysRef) {
5544
- selectedRowKeysRef.current = [];
5545
- }
5546
- } else {
5547
- appDispatch(setSelectedRowKeys2([]));
5615
+ const {
5616
+ refetch,
5617
+ data: dataFormViewResponse,
5618
+ isSuccess
5619
+ } = useGetFormView({
5620
+ data: dataFormView,
5621
+ queryKey: [`form-view-action-${relation}`],
5622
+ enabled: false
5623
+ });
5624
+ useEffect11(() => {
5625
+ if (isSuccess && dataFormViewResponse) {
5626
+ sessionStorage.setItem("actionData", JSON.stringify(dataFormViewResponse));
5627
+ window.location.href = `/form/menu?model=${relation}`;
5628
+ }
5629
+ }, [isSuccess]);
5630
+ useEffect11(() => {
5631
+ if (domainMany2Many && !isLoadedData) {
5632
+ setIsLoadedData(true);
5633
+ }
5634
+ }, [domainMany2Many]);
5635
+ const handleCreateNewOnPage = async () => {
5636
+ try {
5637
+ refetch();
5638
+ } catch (error) {
5639
+ console.log(error);
5548
5640
  }
5549
5641
  };
5550
5642
  return {
5551
- handleCheckBoxAll
5643
+ handleCreateNewOnPage,
5644
+ optionsObject,
5645
+ rows,
5646
+ columns,
5647
+ typeTable,
5648
+ isDataLoading,
5649
+ isDataResponseFetched,
5650
+ isPlaceholderData
5552
5651
  };
5553
5652
  };
5554
5653
 
5555
- // src/widget/advance/table/table-view/controller.ts
5556
- import { useEffect as useEffect12, useMemo as useMemo11, useRef as useRef4, useState as useState8 } from "react";
5557
- import {
5558
- useAppSelector as useAppSelector5,
5559
- selectSearch as selectSearch4,
5560
- selectList as selectList3
5561
- } from "@fctc/interface-logic/store";
5562
- import { domainHelper } from "@fctc/interface-logic/utils";
5563
- var tableController = ({ data }) => {
5564
- const [rows, setRows] = useState8(data.records || []);
5565
- const [columns, setColumns] = useState8([]);
5566
- const dataModelFields = data.fields?.map((field) => {
5567
- return {
5568
- ...data.dataModel?.[field?.name],
5569
- ...field,
5570
- string: field?.string || data.dataModel?.[field?.name]?.string
5571
- };
5654
+ // src/widget/basic/many2many-tags-field/controller.ts
5655
+ import { useMemo as useMemo11 } from "react";
5656
+ import { WIDGETAVATAR, WIDGETCOLOR } from "@fctc/interface-logic/constants";
5657
+ import { getEnv as getEnv7 } from "@fctc/interface-logic/environment";
5658
+ import { useGetSelection as useGetSelection2 } from "@fctc/interface-logic/hooks";
5659
+ import { evalJSONContext as evalJSONContext5, evalJSONDomain as evalJSONDomain4 } from "@fctc/interface-logic/utils";
5660
+ var many2manyTagsController = (props) => {
5661
+ const {
5662
+ relation,
5663
+ domain,
5664
+ options: optionsFields,
5665
+ widget,
5666
+ formValues,
5667
+ placeholderNoOption
5668
+ } = props;
5669
+ const isUser = relation === "res.users" || relation === "res.partner";
5670
+ const env = getEnv7();
5671
+ const addtionalFields = optionsFields ? evalJSONContext5(optionsFields) : null;
5672
+ const domainObject = useMemo11(
5673
+ () => evalJSONDomain4(domain, JSON.parse(JSON.stringify(formValues || {}))),
5674
+ [domain, formValues]
5675
+ );
5676
+ const data = {
5677
+ model: relation ?? "",
5678
+ domain: domainObject,
5679
+ specification: {
5680
+ id: {},
5681
+ name: {},
5682
+ display_name: {},
5683
+ ...widget && WIDGETAVATAR[widget] ? { image_256: {} } : {},
5684
+ ...widget && WIDGETCOLOR[widget] && addtionalFields?.color_field ? { color: {} } : {}
5685
+ },
5686
+ enabled: true,
5687
+ context: env.context
5688
+ };
5689
+ const { data: dataOfSelection } = useGetSelection2({
5690
+ data,
5691
+ queryKey: [`data_${relation}`, domainObject]
5572
5692
  });
5573
- const mergeFields = mergeButtons(dataModelFields);
5574
- const transformData = (dataList) => {
5575
- if (!dataList) return;
5576
- return dataList?.map((item) => {
5577
- const transformedItem = { ...item };
5578
- Object.keys(item).forEach((field) => {
5579
- if (field !== "__domain") {
5580
- if (item[field] && typeof item[field] === "object" && item[field].display_name) {
5581
- transformedItem[field] = item[field];
5582
- } else if (Array.isArray(item[field]) && item[field].length > 0) {
5583
- if (data.typeTable === "group" && item[field]?.length === 2 && typeof item[field]?.[1] === "string") {
5584
- transformedItem["string"] = item[field]?.[1];
5693
+ const customNoOptionsMessage = () => placeholderNoOption;
5694
+ const tranfer = (data2) => {
5695
+ return data2?.map((val) => ({
5696
+ id: val.value,
5697
+ display_name: val.label
5698
+ })) || [];
5699
+ };
5700
+ const options = dataOfSelection?.records?.map((val) => ({
5701
+ value: val.id,
5702
+ label: val.name ?? val.display_name,
5703
+ ...val
5704
+ })) || [];
5705
+ return {
5706
+ options,
5707
+ customNoOptionsMessage,
5708
+ tranfer,
5709
+ dataOfSelection,
5710
+ isUser
5711
+ };
5712
+ };
5713
+
5714
+ // src/widget/basic/status-bar-field/controller.ts
5715
+ import { useState as useState9 } from "react";
5716
+ import { useGetListData as useGetListData2, useChangeStatus } from "@fctc/interface-logic/hooks";
5717
+ import { useAppSelector as useAppSelector4, selectEnv as selectEnv2 } from "@fctc/interface-logic/store";
5718
+ import { evalJSONDomain as evalJSONDomain5 } from "@fctc/interface-logic/utils";
5719
+ var durationController = (props) => {
5720
+ const {
5721
+ relation,
5722
+ defaultValue,
5723
+ domain,
5724
+ formValues,
5725
+ name,
5726
+ id,
5727
+ model,
5728
+ onRefetch
5729
+ } = props;
5730
+ const specification = {
5731
+ id: 0,
5732
+ name: "",
5733
+ fold: ""
5734
+ };
5735
+ const [disabled, setDisabled] = useState9(false);
5736
+ const [modelStatus, setModalStatus] = useState9(false);
5737
+ const { context } = useAppSelector4(selectEnv2);
5738
+ const queryKey = [`data-status-duration`, specification];
5739
+ const listDataProps = {
5740
+ model: relation,
5741
+ specification,
5742
+ domain: evalJSONDomain5(domain, JSON.parse(JSON.stringify(formValues))),
5743
+ limit: 10,
5744
+ offset: 0,
5745
+ fields: "",
5746
+ groupby: [],
5747
+ context: {
5748
+ lang: context.lang
5749
+ },
5750
+ sort: ""
5751
+ };
5752
+ const { data: dataResponse } = useGetListData2(listDataProps, queryKey);
5753
+ const { mutate: fetchChangeStatus } = useChangeStatus();
5754
+ const handleClick = async (stage_id) => {
5755
+ setDisabled(true);
5756
+ if (stage_id) {
5757
+ fetchChangeStatus(
5758
+ {
5759
+ data: {
5760
+ stage_id,
5761
+ name,
5762
+ id,
5763
+ model,
5764
+ lang: context.lang
5765
+ }
5766
+ },
5767
+ {
5768
+ onSuccess: (res) => {
5769
+ if (res) {
5770
+ setDisabled(false);
5771
+ onRefetch && onRefetch();
5585
5772
  }
5586
- transformedItem[field] = item[field];
5587
5773
  }
5588
5774
  }
5589
- });
5590
- return item.display_name ? { ...transformedItem, item: item.display_name } : transformedItem;
5591
- });
5592
- };
5593
- useEffect12(() => {
5594
- setRows(transformData(data.records || null));
5595
- }, [data.records]);
5596
- const handleGetColumns = () => {
5597
- let cols = [];
5598
- try {
5599
- cols = mergeFields?.filter((item) => {
5600
- return item?.widget !== "details_Receive_money" && !(item?.column_invisible ? domainHelper.matchDomains(data.context, item?.column_invisible) : item?.invisible ? domainHelper.matchDomains(data.context, item?.invisible) : false);
5601
- })?.map((field) => {
5602
- return {
5603
- name: field?.name,
5604
- optional: field?.optional,
5605
- title: field?.type_co === "button" ? "" : field?.string,
5606
- field: { ...field }
5607
- };
5608
- });
5609
- } catch (error) {
5610
- console.error("Error in useTable:", error);
5775
+ );
5611
5776
  }
5612
- return cols;
5613
- };
5614
- useEffect12(() => {
5615
- const columns2 = handleGetColumns();
5616
- setColumns(columns2);
5617
- }, [data.records]);
5618
- const onToggleColumnOptional = (item) => {
5619
- const tempColumn = [...columns]?.map((val) => {
5620
- if (item?.name === val?.name) {
5621
- return {
5622
- ...val,
5623
- optional: item?.optional === "show" ? "hide" : "show"
5624
- };
5625
- }
5626
- return val;
5627
- });
5628
- setColumns(tempColumn);
5629
5777
  };
5630
5778
  return {
5631
- rows,
5632
- columns,
5633
- onToggleColumnOptional,
5634
- typeTable: data.typeTable
5779
+ defaultValue,
5780
+ dataResponse,
5781
+ handleClick,
5782
+ disabled,
5783
+ modelStatus,
5784
+ setModalStatus
5635
5785
  };
5636
5786
  };
5637
5787
 
5638
- // src/widget/advance/table/table-group/controller.ts
5639
- import { useEffect as useEffect13, useMemo as useMemo12, useState as useState9 } from "react";
5640
-
5641
- // src/utils/i18n.ts
5642
- import { initReactI18next } from "react-i18next";
5643
- import i18n from "i18next";
5644
- import LanguageDetector from "i18next-browser-languagedetector";
5645
-
5646
- // src/locales/vi.json
5647
- var vi_default = {
5648
- login: "\u0110\u0103ng Nh\u1EADp",
5649
- english: "Ti\u1EBFng Anh",
5650
- vietnamese: "Ti\u1EBFng Vi\u1EC7t",
5651
- note_login: "Xin h\xE3y \u0111\u0103ng nh\u1EADp v\xE0o t\xE0i kho\u1EA3n c\u1EE7a b\u1EA1n",
5652
- "note-forgotpassword": "Vui l\xF2ng nh\u1EADp \u0111\u1ECBa ch\u1EC9 email b\u1EA1n mu\u1ED1n th\xF4ng tin \u0111\u1EB7t l\u1EA1i m\u1EADt kh\u1EA9u c\u1EE7a b\u1EA1n \u0111\u01B0\u1EE3c g\u1EEDi t\u1EDBi ",
5653
- placeholder_email: "Nh\u1EADp \u0111\u1ECBa ch\u1EC9 email c\u1EE7a b\u1EA1n",
5654
- placeholder_password: "Nh\u1EADp m\u1EADt kh\u1EA9u",
5655
- placeholder_username: "Nh\u1EADp t\xEAn \u0111\u0103ng nh\u1EADp",
5656
- validate_password_1: "M\u1EADt kh\u1EA9u ph\u1EA3i c\xF3 \xEDt nh\u1EA5t 8 k\xFD t\u1EF1.",
5657
- validate_password_2: "M\u1EADt kh\u1EA9u g\u1ED3m 1 ch\u1EEF hoa.",
5788
+ // src/widget/basic/priority-field/controller.ts
5789
+ import { useSave as useSave2 } from "@fctc/interface-logic/hooks";
5790
+ import { evalJSONContext as evalJSONContext6 } from "@fctc/interface-logic/utils";
5791
+ var priorityFieldController = (props) => {
5792
+ const {
5793
+ value,
5794
+ isForm,
5795
+ name,
5796
+ methods,
5797
+ onChange,
5798
+ model,
5799
+ selection,
5800
+ id,
5801
+ actionData,
5802
+ viewData,
5803
+ context
5804
+ } = props;
5805
+ const _context = { ...evalJSONContext6(actionData?.context) };
5806
+ const contextObject = { ...context, ..._context };
5807
+ const defaultPriority = parseInt(value) + 1;
5808
+ const label = viewData?.models?.[model]?.[name ?? ""]?.string ?? name;
5809
+ const { mutateAsync: fetchSave } = useSave2();
5810
+ const savePriorities = async ({
5811
+ value: value2,
5812
+ resetPriority
5813
+ }) => {
5814
+ const priorityValue = value2 <= 0 ? 0 : value2 - 1;
5815
+ try {
5816
+ fetchSave({
5817
+ ids: id ? [id] : [],
5818
+ data: { [name ?? ""]: String(priorityValue) },
5819
+ model: model ?? "",
5820
+ context: contextObject
5821
+ });
5822
+ if (typeof onChange === "function") {
5823
+ onChange(name ?? "", String(priorityValue));
5824
+ }
5825
+ } catch (error) {
5826
+ if (resetPriority) {
5827
+ resetPriority();
5828
+ }
5829
+ }
5830
+ };
5831
+ return {
5832
+ selection,
5833
+ isForm,
5834
+ methods,
5835
+ defaultPriority,
5836
+ savePriorities,
5837
+ label,
5838
+ id,
5839
+ onChange
5840
+ };
5841
+ };
5842
+
5843
+ // src/widget/basic/float-time-field/controller.ts
5844
+ import { useState as useState10 } from "react";
5845
+ import {
5846
+ convertFloatToTime,
5847
+ convertTimeToFloat
5848
+ } from "@fctc/interface-logic/utils";
5849
+ var floatTimeFiledController = ({
5850
+ onChange: fieldOnChange,
5851
+ onBlur,
5852
+ value,
5853
+ isDirty,
5854
+ props
5855
+ }) => {
5856
+ const { name, defaultValue = 0, onChange } = props;
5857
+ const [input, setInput] = useState10(
5858
+ convertFloatToTime(value ?? defaultValue)
5859
+ );
5860
+ const [formattedTime, setFormattedTime] = useState10("");
5861
+ const [errors, setErrors] = useState10("");
5862
+ const handleInputChange = (e) => {
5863
+ const raw = e.target.value.replace(/[^\d:]/g, "");
5864
+ setInput(raw);
5865
+ const timeRegex = /^(\d{1,2}):?(\d{0,2})$/;
5866
+ const match = raw.match(timeRegex);
5867
+ if (!match) {
5868
+ setErrors("\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7");
5869
+ setFormattedTime("");
5870
+ return;
5871
+ }
5872
+ let hours = parseInt(match[1] ?? "0", 10);
5873
+ let minutes = parseInt(match[2] ?? "0", 10);
5874
+ if (isNaN(hours)) hours = 0;
5875
+ if (isNaN(minutes)) minutes = 0;
5876
+ if (hours >= 24) {
5877
+ hours = 0;
5878
+ }
5879
+ if (minutes >= 60) {
5880
+ minutes = 0;
5881
+ }
5882
+ const formatted = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
5883
+ setErrors("");
5884
+ setFormattedTime(formatted);
5885
+ fieldOnChange(formatted);
5886
+ };
5887
+ const handleBlur = () => {
5888
+ if (!isDirty) return;
5889
+ if (formattedTime) {
5890
+ setInput(formattedTime);
5891
+ const floatVal = convertTimeToFloat(formattedTime);
5892
+ fieldOnChange(floatVal);
5893
+ if (onChange) {
5894
+ onChange(name ?? "", floatVal);
5895
+ }
5896
+ } else {
5897
+ setInput("00:00");
5898
+ fieldOnChange(0);
5899
+ if (onChange) {
5900
+ onChange(name ?? "", 0);
5901
+ }
5902
+ setErrors("");
5903
+ }
5904
+ onBlur();
5905
+ };
5906
+ const handleKeyDown = (e) => {
5907
+ {
5908
+ const allowed = [
5909
+ "Backspace",
5910
+ "Tab",
5911
+ "ArrowLeft",
5912
+ "ArrowRight",
5913
+ "Delete",
5914
+ "Home",
5915
+ "End",
5916
+ ":"
5917
+ ];
5918
+ const isNumber = /^[0-9]$/.test(e.key);
5919
+ if (!isNumber && !allowed.includes(e.key)) {
5920
+ e.preventDefault();
5921
+ }
5922
+ }
5923
+ };
5924
+ return {
5925
+ handleInputChange,
5926
+ handleBlur,
5927
+ handleKeyDown,
5928
+ input,
5929
+ errors
5930
+ };
5931
+ };
5932
+
5933
+ // src/widget/basic/float-field/controller.ts
5934
+ import { useEffect as useEffect12, useRef as useRef4, useState as useState11 } from "react";
5935
+
5936
+ // src/utils/i18n.ts
5937
+ import { initReactI18next } from "react-i18next";
5938
+ import i18n from "i18next";
5939
+ import LanguageDetector from "i18next-browser-languagedetector";
5940
+
5941
+ // src/locales/vi.json
5942
+ var vi_default = {
5943
+ login: "\u0110\u0103ng Nh\u1EADp",
5944
+ english: "Ti\u1EBFng Anh",
5945
+ vietnamese: "Ti\u1EBFng Vi\u1EC7t",
5946
+ note_login: "Xin h\xE3y \u0111\u0103ng nh\u1EADp v\xE0o t\xE0i kho\u1EA3n c\u1EE7a b\u1EA1n",
5947
+ "note-forgotpassword": "Vui l\xF2ng nh\u1EADp \u0111\u1ECBa ch\u1EC9 email b\u1EA1n mu\u1ED1n th\xF4ng tin \u0111\u1EB7t l\u1EA1i m\u1EADt kh\u1EA9u c\u1EE7a b\u1EA1n \u0111\u01B0\u1EE3c g\u1EEDi t\u1EDBi ",
5948
+ placeholder_email: "Nh\u1EADp \u0111\u1ECBa ch\u1EC9 email c\u1EE7a b\u1EA1n",
5949
+ placeholder_password: "Nh\u1EADp m\u1EADt kh\u1EA9u",
5950
+ placeholder_username: "Nh\u1EADp t\xEAn \u0111\u0103ng nh\u1EADp",
5951
+ validate_password_1: "M\u1EADt kh\u1EA9u ph\u1EA3i c\xF3 \xEDt nh\u1EA5t 8 k\xFD t\u1EF1.",
5952
+ validate_password_2: "M\u1EADt kh\u1EA9u g\u1ED3m 1 ch\u1EEF hoa.",
5658
5953
  validate_password_3: "M\u1EADt kh\u1EA9u g\u1ED3m 1 s\u1ED1.",
5659
5954
  validate_password_4: "M\u1EADt kh\u1EA9u g\u1ED3m 1 k\xFD t\u1EF1 \u0111\u1EB7c bi\u1EC7t.",
5660
5955
  validate_password_5: "M\u1EADt kh\u1EA9u kh\xF4ng kh\u1EDBp.",
@@ -6404,1378 +6699,1087 @@ i18n.use(LanguageDetector).use(initReactI18next).init({
6404
6699
  });
6405
6700
  var i18n_default = i18n;
6406
6701
 
6407
- // src/widget/advance/table/table-group/controller.ts
6408
- import {
6409
- useOdooDataTransform,
6410
- useGetListData as useGetListData2
6411
- } from "@fctc/interface-logic/hooks";
6412
- import {
6413
- useAppSelector as useAppSelector6,
6414
- selectSearch as selectSearch5,
6415
- selectList as selectList4,
6416
- useAppDispatch as useAppDispatch7,
6417
- setSelectedRowKeys as setSelectedRowKeys3
6418
- } from "@fctc/interface-logic/store";
6419
-
6420
- // src/environment.ts
6421
- var environment_exports = {};
6422
- __reExport(environment_exports, environment_star);
6423
- import * as environment_star from "@fctc/interface-logic/environment";
6424
-
6425
- // src/widget/advance/table/table-group/controller.ts
6426
- var tableGroupController = (props) => {
6427
- const env = (0, environment_exports.getEnv)();
6428
- const {
6429
- rows,
6430
- columns,
6431
- indexRow,
6432
- row,
6433
- model,
6434
- viewData,
6435
- renderField,
6436
- level,
6437
- specification,
6438
- domain,
6439
- context,
6440
- checkedAll,
6441
- isDisplayCheckbox,
6442
- isAutoSelect,
6443
- setIsAutoSelect,
6444
- selectedRowKeysRef
6445
- } = props;
6446
- const [pageGroup, setPageGroup] = useState9(0);
6447
- const { groupByDomain, selectedTags } = useAppSelector6(selectSearch5);
6448
- const { selectedRowKeys } = useAppSelector6(selectList4);
6449
- const appDispatch = useAppDispatch7();
6450
- const { toDataJS } = useOdooDataTransform();
6451
- const initVal = toDataJS(row, viewData, model);
6452
- const [isShowGroup, setIsShowGroup] = useState9(false);
6453
- const [colEmptyGroup, setColEmptyGroup] = useState9({
6454
- fromStart: 1,
6455
- fromEnd: 1
6456
- });
6457
- const processedData = useMemo12(() => {
6458
- const calculateColSpanEmpty = () => {
6459
- const startIndex = columns.findIndex(
6460
- (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator === "sum"
6461
- );
6462
- const endIndex = columns.findLastIndex(
6463
- (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator !== "sum"
6464
- );
6465
- const fromStart = startIndex === -1 ? columns.length : startIndex;
6466
- const fromEnd = endIndex === -1 ? columns.length : columns.length - 1 - endIndex;
6467
- setColEmptyGroup({ fromStart: fromStart + 1, fromEnd: fromEnd + 1 });
6468
- return { fromStart: fromStart + 1, fromEnd: fromEnd + 1 };
6469
- };
6470
- return calculateColSpanEmpty();
6471
- }, [columns, row]);
6472
- const shouldFetchData = useMemo12(() => {
6473
- return !!isShowGroup;
6474
- }, [isShowGroup]);
6475
- const enabled = shouldFetchData && !!processedData;
6476
- const listDataProps = {
6477
- model,
6478
- specification,
6479
- domain,
6480
- context,
6481
- offset: pageGroup * 10,
6482
- fields: groupByDomain?.fields,
6483
- groupby: [groupByDomain?.contexts[level]?.group_by]
6484
- };
6485
- const queryKey = [
6486
- `data-${model}--${level}-row${indexRow}`,
6487
- specification,
6488
- domain,
6489
- pageGroup
6490
- ];
6491
- const {
6492
- data: dataResponse,
6493
- isFetched: isQueryFetched,
6494
- isPlaceholderData,
6495
- isLoading,
6496
- isFetching
6497
- } = useGetListData2(listDataProps, queryKey, enabled);
6498
- const {
6499
- columns: columnsGroup,
6500
- rows: rowsGroup,
6501
- typeTable: typeTableGroup
6502
- } = tableController({
6503
- data: {
6504
- fields: viewData?.views?.list?.fields,
6505
- records: dataResponse?.records ?? dataResponse?.groups,
6506
- dataModel: viewData?.models?.[model],
6507
- context: env.context,
6508
- typeTable: dataResponse?.groups ? "group" : "list"
6509
- }
6510
- });
6511
- const leftPadding = level > 1 ? level * 8 + "px" : "0px";
6512
- useEffect13(() => {
6513
- if (isShowGroup && selectedTags?.length > 0) {
6514
- setIsShowGroup(false);
6515
- }
6516
- }, [selectedTags]);
6517
- const group_by_field_name = groupByDomain?.contexts[level - 1]?.group_by;
6518
- const nameGroup = Array.isArray(row[group_by_field_name]) ? row?.string ?? row[`${group_by_field_name}`][1] : viewData?.models?.[model]?.[group_by_field_name]?.selection ? viewData.models[model][group_by_field_name].selection.find(
6519
- (selectItem) => selectItem?.[0] === row[group_by_field_name]
6520
- )?.[1] : row[group_by_field_name];
6521
- const nameGroupWithCount = `${typeof nameGroup === "string" ? nameGroup : typeof nameGroup === "boolean" && nameGroup ? i18n_default.t("yes") : i18n_default.t("no")} (${row[`${group_by_field_name?.split(":")?.[0]}_count`]})`;
6522
- const allIdsNull = selectedRowKeys?.every((item) => item === void 0);
6523
- const handleExpandChildGroup = () => {
6524
- if (isLoading || isFetching) return;
6525
- const toggleShowGroup = () => setIsShowGroup((prev) => !prev);
6526
- if (allIdsNull || typeTableGroup === "group") {
6527
- toggleShowGroup();
6528
- return;
6702
+ // src/widget/basic/float-field/controller.ts
6703
+ var floatController = ({
6704
+ onChange,
6705
+ value,
6706
+ props
6707
+ }) => {
6708
+ const { name, required, methods, onChange: handleOnchange, string } = props;
6709
+ const { setError, clearErrors } = methods;
6710
+ const [inputValue, setInputValue] = useState11(
6711
+ value !== void 0 && value !== null ? useFormatFloatNumber(value) : ""
6712
+ );
6713
+ useEffect12(() => {
6714
+ if (value !== void 0 && value !== null && value !== parseFloat(inputValue?.replace(/,/g, ""))) {
6715
+ setInputValue(useFormatFloatNumber(value));
6716
+ clearErrors(name);
6717
+ } else if (value === null || value === void 0) {
6718
+ setInputValue("");
6529
6719
  }
6530
- if (isShowGroup && checkedAll) {
6531
- const ids = rowsGroup?.map((item) => item?.id) || [];
6532
- const filteredIds = selectedRowKeys.filter(
6533
- (id) => !ids.includes(id)
6534
- );
6535
- appDispatch(setSelectedRowKeys3(filteredIds));
6536
- } else if (!isShowGroup && selectedRowKeys?.length > 0 && typeTableGroup === "list" && checkedAll && !allIdsNull && isQueryFetched) {
6537
- const clonedKeys = [...selectedRowKeys];
6538
- appDispatch(setSelectedRowKeys3([...clonedKeys, -1]));
6539
- setTimeout(() => appDispatch(setSelectedRowKeys3(clonedKeys)), 500);
6540
- } else if (isShowGroup && selectedRowKeys?.length > 0 && typeTableGroup === "list" && !checkedAll && !allIdsNull) {
6541
- const filteredKeys = selectedRowKeys.filter((id) => id > -1);
6542
- appDispatch(setSelectedRowKeys3(filteredKeys));
6720
+ }, [value, name, clearErrors]);
6721
+ const isDirtyRef = useRef4(false);
6722
+ const inputRef = useRef4(null);
6723
+ const lastCommittedValueRef = useRef4(null);
6724
+ const handleInputChange = (e) => {
6725
+ const newValue = e.target.value;
6726
+ const valueWithoutCommas = newValue.replace(/,/g, "");
6727
+ if (/^[0-9]*[.,]?[0-9]*$/.test(valueWithoutCommas) || newValue === "") {
6728
+ const parts = valueWithoutCommas.split(".");
6729
+ let integerPart = parts[0] || "";
6730
+ const decimalPart = parts[1] || "";
6731
+ if (decimalPart.length > 100) return;
6732
+ if (integerPart) {
6733
+ integerPart = Number(integerPart).toLocaleString("en-US");
6734
+ }
6735
+ const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
6736
+ setInputValue(formattedValue);
6737
+ const parsedValue = parseFloat(valueWithoutCommas.replace(",", "."));
6738
+ if (!isNaN(parsedValue)) {
6739
+ if (parsedValue < 0) {
6740
+ setError(name, {
6741
+ type: "validate",
6742
+ message: i18n_default.t("invalid_number")
6743
+ });
6744
+ } else {
6745
+ onChange(parsedValue);
6746
+ clearErrors(name);
6747
+ isDirtyRef.current = true;
6748
+ }
6749
+ } else {
6750
+ onChange(null);
6751
+ clearErrors(name);
6752
+ }
6543
6753
  }
6544
- toggleShowGroup();
6545
6754
  };
6546
- useEffect13(() => {
6547
- if (!isQueryFetched || !rowsGroup || !checkedAll || allIdsNull || typeTableGroup === "group") {
6755
+ const handleInputMouseLeave = () => {
6756
+ if (!isDirtyRef.current) {
6757
+ inputRef.current?.blur();
6548
6758
  return;
6549
6759
  }
6550
- const clonedKeys = [...selectedRowKeys];
6551
- setSelectedRowKeys3([...clonedKeys, -1]);
6552
- setTimeout(() => setSelectedRowKeys3(clonedKeys), 500);
6553
- }, [isQueryFetched]);
6554
- return {
6555
- handleExpandChildGroup,
6556
- colEmptyGroup,
6557
- leftPadding,
6558
- isShowGroup,
6559
- isQueryFetched,
6560
- nameGroupWithCount,
6561
- columns,
6562
- row,
6563
- isPlaceholderData,
6564
- columnsGroup,
6565
- indexRow,
6566
- rowsGroup,
6567
- model,
6568
- viewData,
6569
- renderField,
6570
- level,
6571
- specification,
6572
- context,
6573
- checkedAll,
6574
- isDisplayCheckbox,
6575
- isAutoSelect,
6576
- setIsAutoSelect,
6577
- selectedRowKeysRef,
6578
- initVal,
6579
- dataResponse,
6580
- pageGroup,
6581
- setPageGroup
6582
- };
6583
- };
6584
-
6585
- // src/widget/advance/search/controller.ts
6586
- var import_moment = __toESM(require_moment());
6587
- import { SearchType } from "@fctc/interface-logic/constants";
6588
- import {
6589
- domainHelper as domainHelper2,
6590
- evalJSONDomain as evalJSONDomain4,
6591
- validateAndParseDate
6592
- } from "@fctc/interface-logic/utils";
6593
- import { useCallback as useCallback3, useEffect as useEffect14, useState as useState10 } from "react";
6594
- var searchController = ({
6595
- viewData,
6596
- actionData,
6597
- fieldsList,
6598
- contextSearch,
6599
- setSearchMap,
6600
- searchMap
6601
- }) => {
6602
- const [filterBy, setFilterBy] = useState10(null);
6603
- const [searchBy, setSearchBy] = useState10(null);
6604
- const [groupBy, setGroupBy] = useState10(null);
6605
- const [selectedTags, setSelectedTags] = useState10(null);
6606
- const [searchString, setSearchString] = useState10("");
6607
- const aid = actionData?.id;
6608
- const model = actionData?.res_model;
6609
- const clearSearch = () => {
6610
- setFilterBy([]);
6611
- setGroupBy([]);
6612
- setSearchBy([]);
6613
- setSelectedTags(null);
6614
- setSearchString("");
6615
- setSearchMap({});
6616
- };
6617
- const fetchData = async () => {
6618
- if (viewData) {
6619
- try {
6620
- const dataModel = viewData?.models?.[model];
6621
- const searchViews = viewData?.views?.search;
6622
- const searchByItems = searchViews?.search_by?.filter(
6623
- (item) => !domainHelper2.matchDomains(contextSearch, item.invisible)
6624
- )?.map(
6625
- ({ string, name, filter_domain, operator, widget }, index) => ({
6626
- dataIndex: index,
6627
- title: string ?? dataModel[name]?.string,
6628
- name: name ?? dataModel[name]?.name,
6629
- filter_domain,
6630
- operator,
6631
- widget,
6632
- type: dataModel[name]?.type
6633
- })
6634
- );
6635
- const filterByItems = searchViews?.filter_by.filter((item) => {
6636
- return !domainHelper2.matchDomains(contextSearch, item?.invisible);
6637
- })?.map((item) => ({ ...item, active: false }));
6638
- const groupByItems = searchViews?.group_by.filter(
6639
- (item) => !domainHelper2.matchDomains(contextSearch, item?.invisible)
6640
- ).map((item) => ({
6641
- ...item,
6642
- string: item.string ?? viewData?.models?.[model]?.[item?.name?.split("group_by_")?.[1]]?.string
6643
- }));
6644
- setSearchBy(searchByItems);
6645
- setFilterBy(filterByItems);
6646
- setGroupBy(groupByItems);
6647
- } catch (error) {
6648
- console.error("Error fetching data:", error);
6760
+ const rawValue = inputValue.replace(/,/g, "");
6761
+ const parsedValue = parseFloat(rawValue);
6762
+ if (rawValue === "" || rawValue === ".") {
6763
+ if (required) {
6764
+ setError(name, {
6765
+ type: "required",
6766
+ message: `${string} ${i18n_default.t("must_required")}`
6767
+ });
6649
6768
  }
6650
- }
6651
- };
6652
- useEffect14(() => {
6653
- clearSearch();
6654
- fetchData();
6655
- }, [aid, model, viewData]);
6656
- const onChangeSearchInput = (search_string) => {
6657
- setSearchString(search_string);
6658
- };
6659
- const removeKeyFromSearchMap = ({
6660
- key,
6661
- item
6662
- }) => {
6663
- const values = searchMap[key];
6664
- if (!values) return searchMap;
6665
- const newSearchMap = { ...searchMap };
6666
- if (item) {
6667
- const filtered = values.filter((value) => value.name !== item.name);
6668
- if (filtered.length > 0) {
6669
- newSearchMap[key] = filtered;
6769
+ onChange(null);
6770
+ setInputValue("");
6771
+ lastCommittedValueRef.current = null;
6772
+ } else if (!isNaN(parsedValue)) {
6773
+ if (parsedValue < 0) {
6774
+ setError(name, {
6775
+ type: "validate",
6776
+ message: i18n_default.t("invalid_number")
6777
+ });
6778
+ setInputValue("");
6779
+ lastCommittedValueRef.current = null;
6670
6780
  } else {
6671
- delete newSearchMap[key];
6781
+ if (lastCommittedValueRef.current !== parsedValue) {
6782
+ const parts = rawValue.split(".");
6783
+ let integerPart = parts[0];
6784
+ const decimalPart = parts[1] || "";
6785
+ integerPart = Number(integerPart).toLocaleString("en-US");
6786
+ const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
6787
+ onChange(parsedValue);
6788
+ setInputValue(formattedValue);
6789
+ handleOnchange?.(name ?? "", parsedValue);
6790
+ clearErrors(name);
6791
+ lastCommittedValueRef.current = parsedValue;
6792
+ }
6672
6793
  }
6673
6794
  } else {
6674
- delete newSearchMap[key];
6795
+ setError(name, {
6796
+ type: "validate",
6797
+ message: i18n_default.t("invalid_number")
6798
+ });
6799
+ setInputValue("");
6800
+ lastCommittedValueRef.current = null;
6675
6801
  }
6676
- setSearchMap(newSearchMap);
6677
- };
6678
- const updateSearchMap = ({ key, item }) => {
6679
- const newSearchMap = { ...searchMap };
6680
- const currentValues = searchMap[key] ?? [];
6681
- newSearchMap[key] = [...currentValues, item];
6682
- setSearchMap(newSearchMap);
6683
- };
6684
- const removeSearchItems = (key, item) => {
6685
- removeKeyFromSearchMap({ key: String(key), item });
6802
+ isDirtyRef.current = false;
6803
+ inputRef.current?.blur();
6686
6804
  };
6687
- const addSearchItems = (key, newItem) => {
6688
- updateSearchMap({ key, item: newItem });
6805
+ return {
6806
+ handleInputMouseLeave,
6807
+ handleInputChange,
6808
+ useFormatFloatNumber,
6809
+ inputRef,
6810
+ inputValue
6689
6811
  };
6690
- const domainAction = actionData?.domain ? Array.isArray(actionData?.domain) ? [...actionData?.domain] : evalJSONDomain4(actionData?.domain, contextSearch) : [];
6691
- const formatDomain = () => {
6692
- if (domainAction) {
6693
- const domain = [];
6694
- if (domainAction?.length > 0) {
6695
- if (Object.keys(searchMap).length > 0) {
6696
- domain.push("&");
6697
- }
6698
- domainAction.forEach((domainItem) => {
6699
- domain.push(domainItem);
6700
- });
6701
- }
6702
- Object.keys(searchMap).forEach((key, keyIndex, keys) => {
6703
- if (!key?.includes(SearchType.GROUP)) {
6704
- if (keys.length > 1 && keyIndex < keys.length - 1) {
6705
- domain.push("&");
6706
- }
6707
- const valuesOfKey = searchMap[key];
6708
- valuesOfKey.forEach((value, index) => {
6709
- if (index < valuesOfKey.length - 1) {
6710
- domain.push("|");
6711
- }
6712
- if (value.domain) {
6713
- domain.push(...value.domain);
6714
- return;
6715
- }
6716
- let valueDomainItem = value?.value;
6717
- if (value?.modelType === "date") {
6718
- valueDomainItem = validateAndParseDate(value?.value);
6719
- } else if (value?.modelType === "datetime") {
6720
- if (value?.operator === "<=" || value?.operator === "<") {
6721
- const parsedDate = validateAndParseDate(value?.value, true);
6722
- const hasTime = (0, import_moment.default)(value?.value).format("HH:mm:ss") !== "00:00:00";
6723
- valueDomainItem = hasTime ? (0, import_moment.default)(parsedDate).format("YYYY-MM-DD HH:mm:ss") : (0, import_moment.default)(parsedDate).add(1, "day").subtract(1, "second").format("YYYY-MM-DD HH:mm:ss");
6724
- } else {
6725
- valueDomainItem = validateAndParseDate(value?.value, true);
6726
- }
6727
- }
6728
- const operator = value?.modelType === "date" || value?.modelType === "datetime" || value?.modelType === "boolean" || value?.modelType === "integer" ? value?.operator ?? "=" : value.operator ?? "ilike";
6729
- domain.push([value.name, operator, valueDomainItem]);
6730
- });
6731
- }
6732
- });
6733
- return [...domain];
6734
- }
6812
+ };
6813
+ var useFormatFloatNumber = (value) => {
6814
+ if (value === void 0 || value === null || value === "") return "";
6815
+ const numValue = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : value;
6816
+ if (isNaN(numValue)) return "";
6817
+ return numValue.toLocaleString("en-US", {
6818
+ minimumFractionDigits: numValue % 1 === 0 ? 0 : 1,
6819
+ maximumFractionDigits: 20
6820
+ });
6821
+ };
6822
+
6823
+ // src/widget/basic/download-file-field/controller.ts
6824
+ import { useId, useState as useState12 } from "react";
6825
+ var downloadFileController = () => {
6826
+ const inputId = useId();
6827
+ const [file, setFile] = useState12(null);
6828
+ const handleFileChange = (e) => {
6829
+ setFile(e.target.files[0]);
6735
6830
  };
6736
- const setTagSearch = useCallback3(
6737
- (updatedMap) => {
6738
- if (!updatedMap) return;
6739
- const tagsSearch = Object.entries(updatedMap).map(
6740
- ([key, objValues]) => {
6741
- const {
6742
- title,
6743
- name,
6744
- groupIndex,
6745
- type,
6746
- widget,
6747
- modelType,
6748
- dataIndex
6749
- } = objValues[0];
6750
- if (!key?.includes(SearchType.GROUP)) {
6751
- const values = objValues?.map((objValue) => objValue.value);
6752
- return {
6753
- title,
6754
- name: type === SearchType.SEARCH ? `${SearchType.SEARCH}_${String(dataIndex)}` : groupIndex ?? name,
6755
- values,
6756
- type,
6757
- widget,
6758
- modelType
6759
- };
6760
- } else {
6761
- const contexts = [];
6762
- let groupValues = [];
6763
- objValues?.forEach((objValue) => {
6764
- const { context, value, active, groupIndex: groupIndex2, isDefault } = objValue;
6765
- const indexAppend = groupIndex2 != null ? groupIndex2 : viewData?.views?.search?.filters_by?.length ?? 0;
6766
- contexts.push(
6767
- ...Array.isArray(context?.group_by) ? context.group_by.map((item) => ({ group_by: item })) : [context]
6768
- );
6769
- groupValues[indexAppend] = {
6770
- contexts: [
6771
- ...Array.isArray(context?.group_by) ? context.group_by.map((item) => ({
6772
- group_by: item
6773
- })) : [context]
6774
- ],
6775
- strings: isDefault ? [value] : [...groupValues[indexAppend]?.strings ?? [], value]
6776
- };
6777
- });
6778
- const fields = [
6779
- ...new Set(fieldsList?.map((item) => item?.name))
6780
- ];
6781
- const groupByTag = {
6782
- title,
6783
- values: groupValues?.filter(
6784
- (item) => item !== void 0
6785
- ),
6786
- type,
6787
- contexts,
6788
- fields
6789
- };
6790
- return groupByTag;
6791
- }
6792
- }
6793
- );
6794
- setSelectedTags(tagsSearch);
6795
- setSearchString("");
6796
- },
6797
- [searchMap]
6798
- );
6799
- useEffect14(() => {
6800
- setSelectedTags(null);
6801
- setTagSearch(searchMap);
6802
- }, [searchMap]);
6803
- const handleAddTagSearch = (tag) => {
6804
- const {
6805
- domain,
6806
- groupIndex,
6807
- value,
6808
- type,
6809
- title,
6810
- context,
6811
- active,
6812
- dataIndex
6813
- } = tag;
6814
- const domainFormat = new domainHelper2.Domain(domain);
6815
- if (type === SearchType.FILTER) {
6816
- addSearchItems(`${SearchType.FILTER}_${groupIndex}`, {
6817
- ...tag,
6818
- domain: domain ? domainFormat.toList(context) : null
6819
- });
6820
- } else if (type === SearchType.SEARCH) {
6821
- addSearchItems(`${SearchType.SEARCH}_${String(dataIndex)}`, {
6822
- ...tag,
6823
- domain: domain ? domainFormat.toList({
6824
- ...context,
6825
- self: value
6826
- }) : null
6827
- });
6828
- } else if (type === SearchType.GROUP) {
6829
- addSearchItems(`${SearchType.GROUP}`, {
6830
- ...tag,
6831
- domain: domain ? domainFormat.toList({
6832
- context,
6833
- self: value
6834
- }) : null
6835
- });
6831
+ const handleFileDownload = () => {
6832
+ const url = URL.createObjectURL(file);
6833
+ const link = document.createElement("a");
6834
+ link.href = url;
6835
+ link.download = file.name;
6836
+ document.body.appendChild(link);
6837
+ link.click();
6838
+ document.body.removeChild(link);
6839
+ };
6840
+ return {
6841
+ inputId,
6842
+ file,
6843
+ handleFileChange,
6844
+ handleFileDownload
6845
+ };
6846
+ };
6847
+
6848
+ // src/widget/basic/download-binary-field/controller.ts
6849
+ var downLoadBinaryController = (props) => {
6850
+ const { value, defaultValue, formValues } = props;
6851
+ const handleFileDownload = async (e) => {
6852
+ e.stopPropagation();
6853
+ await downloadFile(value || defaultValue, formValues?.name);
6854
+ };
6855
+ const downloadFile = async (url, filename) => {
6856
+ try {
6857
+ const response = await fetch(url);
6858
+ if (response) {
6859
+ const blob = await response.blob();
6860
+ const urlBlob = window.URL.createObjectURL(blob);
6861
+ const link = document.createElement("a");
6862
+ link.href = urlBlob;
6863
+ link.download = filename || "downloaded-file";
6864
+ document.body.appendChild(link);
6865
+ link.click();
6866
+ document.body.removeChild(link);
6867
+ window.URL.revokeObjectURL(urlBlob);
6868
+ }
6869
+ } catch (error) {
6870
+ console.error("File download failed:", error);
6836
6871
  }
6837
6872
  };
6838
6873
  return {
6839
- groupBy,
6840
- searchBy,
6841
- filterBy,
6842
- selectedTags,
6843
- searchString,
6844
- setFilterBy,
6845
- setGroupBy,
6846
- setSearchBy,
6847
- clearSearch,
6848
- setSelectedTags,
6849
- removeSearchItems,
6850
- onSearchString: onChangeSearchInput,
6851
- handleAddTagSearch,
6852
- domain: formatDomain()
6874
+ handleFileDownload
6853
6875
  };
6854
6876
  };
6855
6877
 
6856
- // src/widget/basic/many2many-field/controller.ts
6857
- import { getEnv as getEnv8 } from "@fctc/interface-logic/environment";
6858
- import {
6859
- useAppDispatch as useAppDispatch8,
6860
- useAppSelector as useAppSelector7,
6861
- selectSearch as selectSearch6,
6862
- setFirstDomain,
6863
- setViewDataStore,
6864
- setPage,
6865
- setGroupByDomain
6866
- } from "@fctc/interface-logic/store";
6867
- import {
6868
- evalJSONContext as evalJSONContext4,
6869
- formatSortingString as formatSortingString2
6870
- } from "@fctc/interface-logic/utils";
6871
- var many2manyFieldController = (props) => {
6878
+ // src/widget/basic/date-field/controller.ts
6879
+ var import_moment = __toESM(require_moment());
6880
+ var DURATIONS = {
6881
+ PAST: "past",
6882
+ NOW: "now",
6883
+ FUTURE: "future"
6884
+ };
6885
+ var dateFieldController = (props) => {
6872
6886
  const {
6873
- relation,
6874
- domain,
6875
- context,
6876
- tab,
6877
- model,
6878
- aid,
6879
- setSelectedRowKeys: setSelectedRowKeys4,
6880
- fields,
6881
- setFields,
6882
- groupByDomain,
6883
- page,
6884
- options,
6885
- sessionStorageUtils
6887
+ string,
6888
+ showTime = false,
6889
+ widget,
6890
+ min,
6891
+ max,
6892
+ viewData,
6893
+ formValues,
6894
+ model
6886
6895
  } = props;
6887
- const appDispatch = useAppDispatch8();
6888
- const actionData = sessionStorageUtils.getActionData();
6889
- const [debouncedPage] = useDebounce(page, 500);
6890
- const [order, setOrder] = useState11();
6891
- const [isLoadedData, setIsLoadedData] = useState11(false);
6892
- const [domainMany2Many, setDomainMany2Many] = useState11(domain);
6893
- const env = getEnv8();
6894
- const { selectedTags } = useAppSelector7(selectSearch6);
6895
- const viewParams = {
6896
- model: relation,
6897
- views: [
6898
- [false, "list"],
6899
- [false, "search"]
6900
- ],
6901
- context
6896
+ const range = (start, end, step = 1) => {
6897
+ const arr = [];
6898
+ for (let i = start; i < end; i += step) {
6899
+ arr.push(i);
6900
+ }
6901
+ return arr;
6902
6902
  };
6903
- const { data: viewResponse, isFetched: isViewReponseFetched } = (0, hooks_exports.useGetView)(
6904
- viewParams,
6905
- actionData
6906
- );
6907
- const baseModel = useMemo13(
6908
- () => ({
6909
- name: String(relation),
6910
- view: viewResponse || {},
6911
- actContext: context,
6912
- fields: [
6913
- ...Object.values(viewResponse?.views?.list?.fields ?? {}),
6914
- ...tab?.fields ? tab.fields : []
6915
- ]
6916
- }),
6917
- [model, viewResponse]
6903
+ const formatDate = showTime ? "DD/MM/YYYY HH:mm:ss" : "DD/MM/YYYY";
6904
+ const formatDateParse = showTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
6905
+ const fieldForCustom = widget === "datetime_custom" || widget === "date_custom";
6906
+ const minNowValue = fieldForCustom && (min === DURATIONS.NOW ? true : typeof min === "string" && Object.keys(formValues)?.includes(min) && formValues?.[min] ? (0, import_moment.default)(formValues?.[min], formatDateParse).add(7, "hours") : null);
6907
+ const maxNowValue = fieldForCustom && (max === DURATIONS.NOW ? true : typeof max === "string" && Object.keys(formValues)?.includes(max) && formValues?.[max] ? (0, import_moment.default)(formValues?.[max], formatDateParse).add(7, "hours") : null);
6908
+ const years = range(
6909
+ minNowValue ? (/* @__PURE__ */ new Date()).getFullYear() : 1990,
6910
+ (/* @__PURE__ */ new Date()).getFullYear() + 4,
6911
+ 1
6918
6912
  );
6919
- const initModel = (0, hooks_exports.useModel)();
6920
- const modelInstance = useMemo13(() => {
6921
- if (viewResponse) {
6922
- return initModel.initModel(baseModel);
6923
- }
6924
- return null;
6925
- }, [baseModel, viewResponse]);
6926
- const specification = useMemo13(() => {
6927
- if (modelInstance) {
6928
- return modelInstance.getSpecification();
6929
- }
6930
- return null;
6931
- }, [modelInstance]);
6932
- const default_order = viewResponse && viewResponse?.views?.list?.default_order;
6933
- const optionsObject = tab?.options ? evalJSONContext4(tab?.options) : (options ? evalJSONContext4(options) : {}) || {};
6934
- const fetchData = async () => {
6935
- try {
6936
- setDomainMany2Many(domain);
6937
- appDispatch(setFirstDomain(domain));
6938
- appDispatch(setViewDataStore(viewResponse));
6939
- const modalData = viewResponse?.views?.list?.fields.map((field) => ({
6940
- ...viewResponse?.models?.[String(model)]?.[field?.name],
6941
- ...field
6942
- }));
6943
- if (!fields?.[`${aid}_${relation}_popupmany2many`] && modalData) {
6944
- setFields({
6945
- ...fields,
6946
- [`${aid}_${relation}_popupmany2many`]: modalData
6947
- });
6913
+ const months_vi = [
6914
+ "Th\xE1ng 1",
6915
+ "Th\xE1ng 2",
6916
+ "Th\xE1ng 3",
6917
+ "Th\xE1ng 4",
6918
+ "Th\xE1ng 5",
6919
+ "Th\xE1ng 6",
6920
+ "Th\xE1ng 7",
6921
+ "Th\xE1ng 8",
6922
+ "Th\xE1ng 9",
6923
+ "Th\xE1ng 10",
6924
+ "Th\xE1ng 11",
6925
+ "Th\xE1ng 12"
6926
+ ];
6927
+ const months_en = [
6928
+ "January",
6929
+ "February",
6930
+ "March",
6931
+ "April",
6932
+ "May",
6933
+ "June",
6934
+ "July",
6935
+ "August",
6936
+ "September",
6937
+ "October",
6938
+ "November",
6939
+ "December"
6940
+ ];
6941
+ const customValidateMinMax = (date) => {
6942
+ const selected = (0, import_moment.default)(date, formatDateParse);
6943
+ const now = (0, import_moment.default)();
6944
+ const compareSelected = showTime ? selected : selected.clone().startOf("day");
6945
+ const compareNow = showTime ? now : now.clone().startOf("day");
6946
+ if (minNowValue) {
6947
+ if (compareSelected.isBefore(compareNow) && typeof minNowValue === "boolean" && minNowValue === true) {
6948
+ return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
6949
+ "greater_or_equal_now"
6950
+ )}`;
6951
+ } else if (import_moment.default.isMoment(minNowValue)) {
6952
+ const compareMin = showTime ? minNowValue : minNowValue.clone().startOf("day");
6953
+ if (compareSelected.isBefore(compareMin)) {
6954
+ const fieldRelationDate = viewData?.models?.[model]?.[min ?? ""];
6955
+ return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
6956
+ "greater_or_equal"
6957
+ )} ${fieldRelationDate?.string}`;
6958
+ }
6959
+ }
6960
+ } else if (maxNowValue) {
6961
+ if (compareSelected.isAfter(compareNow) && typeof maxNowValue === "boolean" && maxNowValue === true) {
6962
+ return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
6963
+ "less_or_equal_now"
6964
+ )}`;
6965
+ } else if (import_moment.default.isMoment(maxNowValue)) {
6966
+ const compareMax = showTime ? maxNowValue : maxNowValue.clone().startOf("day");
6967
+ if (compareSelected.isAfter(compareMax)) {
6968
+ const fieldRelationDate = viewData?.models?.[model]?.[max ?? ""];
6969
+ return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
6970
+ "less_or_equal"
6971
+ )} ${fieldRelationDate?.string}`;
6972
+ }
6948
6973
  }
6949
- appDispatch(setPage(0));
6950
- } catch (err) {
6951
- console.log(err);
6952
6974
  }
6975
+ return false;
6953
6976
  };
6954
- const queryKey = [
6955
- `view-${relation}-${aid}`,
6956
- specification,
6957
- domainMany2Many,
6958
- debouncedPage,
6959
- groupByDomain,
6960
- order
6961
- ];
6962
- const data = {
6963
- model: relation,
6964
- specification,
6965
- domain: domainMany2Many,
6966
- offset: debouncedPage * 10,
6967
- limit: 10,
6968
- context,
6969
- fields: groupByDomain?.fields,
6970
- groupby: [groupByDomain?.contexts[0]?.group_by],
6971
- sort: order ? order : default_order ? formatSortingString2(default_order) : ""
6977
+ return {
6978
+ formatDate,
6979
+ formatDateParse,
6980
+ range,
6981
+ years,
6982
+ months_vi,
6983
+ months_en,
6984
+ customValidateMinMax,
6985
+ minNowValue,
6986
+ maxNowValue
6972
6987
  };
6973
- const enabled = isLoadedData && !!specification && !!relation && !!domainMany2Many && !!viewResponse;
6974
- const {
6975
- data: dataResponse,
6976
- isLoading: isDataLoading,
6977
- isFetched: isDataResponseFetched,
6978
- isPlaceholderData
6979
- } = (0, hooks_exports.useGetListData)(data, queryKey, enabled);
6980
- useEffect15(() => {
6981
- if (viewResponse) {
6982
- fetchData();
6983
- }
6984
- return () => {
6985
- appDispatch(setGroupByDomain(null));
6986
- setFields((prevFields) => ({
6987
- ...prevFields,
6988
- [`${aid}_${relation}_popupmany2many`]: null
6989
- }));
6990
- appDispatch(setPage(0));
6991
- setSelectedRowKeys4([]);
6992
- setDomainMany2Many(null);
6993
- setIsLoadedData(false);
6994
- };
6995
- }, [viewResponse]);
6996
- const { rows, columns, typeTable } = tableController({
6997
- data: {
6998
- fields: fields?.[`${aid}_${relation}_popupmany2many`] || viewResponse?.views?.list?.fields,
6999
- records: dataResponse?.records ?? dataResponse?.groups,
7000
- dataModel: viewResponse?.models?.[String(relation)],
7001
- context: { ...env.context, ...context },
7002
- typeTable: dataResponse?.groups ? "group" : "list"
7003
- }
7004
- });
7005
- const dataFormView = {
7006
- id: null,
7007
- model: relation,
7008
- context
6988
+ };
6989
+
6990
+ // src/widget/basic/copy-link-button/controller.ts
6991
+ import { useState as useState13 } from "react";
6992
+ import { copyTextToClipboard } from "@fctc/interface-logic/utils";
6993
+ var copyLinkButtonController = (props) => {
6994
+ const { value, defaultValue } = props;
6995
+ const [isCopied, setIsCopied] = useState13(false);
6996
+ const handleCopyToClipboard = async (value2) => {
6997
+ await copyTextToClipboard(value2);
6998
+ setIsCopied(true);
6999
+ setTimeout(() => setIsCopied(false), 2e3);
7009
7000
  };
7010
- const {
7011
- refetch,
7012
- data: dataFormViewResponse,
7013
- isSuccess
7014
- } = (0, hooks_exports.useGetFormView)({
7015
- data: dataFormView,
7016
- queryKey: [`form-view-action-${relation}`],
7017
- enabled: false
7018
- });
7019
- useEffect15(() => {
7020
- if (isSuccess && dataFormViewResponse) {
7021
- sessionStorage.setItem("actionData", JSON.stringify(dataFormViewResponse));
7022
- window.location.href = `/form/menu?model=${relation}`;
7023
- }
7024
- }, [isSuccess]);
7025
- useEffect15(() => {
7026
- if (domainMany2Many && !isLoadedData) {
7027
- setIsLoadedData(true);
7028
- }
7029
- }, [domainMany2Many]);
7030
- const handleCreateNewOnPage = async () => {
7001
+ const propValue = value || defaultValue;
7002
+ return {
7003
+ isCopied,
7004
+ handleCopyToClipboard,
7005
+ propValue
7006
+ };
7007
+ };
7008
+
7009
+ // src/widget/basic/color-field/color-controller.ts
7010
+ import { getEnv as getEnv8 } from "@fctc/interface-logic/environment";
7011
+ import { useSave as useSave3 } from "@fctc/interface-logic/hooks";
7012
+ import { evalJSONContext as evalJSONContext7 } from "@fctc/interface-logic/utils";
7013
+ var colorFieldController = (props) => {
7014
+ const { value, isForm, name, formValues, idForm, model, actionData } = props;
7015
+ const env = getEnv8();
7016
+ const _context = { ...evalJSONContext7(actionData?.context) || {} };
7017
+ const contextObject = { ...env.context, ..._context };
7018
+ const idDefault = isForm ? idForm : formValues?.id;
7019
+ const { mutate: onSave } = useSave3();
7020
+ const savePickColor = async (colorObject) => {
7021
+ const { id } = colorObject;
7022
+ if (value === id) return;
7031
7023
  try {
7032
- refetch();
7024
+ onSave({
7025
+ ids: idDefault !== null ? [idDefault] : [],
7026
+ model: model ?? "",
7027
+ data: { [name ?? ""]: id },
7028
+ specification: {
7029
+ name: {},
7030
+ color: {}
7031
+ },
7032
+ context: contextObject
7033
+ });
7033
7034
  } catch (error) {
7034
7035
  console.log(error);
7035
7036
  }
7036
7037
  };
7037
- return {};
7038
+ return {
7039
+ savePickColor
7040
+ };
7038
7041
  };
7039
7042
 
7040
- // src/widget/basic/many2many-tags-field/controller.ts
7041
- import { useMemo as useMemo14 } from "react";
7042
- import { WIDGETAVATAR, WIDGETCOLOR } from "@fctc/interface-logic/constants";
7043
- import { getEnv as getEnv9 } from "@fctc/interface-logic/environment";
7044
- import { useGetSelection as useGetSelection3 } from "@fctc/interface-logic/hooks";
7045
- import { evalJSONContext as evalJSONContext5, evalJSONDomain as evalJSONDomain5 } from "@fctc/interface-logic/utils";
7046
- var many2manyTagsController = (props) => {
7047
- const {
7048
- relation,
7049
- domain,
7050
- options: optionsFields,
7051
- widget,
7052
- formValues,
7053
- placeholderNoOption
7054
- } = props;
7055
- const isUser = relation === "res.users" || relation === "res.partner";
7056
- const env = getEnv9();
7057
- const addtionalFields = optionsFields ? evalJSONContext5(optionsFields) : null;
7058
- const domainObject = useMemo14(
7059
- () => evalJSONDomain5(domain, JSON.parse(JSON.stringify(formValues || {}))),
7060
- [domain, formValues]
7061
- );
7062
- const data = {
7063
- model: relation ?? "",
7064
- domain: domainObject,
7065
- specification: {
7066
- id: {},
7067
- name: {},
7068
- display_name: {},
7069
- ...widget && WIDGETAVATAR[widget] ? { image_256: {} } : {},
7070
- ...widget && WIDGETCOLOR[widget] && addtionalFields?.color_field ? { color: {} } : {}
7071
- },
7072
- enabled: true,
7073
- context: env.context
7043
+ // src/widget/basic/binary-field/controller.ts
7044
+ import { useEffect as useEffect13, useId as useId2, useRef as useRef5, useState as useState14 } from "react";
7045
+ import { isBase64Image } from "@fctc/interface-logic/utils";
7046
+ var binaryFieldController = (props) => {
7047
+ const { name, methods, readonly = false, value } = props;
7048
+ const inputId = useId2();
7049
+ const [selectedImage, setSelectedImage] = useState14(null);
7050
+ const [initialImage, setInitialImage] = useState14(value || null);
7051
+ const [isInsideTable, setIsInsideTable] = useState14(false);
7052
+ const { setValue } = methods;
7053
+ const binaryRef = useRef5(null);
7054
+ const convertUrlToBase64 = async (url) => {
7055
+ try {
7056
+ const response = await fetch(url);
7057
+ const blob = await response.blob();
7058
+ return new Promise((resolve, reject) => {
7059
+ const reader = new FileReader();
7060
+ reader.onloadend = () => {
7061
+ resolve(reader.result);
7062
+ };
7063
+ reader.onerror = reject;
7064
+ reader.readAsDataURL(blob);
7065
+ });
7066
+ } catch (error) {
7067
+ console.error("Error converting URL to Base64:", error);
7068
+ throw error;
7069
+ }
7074
7070
  };
7075
- const { data: dataOfSelection } = useGetSelection3({
7076
- data,
7077
- queryKey: [`data_${relation}`, domainObject]
7078
- });
7079
- const customNoOptionsMessage = () => placeholderNoOption;
7080
- const tranfer = (data2) => {
7081
- return data2?.map((val) => ({
7082
- id: val.value,
7083
- display_name: val.label
7084
- })) || [];
7071
+ const extractBase64Data = (base64Url) => {
7072
+ if (base64Url.includes("base64,")) {
7073
+ return base64Url.split("base64,")[1];
7074
+ }
7075
+ return base64Url;
7085
7076
  };
7086
- const options = dataOfSelection?.records?.map((val) => ({
7087
- value: val.id,
7088
- label: val.name ?? val.display_name,
7089
- ...val
7090
- })) || [];
7077
+ const handleImageChange = async (e, onChange) => {
7078
+ if (readonly) return;
7079
+ const file = e?.target?.files?.[0];
7080
+ if (file) {
7081
+ const imageUrl = URL.createObjectURL(file);
7082
+ setSelectedImage(imageUrl);
7083
+ setInitialImage(null);
7084
+ onChange(file);
7085
+ const compressedBase64 = await convertUrlToBase64(imageUrl);
7086
+ const base64Data = extractBase64Data(compressedBase64);
7087
+ setValue(name, base64Data, {
7088
+ shouldDirty: true
7089
+ });
7090
+ }
7091
+ };
7092
+ const handleRemoveImage = (onChange) => {
7093
+ setSelectedImage(null);
7094
+ setInitialImage(null);
7095
+ onChange(null);
7096
+ };
7097
+ const isBlobUrl = (url) => {
7098
+ return /^blob:/.test(url);
7099
+ };
7100
+ const checkIsImageLink = (url) => {
7101
+ const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
7102
+ return imageExtensions.test(url) || isBase64Image(url) || isBlobUrl(url);
7103
+ };
7104
+ const getImageBase64WithMimeType = (base64) => {
7105
+ if (typeof base64 !== "string" || base64.length < 10) return null;
7106
+ if (isBase64Image(base64)) return base64;
7107
+ let mimeType = null;
7108
+ if (base64.startsWith("iVBORw0KGgo")) mimeType = "image/png";
7109
+ else if (base64.startsWith("/9j/")) mimeType = "image/jpeg";
7110
+ else if (base64.startsWith("R0lGOD")) mimeType = "image/gif";
7111
+ else if (base64.startsWith("Qk")) mimeType = "image/bmp";
7112
+ else if (base64.startsWith("UklGR")) mimeType = "image/webp";
7113
+ return mimeType ? `data:${mimeType};base64,${base64}` : null;
7114
+ };
7115
+ useEffect13(() => {
7116
+ return () => {
7117
+ if (selectedImage) {
7118
+ URL.revokeObjectURL(selectedImage);
7119
+ }
7120
+ };
7121
+ }, [selectedImage]);
7122
+ useEffect13(() => {
7123
+ if (binaryRef.current) {
7124
+ const isInsideTable2 = !!binaryRef.current.closest("table");
7125
+ setIsInsideTable(isInsideTable2);
7126
+ }
7127
+ }, []);
7091
7128
  return {
7092
- options,
7093
- customNoOptionsMessage,
7094
- tranfer,
7095
- dataOfSelection,
7096
- isUser
7129
+ inputId,
7130
+ selectedImage,
7131
+ initialImage,
7132
+ isInsideTable,
7133
+ binaryRef,
7134
+ handleImageChange,
7135
+ handleRemoveImage,
7136
+ checkIsImageLink,
7137
+ getImageBase64WithMimeType
7097
7138
  };
7098
7139
  };
7099
7140
 
7100
- // src/widget/basic/status-bar-field/controller.ts
7101
- import { useState as useState12 } from "react";
7102
- import { useGetListData as useGetListData4, useChangeStatus } from "@fctc/interface-logic/hooks";
7103
- import { useAppSelector as useAppSelector8, selectEnv as selectEnv2 } from "@fctc/interface-logic/store";
7104
- import { evalJSONDomain as evalJSONDomain6 } from "@fctc/interface-logic/utils";
7105
- var durationController = (props) => {
7141
+ // src/widget/advance/table/table-body/controller.ts
7142
+ import { useAppDispatch as useAppDispatch6, setSelectedRowKeys } from "@fctc/interface-logic/store";
7143
+ import { useEffect as useEffect14, useMemo as useMemo12 } from "react";
7144
+ var tableBodyController = (props) => {
7106
7145
  const {
7107
- relation,
7108
- defaultValue,
7109
- domain,
7110
- formValues,
7111
- name,
7112
- id,
7113
- model,
7114
- onRefetch
7146
+ checkedAll,
7147
+ checkboxRef,
7148
+ setIsAutoSelect,
7149
+ selectedRowKeys,
7150
+ row,
7151
+ isAutoSelect,
7152
+ selectedRowKeysRef,
7153
+ onClickRow
7115
7154
  } = props;
7116
- const specification = {
7117
- id: 0,
7118
- name: "",
7119
- fold: ""
7155
+ const appDispatch = useAppDispatch6();
7156
+ const checked = useMemo12(() => {
7157
+ if (!row?.id) return false;
7158
+ if (selectedRowKeys?.includes(row.id)) {
7159
+ return true;
7160
+ }
7161
+ return checkedAll;
7162
+ }, [row?.id, selectedRowKeys, checkedAll]);
7163
+ const handleCheckBoxSingle = (event) => {
7164
+ event.stopPropagation();
7165
+ if (checkedAll) {
7166
+ checkboxRef.current = "uncheck";
7167
+ setIsAutoSelect(true);
7168
+ return;
7169
+ }
7170
+ const newSelectedRowKeys = selectedRowKeys?.includes(row.id) ? selectedRowKeys?.filter((key) => key !== row.id) : [...selectedRowKeys, row.id];
7171
+ console.log("newSelectedRowKeys", newSelectedRowKeys);
7172
+ appDispatch(setSelectedRowKeys(newSelectedRowKeys));
7120
7173
  };
7121
- const [disabled, setDisabled] = useState12(false);
7122
- const [modelStatus, setModalStatus] = useState12(false);
7123
- const { context } = useAppSelector8(selectEnv2);
7124
- const queryKey = [`data-status-duration`, specification];
7125
- const listDataProps = {
7126
- model: relation,
7127
- specification,
7128
- domain: evalJSONDomain6(domain, JSON.parse(JSON.stringify(formValues))),
7129
- limit: 10,
7130
- offset: 0,
7131
- fields: "",
7132
- groupby: [],
7133
- context: {
7134
- lang: context.lang
7135
- },
7136
- sort: ""
7174
+ const handleClickRow = (col, row2) => {
7175
+ onClickRow(col, row2);
7137
7176
  };
7138
- const { data: dataResponse } = useGetListData4(listDataProps, queryKey);
7139
- const { mutate: fetchChangeStatus } = useChangeStatus();
7140
- const handleClick = async (stage_id) => {
7141
- setDisabled(true);
7142
- if (stage_id) {
7143
- fetchChangeStatus(
7144
- {
7145
- data: {
7146
- stage_id,
7147
- name,
7148
- id,
7149
- model,
7150
- lang: context.lang
7151
- }
7152
- },
7153
- {
7154
- onSuccess: (res) => {
7155
- if (res) {
7156
- setDisabled(false);
7157
- onRefetch && onRefetch();
7158
- }
7159
- }
7160
- }
7161
- );
7177
+ useEffect14(() => {
7178
+ if (!row?.id) return;
7179
+ if (isAutoSelect) {
7180
+ if (checkboxRef?.current === "uncheck") {
7181
+ const filtered = selectedRowKeysRef.current.filter(
7182
+ (id) => id !== row.id
7183
+ );
7184
+ selectedRowKeysRef.current = filtered;
7185
+ appDispatch(setSelectedRowKeys(filtered));
7186
+ } else {
7187
+ const unique = Array.from(
7188
+ /* @__PURE__ */ new Set([...selectedRowKeysRef?.current, row?.id])
7189
+ );
7190
+ selectedRowKeysRef.current = unique;
7191
+ appDispatch(setSelectedRowKeys(unique));
7192
+ }
7162
7193
  }
7163
- };
7194
+ }, [isAutoSelect]);
7195
+ useEffect14(() => {
7196
+ if (!checkedAll) {
7197
+ checkboxRef.current = "enabled";
7198
+ false;
7199
+ }
7200
+ }, [checkedAll]);
7164
7201
  return {
7165
- defaultValue,
7166
- dataResponse,
7167
- handleClick,
7168
- disabled,
7169
- modelStatus,
7170
- setModalStatus
7202
+ handleCheckBoxSingle,
7203
+ checked,
7204
+ handleClickRow
7171
7205
  };
7172
7206
  };
7173
7207
 
7174
- // src/widget/basic/priority-field/controller.ts
7175
- import { useSave as useSave2 } from "@fctc/interface-logic/hooks";
7176
- import { evalJSONContext as evalJSONContext6 } from "@fctc/interface-logic/utils";
7177
- var priorityFieldController = (props) => {
7178
- const {
7179
- value,
7180
- isForm,
7181
- name,
7182
- methods,
7183
- onChange,
7184
- model,
7185
- selection,
7186
- id,
7187
- actionData,
7188
- viewData,
7189
- context
7190
- } = props;
7191
- const _context = { ...evalJSONContext6(actionData?.context) };
7192
- const contextObject = { ...context, ..._context };
7193
- const defaultPriority = parseInt(value) + 1;
7194
- const label = viewData?.models?.[model]?.[name ?? ""]?.string ?? name;
7195
- const { mutateAsync: fetchSave } = useSave2();
7196
- const savePriorities = async ({
7197
- value: value2,
7198
- resetPriority
7199
- }) => {
7200
- const priorityValue = value2 <= 0 ? 0 : value2 - 1;
7201
- try {
7202
- fetchSave({
7203
- ids: id ? [id] : [],
7204
- data: { [name ?? ""]: String(priorityValue) },
7205
- model: model ?? "",
7206
- context: contextObject
7207
- });
7208
- if (typeof onChange === "function") {
7209
- onChange(name ?? "", String(priorityValue));
7208
+ // src/widget/advance/table/table-head/controller.ts
7209
+ import {
7210
+ useAppDispatch as useAppDispatch7,
7211
+ useAppSelector as useAppSelector5,
7212
+ selectSearch as selectSearch3,
7213
+ setSelectedRowKeys as setSelectedRowKeys2
7214
+ } from "@fctc/interface-logic/store";
7215
+ var tableHeadController = (props) => {
7216
+ const { typeTable, rows, selectedRowKeysRef } = props;
7217
+ const appDispatch = useAppDispatch7();
7218
+ const { groupByDomain } = useAppSelector5(selectSearch3);
7219
+ const handleCheckBoxAll = (event) => {
7220
+ if (event?.target?.checked && typeTable === "list") {
7221
+ const allRowKeys = Array.isArray(rows) ? rows.map((record) => record?.id) : [];
7222
+ appDispatch(setSelectedRowKeys2(allRowKeys));
7223
+ } else if (event?.target?.checked && typeTable === "group") {
7224
+ const rowsIDs = document.querySelectorAll("tr[data-row-id]");
7225
+ const ids = Array.from(rowsIDs)?.map(
7226
+ (row) => Number(row?.getAttribute("data-row-id"))
7227
+ );
7228
+ if (ids?.length > 0) {
7229
+ appDispatch(setSelectedRowKeys2(ids));
7230
+ } else {
7231
+ const sum = countSum(
7232
+ rows,
7233
+ typeof groupByDomain === "object" ? groupByDomain?.contexts?.[0]?.group_by : void 0
7234
+ );
7235
+ const keys = Array.from({ length: sum }, (_) => void 0);
7236
+ appDispatch(setSelectedRowKeys2(keys));
7210
7237
  }
7211
- } catch (error) {
7212
- if (resetPriority) {
7213
- resetPriority();
7238
+ if (selectedRowKeysRef) {
7239
+ selectedRowKeysRef.current = [];
7214
7240
  }
7241
+ } else {
7242
+ appDispatch(setSelectedRowKeys2([]));
7215
7243
  }
7216
7244
  };
7217
7245
  return {
7218
- selection,
7219
- isForm,
7220
- methods,
7221
- defaultPriority,
7222
- savePriorities,
7223
- label,
7224
- id,
7225
- onChange
7246
+ handleCheckBoxAll
7226
7247
  };
7227
7248
  };
7228
7249
 
7229
- // src/widget/basic/float-time-field/controller.ts
7230
- import { useState as useState13 } from "react";
7250
+ // src/widget/advance/table/table-view/controller.ts
7251
+ import { useEffect as useEffect15, useMemo as useMemo13, useRef as useRef6, useState as useState15 } from "react";
7231
7252
  import {
7232
- convertFloatToTime,
7233
- convertTimeToFloat
7234
- } from "@fctc/interface-logic/utils";
7235
- var floatTimeFiledController = ({
7236
- onChange: fieldOnChange,
7237
- onBlur,
7238
- value,
7239
- isDirty,
7240
- props
7241
- }) => {
7242
- const { name, defaultValue = 0, onChange } = props;
7243
- const [input, setInput] = useState13(
7244
- convertFloatToTime(value ?? defaultValue)
7245
- );
7246
- const [formattedTime, setFormattedTime] = useState13("");
7247
- const [errors, setErrors] = useState13("");
7248
- const handleInputChange = (e) => {
7249
- const raw = e.target.value.replace(/[^\d:]/g, "");
7250
- setInput(raw);
7251
- const timeRegex = /^(\d{1,2}):?(\d{0,2})$/;
7252
- const match = raw.match(timeRegex);
7253
- if (!match) {
7254
- setErrors("\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7");
7255
- setFormattedTime("");
7256
- return;
7257
- }
7258
- let hours = parseInt(match[1] ?? "0", 10);
7259
- let minutes = parseInt(match[2] ?? "0", 10);
7260
- if (isNaN(hours)) hours = 0;
7261
- if (isNaN(minutes)) minutes = 0;
7262
- if (hours >= 24) {
7263
- hours = 0;
7264
- }
7265
- if (minutes >= 60) {
7266
- minutes = 0;
7267
- }
7268
- const formatted = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
7269
- setErrors("");
7270
- setFormattedTime(formatted);
7271
- fieldOnChange(formatted);
7253
+ useAppSelector as useAppSelector6,
7254
+ selectSearch as selectSearch4,
7255
+ selectList as selectList3
7256
+ } from "@fctc/interface-logic/store";
7257
+ import { domainHelper } from "@fctc/interface-logic/utils";
7258
+ var tableController = ({ data }) => {
7259
+ const [rows, setRows] = useState15(data.records || []);
7260
+ const [columns, setColumns] = useState15([]);
7261
+ const dataModelFields = data.fields?.map((field) => {
7262
+ return {
7263
+ ...data.dataModel?.[field?.name],
7264
+ ...field,
7265
+ string: field?.string || data.dataModel?.[field?.name]?.string
7266
+ };
7267
+ });
7268
+ const mergeFields = mergeButtons(dataModelFields);
7269
+ const transformData = (dataList) => {
7270
+ if (!dataList) return;
7271
+ return dataList?.map((item) => {
7272
+ const transformedItem = { ...item };
7273
+ Object.keys(item).forEach((field) => {
7274
+ if (field !== "__domain") {
7275
+ if (item[field] && typeof item[field] === "object" && item[field].display_name) {
7276
+ transformedItem[field] = item[field];
7277
+ } else if (Array.isArray(item[field]) && item[field].length > 0) {
7278
+ if (data.typeTable === "group" && item[field]?.length === 2 && typeof item[field]?.[1] === "string") {
7279
+ transformedItem["string"] = item[field]?.[1];
7280
+ }
7281
+ transformedItem[field] = item[field];
7282
+ }
7283
+ }
7284
+ });
7285
+ return item.display_name ? { ...transformedItem, item: item.display_name } : transformedItem;
7286
+ });
7272
7287
  };
7273
- const handleBlur = () => {
7274
- if (!isDirty) return;
7275
- if (formattedTime) {
7276
- setInput(formattedTime);
7277
- const floatVal = convertTimeToFloat(formattedTime);
7278
- fieldOnChange(floatVal);
7279
- if (onChange) {
7280
- onChange(name ?? "", floatVal);
7281
- }
7282
- } else {
7283
- setInput("00:00");
7284
- fieldOnChange(0);
7285
- if (onChange) {
7286
- onChange(name ?? "", 0);
7287
- }
7288
- setErrors("");
7288
+ useEffect15(() => {
7289
+ setRows(transformData(data.records || null));
7290
+ }, [data.records]);
7291
+ const handleGetColumns = () => {
7292
+ let cols = [];
7293
+ try {
7294
+ cols = mergeFields?.filter((item) => {
7295
+ return item?.widget !== "details_Receive_money" && !(item?.column_invisible ? domainHelper.matchDomains(data.context, item?.column_invisible) : item?.invisible ? domainHelper.matchDomains(data.context, item?.invisible) : false);
7296
+ })?.map((field) => {
7297
+ return {
7298
+ name: field?.name,
7299
+ optional: field?.optional,
7300
+ title: field?.type_co === "button" ? "" : field?.string,
7301
+ field: { ...field }
7302
+ };
7303
+ });
7304
+ } catch (error) {
7305
+ console.error("Error in useTable:", error);
7289
7306
  }
7290
- onBlur();
7307
+ return cols;
7291
7308
  };
7292
- const handleKeyDown = (e) => {
7293
- {
7294
- const allowed = [
7295
- "Backspace",
7296
- "Tab",
7297
- "ArrowLeft",
7298
- "ArrowRight",
7299
- "Delete",
7300
- "Home",
7301
- "End",
7302
- ":"
7303
- ];
7304
- const isNumber = /^[0-9]$/.test(e.key);
7305
- if (!isNumber && !allowed.includes(e.key)) {
7306
- e.preventDefault();
7309
+ useEffect15(() => {
7310
+ const columns2 = handleGetColumns();
7311
+ setColumns(columns2);
7312
+ }, [data.records]);
7313
+ const onToggleColumnOptional = (item) => {
7314
+ const tempColumn = [...columns]?.map((val) => {
7315
+ if (item?.name === val?.name) {
7316
+ return {
7317
+ ...val,
7318
+ optional: item?.optional === "show" ? "hide" : "show"
7319
+ };
7307
7320
  }
7308
- }
7321
+ return val;
7322
+ });
7323
+ setColumns(tempColumn);
7309
7324
  };
7310
7325
  return {
7311
- handleInputChange,
7312
- handleBlur,
7313
- handleKeyDown,
7314
- input,
7315
- errors
7326
+ rows,
7327
+ columns,
7328
+ onToggleColumnOptional,
7329
+ typeTable: data.typeTable
7316
7330
  };
7317
7331
  };
7318
7332
 
7319
- // src/widget/basic/float-field/controller.ts
7320
- import { useEffect as useEffect16, useRef as useRef5, useState as useState14 } from "react";
7321
- var floatController = ({
7322
- onChange,
7323
- value,
7324
- props
7325
- }) => {
7326
- const { name, required, methods, onChange: handleOnchange, string } = props;
7327
- const { setError, clearErrors } = methods;
7328
- const [inputValue, setInputValue] = useState14(
7329
- value !== void 0 && value !== null ? useFormatFloatNumber(value) : ""
7330
- );
7331
- useEffect16(() => {
7332
- if (value !== void 0 && value !== null && value !== parseFloat(inputValue?.replace(/,/g, ""))) {
7333
- setInputValue(useFormatFloatNumber(value));
7334
- clearErrors(name);
7335
- } else if (value === null || value === void 0) {
7336
- setInputValue("");
7337
- }
7338
- }, [value, name, clearErrors]);
7339
- const isDirtyRef = useRef5(false);
7340
- const inputRef = useRef5(null);
7341
- const lastCommittedValueRef = useRef5(null);
7342
- const handleInputChange = (e) => {
7343
- const newValue = e.target.value;
7344
- const valueWithoutCommas = newValue.replace(/,/g, "");
7345
- if (/^[0-9]*[.,]?[0-9]*$/.test(valueWithoutCommas) || newValue === "") {
7346
- const parts = valueWithoutCommas.split(".");
7347
- let integerPart = parts[0] || "";
7348
- const decimalPart = parts[1] || "";
7349
- if (decimalPart.length > 100) return;
7350
- if (integerPart) {
7351
- integerPart = Number(integerPart).toLocaleString("en-US");
7352
- }
7353
- const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
7354
- setInputValue(formattedValue);
7355
- const parsedValue = parseFloat(valueWithoutCommas.replace(",", "."));
7356
- if (!isNaN(parsedValue)) {
7357
- if (parsedValue < 0) {
7358
- setError(name, {
7359
- type: "validate",
7360
- message: i18n_default.t("invalid_number")
7361
- });
7362
- } else {
7363
- onChange(parsedValue);
7364
- clearErrors(name);
7365
- isDirtyRef.current = true;
7366
- }
7367
- } else {
7368
- onChange(null);
7369
- clearErrors(name);
7370
- }
7371
- }
7333
+ // src/widget/advance/table/table-group/controller.ts
7334
+ import { useEffect as useEffect16, useMemo as useMemo14, useState as useState16 } from "react";
7335
+ import {
7336
+ useOdooDataTransform,
7337
+ useGetListData as useGetListData3
7338
+ } from "@fctc/interface-logic/hooks";
7339
+ import {
7340
+ useAppSelector as useAppSelector7,
7341
+ selectSearch as selectSearch5,
7342
+ selectList as selectList4,
7343
+ useAppDispatch as useAppDispatch8,
7344
+ setSelectedRowKeys as setSelectedRowKeys3
7345
+ } from "@fctc/interface-logic/store";
7346
+
7347
+ // src/environment.ts
7348
+ var environment_exports = {};
7349
+ __reExport(environment_exports, environment_star);
7350
+ import * as environment_star from "@fctc/interface-logic/environment";
7351
+
7352
+ // src/widget/advance/table/table-group/controller.ts
7353
+ var tableGroupController = (props) => {
7354
+ const env = (0, environment_exports.getEnv)();
7355
+ const {
7356
+ rows,
7357
+ columns,
7358
+ indexRow,
7359
+ row,
7360
+ model,
7361
+ viewData,
7362
+ renderField,
7363
+ level,
7364
+ specification,
7365
+ domain,
7366
+ context,
7367
+ checkedAll,
7368
+ isDisplayCheckbox,
7369
+ isAutoSelect,
7370
+ setIsAutoSelect,
7371
+ selectedRowKeysRef
7372
+ } = props;
7373
+ const [pageGroup, setPageGroup] = useState16(0);
7374
+ const { groupByDomain, selectedTags } = useAppSelector7(selectSearch5);
7375
+ const { selectedRowKeys } = useAppSelector7(selectList4);
7376
+ const appDispatch = useAppDispatch8();
7377
+ const { toDataJS } = useOdooDataTransform();
7378
+ const initVal = toDataJS(row, viewData, model);
7379
+ const [isShowGroup, setIsShowGroup] = useState16(false);
7380
+ const [colEmptyGroup, setColEmptyGroup] = useState16({
7381
+ fromStart: 1,
7382
+ fromEnd: 1
7383
+ });
7384
+ const processedData = useMemo14(() => {
7385
+ const calculateColSpanEmpty = () => {
7386
+ const startIndex = columns.findIndex(
7387
+ (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator === "sum"
7388
+ );
7389
+ const endIndex = columns.findLastIndex(
7390
+ (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator !== "sum"
7391
+ );
7392
+ const fromStart = startIndex === -1 ? columns.length : startIndex;
7393
+ const fromEnd = endIndex === -1 ? columns.length : columns.length - 1 - endIndex;
7394
+ setColEmptyGroup({ fromStart: fromStart + 1, fromEnd: fromEnd + 1 });
7395
+ return { fromStart: fromStart + 1, fromEnd: fromEnd + 1 };
7396
+ };
7397
+ return calculateColSpanEmpty();
7398
+ }, [columns, row]);
7399
+ const shouldFetchData = useMemo14(() => {
7400
+ return !!isShowGroup;
7401
+ }, [isShowGroup]);
7402
+ const enabled = shouldFetchData && !!processedData;
7403
+ const listDataProps = {
7404
+ model,
7405
+ specification,
7406
+ domain,
7407
+ context,
7408
+ offset: pageGroup * 10,
7409
+ fields: groupByDomain?.fields,
7410
+ groupby: [groupByDomain?.contexts[level]?.group_by]
7372
7411
  };
7373
- const handleInputMouseLeave = () => {
7374
- if (!isDirtyRef.current) {
7375
- inputRef.current?.blur();
7412
+ const queryKey = [
7413
+ `data-${model}--${level}-row${indexRow}`,
7414
+ specification,
7415
+ domain,
7416
+ pageGroup
7417
+ ];
7418
+ const {
7419
+ data: dataResponse,
7420
+ isFetched: isQueryFetched,
7421
+ isPlaceholderData,
7422
+ isLoading,
7423
+ isFetching
7424
+ } = useGetListData3(listDataProps, queryKey, enabled);
7425
+ const {
7426
+ columns: columnsGroup,
7427
+ rows: rowsGroup,
7428
+ typeTable: typeTableGroup
7429
+ } = tableController({
7430
+ data: {
7431
+ fields: viewData?.views?.list?.fields,
7432
+ records: dataResponse?.records ?? dataResponse?.groups,
7433
+ dataModel: viewData?.models?.[model],
7434
+ context: env.context,
7435
+ typeTable: dataResponse?.groups ? "group" : "list"
7436
+ }
7437
+ });
7438
+ const leftPadding = level > 1 ? level * 8 + "px" : "0px";
7439
+ useEffect16(() => {
7440
+ if (isShowGroup && selectedTags?.length > 0) {
7441
+ setIsShowGroup(false);
7442
+ }
7443
+ }, [selectedTags]);
7444
+ const group_by_field_name = groupByDomain?.contexts[level - 1]?.group_by;
7445
+ const nameGroup = Array.isArray(row[group_by_field_name]) ? row?.string ?? row[`${group_by_field_name}`][1] : viewData?.models?.[model]?.[group_by_field_name]?.selection ? viewData.models[model][group_by_field_name].selection.find(
7446
+ (selectItem) => selectItem?.[0] === row[group_by_field_name]
7447
+ )?.[1] : row[group_by_field_name];
7448
+ const nameGroupWithCount = `${typeof nameGroup === "string" ? nameGroup : typeof nameGroup === "boolean" && nameGroup ? i18n_default.t("yes") : i18n_default.t("no")} (${row[`${group_by_field_name?.split(":")?.[0]}_count`]})`;
7449
+ const allIdsNull = selectedRowKeys?.every((item) => item === void 0);
7450
+ const handleExpandChildGroup = () => {
7451
+ if (isLoading || isFetching) return;
7452
+ const toggleShowGroup = () => setIsShowGroup((prev) => !prev);
7453
+ if (allIdsNull || typeTableGroup === "group") {
7454
+ toggleShowGroup();
7376
7455
  return;
7377
7456
  }
7378
- const rawValue = inputValue.replace(/,/g, "");
7379
- const parsedValue = parseFloat(rawValue);
7380
- if (rawValue === "" || rawValue === ".") {
7381
- if (required) {
7382
- setError(name, {
7383
- type: "required",
7384
- message: `${string} ${i18n_default.t("must_required")}`
7385
- });
7386
- }
7387
- onChange(null);
7388
- setInputValue("");
7389
- lastCommittedValueRef.current = null;
7390
- } else if (!isNaN(parsedValue)) {
7391
- if (parsedValue < 0) {
7392
- setError(name, {
7393
- type: "validate",
7394
- message: i18n_default.t("invalid_number")
7395
- });
7396
- setInputValue("");
7397
- lastCommittedValueRef.current = null;
7398
- } else {
7399
- if (lastCommittedValueRef.current !== parsedValue) {
7400
- const parts = rawValue.split(".");
7401
- let integerPart = parts[0];
7402
- const decimalPart = parts[1] || "";
7403
- integerPart = Number(integerPart).toLocaleString("en-US");
7404
- const formattedValue = decimalPart ? `${integerPart}.${decimalPart}` : integerPart;
7405
- onChange(parsedValue);
7406
- setInputValue(formattedValue);
7407
- handleOnchange?.(name ?? "", parsedValue);
7408
- clearErrors(name);
7409
- lastCommittedValueRef.current = parsedValue;
7410
- }
7411
- }
7412
- } else {
7413
- setError(name, {
7414
- type: "validate",
7415
- message: i18n_default.t("invalid_number")
7416
- });
7417
- setInputValue("");
7418
- lastCommittedValueRef.current = null;
7457
+ if (isShowGroup && checkedAll) {
7458
+ const ids = rowsGroup?.map((item) => item?.id) || [];
7459
+ const filteredIds = selectedRowKeys.filter(
7460
+ (id) => !ids.includes(id)
7461
+ );
7462
+ appDispatch(setSelectedRowKeys3(filteredIds));
7463
+ } else if (!isShowGroup && selectedRowKeys?.length > 0 && typeTableGroup === "list" && checkedAll && !allIdsNull && isQueryFetched) {
7464
+ const clonedKeys = [...selectedRowKeys];
7465
+ appDispatch(setSelectedRowKeys3([...clonedKeys, -1]));
7466
+ setTimeout(() => appDispatch(setSelectedRowKeys3(clonedKeys)), 500);
7467
+ } else if (isShowGroup && selectedRowKeys?.length > 0 && typeTableGroup === "list" && !checkedAll && !allIdsNull) {
7468
+ const filteredKeys = selectedRowKeys.filter((id) => id > -1);
7469
+ appDispatch(setSelectedRowKeys3(filteredKeys));
7419
7470
  }
7420
- isDirtyRef.current = false;
7421
- inputRef.current?.blur();
7471
+ toggleShowGroup();
7422
7472
  };
7473
+ useEffect16(() => {
7474
+ if (!isQueryFetched || !rowsGroup || !checkedAll || allIdsNull || typeTableGroup === "group") {
7475
+ return;
7476
+ }
7477
+ const clonedKeys = [...selectedRowKeys];
7478
+ setSelectedRowKeys3([...clonedKeys, -1]);
7479
+ setTimeout(() => setSelectedRowKeys3(clonedKeys), 500);
7480
+ }, [isQueryFetched]);
7423
7481
  return {
7424
- handleInputMouseLeave,
7425
- handleInputChange,
7426
- useFormatFloatNumber,
7427
- inputRef,
7428
- inputValue
7482
+ handleExpandChildGroup,
7483
+ colEmptyGroup,
7484
+ leftPadding,
7485
+ isShowGroup,
7486
+ isQueryFetched,
7487
+ nameGroupWithCount,
7488
+ columns,
7489
+ row,
7490
+ isPlaceholderData,
7491
+ columnsGroup,
7492
+ indexRow,
7493
+ rowsGroup,
7494
+ model,
7495
+ viewData,
7496
+ renderField,
7497
+ level,
7498
+ specification,
7499
+ context,
7500
+ checkedAll,
7501
+ isDisplayCheckbox,
7502
+ isAutoSelect,
7503
+ setIsAutoSelect,
7504
+ selectedRowKeysRef,
7505
+ initVal,
7506
+ dataResponse,
7507
+ pageGroup,
7508
+ setPageGroup
7429
7509
  };
7430
7510
  };
7431
- var useFormatFloatNumber = (value) => {
7432
- if (value === void 0 || value === null || value === "") return "";
7433
- const numValue = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : value;
7434
- if (isNaN(numValue)) return "";
7435
- return numValue.toLocaleString("en-US", {
7436
- minimumFractionDigits: numValue % 1 === 0 ? 0 : 1,
7437
- maximumFractionDigits: 20
7438
- });
7439
- };
7440
7511
 
7441
- // src/widget/basic/download-file-field/controller.ts
7442
- import { useId, useState as useState15 } from "react";
7443
- var downloadFileController = () => {
7444
- const inputId = useId();
7445
- const [file, setFile] = useState15(null);
7446
- const handleFileChange = (e) => {
7447
- setFile(e.target.files[0]);
7512
+ // src/widget/advance/search/controller.ts
7513
+ var import_moment2 = __toESM(require_moment());
7514
+ import { SearchType } from "@fctc/interface-logic/constants";
7515
+ import {
7516
+ domainHelper as domainHelper2,
7517
+ evalJSONDomain as evalJSONDomain6,
7518
+ validateAndParseDate
7519
+ } from "@fctc/interface-logic/utils";
7520
+ import { useCallback as useCallback3, useEffect as useEffect17, useState as useState17 } from "react";
7521
+ var searchController = ({
7522
+ viewData,
7523
+ actionData,
7524
+ fieldsList,
7525
+ contextSearch,
7526
+ setSearchMap,
7527
+ searchMap
7528
+ }) => {
7529
+ const [filterBy, setFilterBy] = useState17(null);
7530
+ const [searchBy, setSearchBy] = useState17(null);
7531
+ const [groupBy, setGroupBy] = useState17(null);
7532
+ const [selectedTags, setSelectedTags] = useState17(null);
7533
+ const [searchString, setSearchString] = useState17("");
7534
+ const domainAction = actionData?.domain ? Array.isArray(actionData?.domain) ? [...actionData?.domain] : evalJSONDomain6(actionData?.domain, contextSearch) : [];
7535
+ const aid = actionData?.id;
7536
+ const model = actionData?.res_model;
7537
+ const clearSearch = () => {
7538
+ setFilterBy([]);
7539
+ setGroupBy([]);
7540
+ setSearchBy([]);
7541
+ setSelectedTags(null);
7542
+ setSearchString("");
7543
+ setSearchMap({});
7448
7544
  };
7449
- const handleFileDownload = () => {
7450
- const url = URL.createObjectURL(file);
7451
- const link = document.createElement("a");
7452
- link.href = url;
7453
- link.download = file.name;
7454
- document.body.appendChild(link);
7455
- link.click();
7456
- document.body.removeChild(link);
7457
- };
7458
- return {
7459
- inputId,
7460
- file,
7461
- handleFileChange,
7462
- handleFileDownload
7463
- };
7464
- };
7465
-
7466
- // src/widget/basic/download-binary-field/controller.ts
7467
- var downLoadBinaryController = (props) => {
7468
- const { value, defaultValue, formValues } = props;
7469
- const handleFileDownload = async (e) => {
7470
- e.stopPropagation();
7471
- await downloadFile(value || defaultValue, formValues?.name);
7472
- };
7473
- const downloadFile = async (url, filename) => {
7474
- try {
7475
- const response = await fetch(url);
7476
- if (response) {
7477
- const blob = await response.blob();
7478
- const urlBlob = window.URL.createObjectURL(blob);
7479
- const link = document.createElement("a");
7480
- link.href = urlBlob;
7481
- link.download = filename || "downloaded-file";
7482
- document.body.appendChild(link);
7483
- link.click();
7484
- document.body.removeChild(link);
7485
- window.URL.revokeObjectURL(urlBlob);
7486
- }
7487
- } catch (error) {
7488
- console.error("File download failed:", error);
7489
- }
7490
- };
7491
- return {
7492
- handleFileDownload
7493
- };
7494
- };
7495
-
7496
- // src/widget/basic/date-field/controller.ts
7497
- var import_moment2 = __toESM(require_moment());
7498
- var DURATIONS = {
7499
- PAST: "past",
7500
- NOW: "now",
7501
- FUTURE: "future"
7502
- };
7503
- var dateFieldController = (props) => {
7504
- const {
7505
- string,
7506
- showTime = false,
7507
- widget,
7508
- min,
7509
- max,
7510
- viewData,
7511
- formValues,
7512
- model
7513
- } = props;
7514
- const range = (start, end, step = 1) => {
7515
- const arr = [];
7516
- for (let i = start; i < end; i += step) {
7517
- arr.push(i);
7518
- }
7519
- return arr;
7520
- };
7521
- const formatDate = showTime ? "DD/MM/YYYY HH:mm:ss" : "DD/MM/YYYY";
7522
- const formatDateParse = showTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
7523
- const fieldForCustom = widget === "datetime_custom" || widget === "date_custom";
7524
- const minNowValue = fieldForCustom && (min === DURATIONS.NOW ? true : typeof min === "string" && Object.keys(formValues)?.includes(min) && formValues?.[min] ? (0, import_moment2.default)(formValues?.[min], formatDateParse).add(7, "hours") : null);
7525
- const maxNowValue = fieldForCustom && (max === DURATIONS.NOW ? true : typeof max === "string" && Object.keys(formValues)?.includes(max) && formValues?.[max] ? (0, import_moment2.default)(formValues?.[max], formatDateParse).add(7, "hours") : null);
7526
- const years = range(
7527
- minNowValue ? (/* @__PURE__ */ new Date()).getFullYear() : 1990,
7528
- (/* @__PURE__ */ new Date()).getFullYear() + 4,
7529
- 1
7530
- );
7531
- const months_vi = [
7532
- "Th\xE1ng 1",
7533
- "Th\xE1ng 2",
7534
- "Th\xE1ng 3",
7535
- "Th\xE1ng 4",
7536
- "Th\xE1ng 5",
7537
- "Th\xE1ng 6",
7538
- "Th\xE1ng 7",
7539
- "Th\xE1ng 8",
7540
- "Th\xE1ng 9",
7541
- "Th\xE1ng 10",
7542
- "Th\xE1ng 11",
7543
- "Th\xE1ng 12"
7544
- ];
7545
- const months_en = [
7546
- "January",
7547
- "February",
7548
- "March",
7549
- "April",
7550
- "May",
7551
- "June",
7552
- "July",
7553
- "August",
7554
- "September",
7555
- "October",
7556
- "November",
7557
- "December"
7558
- ];
7559
- const customValidateMinMax = (date) => {
7560
- const selected = (0, import_moment2.default)(date, formatDateParse);
7561
- const now = (0, import_moment2.default)();
7562
- const compareSelected = showTime ? selected : selected.clone().startOf("day");
7563
- const compareNow = showTime ? now : now.clone().startOf("day");
7564
- if (minNowValue) {
7565
- if (compareSelected.isBefore(compareNow) && typeof minNowValue === "boolean" && minNowValue === true) {
7566
- return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
7567
- "greater_or_equal_now"
7568
- )}`;
7569
- } else if (import_moment2.default.isMoment(minNowValue)) {
7570
- const compareMin = showTime ? minNowValue : minNowValue.clone().startOf("day");
7571
- if (compareSelected.isBefore(compareMin)) {
7572
- const fieldRelationDate = viewData?.models?.[model]?.[min ?? ""];
7573
- return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
7574
- "greater_or_equal"
7575
- )} ${fieldRelationDate?.string}`;
7576
- }
7577
- }
7578
- } else if (maxNowValue) {
7579
- if (compareSelected.isAfter(compareNow) && typeof maxNowValue === "boolean" && maxNowValue === true) {
7580
- return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
7581
- "less_or_equal_now"
7582
- )}`;
7583
- } else if (import_moment2.default.isMoment(maxNowValue)) {
7584
- const compareMax = showTime ? maxNowValue : maxNowValue.clone().startOf("day");
7585
- if (compareSelected.isAfter(compareMax)) {
7586
- const fieldRelationDate = viewData?.models?.[model]?.[max ?? ""];
7587
- return `${i18n_default.t("please_enter")} ${string} ${i18n_default.t(
7588
- "less_or_equal"
7589
- )} ${fieldRelationDate?.string}`;
7590
- }
7591
- }
7592
- }
7593
- return false;
7594
- };
7595
- return {
7596
- formatDate,
7597
- formatDateParse,
7598
- range,
7599
- years,
7600
- months_vi,
7601
- months_en,
7602
- customValidateMinMax,
7603
- minNowValue,
7604
- maxNowValue
7605
- };
7606
- };
7607
-
7608
- // src/widget/basic/copy-link-button/controller.ts
7609
- import { useState as useState16 } from "react";
7610
- import { copyTextToClipboard } from "@fctc/interface-logic/utils";
7611
- var copyLinkButtonController = (props) => {
7612
- const { value, defaultValue } = props;
7613
- const [isCopied, setIsCopied] = useState16(false);
7614
- const handleCopyToClipboard = async (value2) => {
7615
- await copyTextToClipboard(value2);
7616
- setIsCopied(true);
7617
- setTimeout(() => setIsCopied(false), 2e3);
7618
- };
7619
- const propValue = value || defaultValue;
7620
- return {
7621
- isCopied,
7622
- handleCopyToClipboard,
7623
- propValue
7624
- };
7625
- };
7626
-
7627
- // src/widget/basic/color-field/color-controller.ts
7628
- import { getEnv as getEnv10 } from "@fctc/interface-logic/environment";
7629
- import { useSave as useSave3 } from "@fctc/interface-logic/hooks";
7630
- import { evalJSONContext as evalJSONContext7 } from "@fctc/interface-logic/utils";
7631
- var colorFieldController = (props) => {
7632
- const { value, isForm, name, formValues, idForm, model, actionData } = props;
7633
- const env = getEnv10();
7634
- const _context = { ...evalJSONContext7(actionData?.context) || {} };
7635
- const contextObject = { ...env.context, ..._context };
7636
- const idDefault = isForm ? idForm : formValues?.id;
7637
- const { mutate: onSave } = useSave3();
7638
- const savePickColor = async (colorObject) => {
7639
- const { id } = colorObject;
7640
- if (value === id) return;
7641
- try {
7642
- onSave({
7643
- ids: idDefault !== null ? [idDefault] : [],
7644
- model: model ?? "",
7645
- data: { [name ?? ""]: id },
7646
- specification: {
7647
- name: {},
7648
- color: {}
7649
- },
7650
- context: contextObject
7651
- });
7652
- } catch (error) {
7653
- console.log(error);
7654
- }
7655
- };
7656
- return {
7657
- savePickColor
7658
- };
7659
- };
7660
-
7661
- // src/widget/basic/binary-field/controller.ts
7662
- import { useEffect as useEffect17, useId as useId2, useRef as useRef6, useState as useState17 } from "react";
7663
- import { isBase64Image } from "@fctc/interface-logic/utils";
7664
- var binaryFieldController = (props) => {
7665
- const { name, methods, readonly = false, value } = props;
7666
- const inputId = useId2();
7667
- const [selectedImage, setSelectedImage] = useState17(null);
7668
- const [initialImage, setInitialImage] = useState17(value || null);
7669
- const [isInsideTable, setIsInsideTable] = useState17(false);
7670
- const { setValue } = methods;
7671
- const binaryRef = useRef6(null);
7672
- const convertUrlToBase64 = async (url) => {
7673
- try {
7674
- const response = await fetch(url);
7675
- const blob = await response.blob();
7676
- return new Promise((resolve, reject) => {
7677
- const reader = new FileReader();
7678
- reader.onloadend = () => {
7679
- resolve(reader.result);
7680
- };
7681
- reader.onerror = reject;
7682
- reader.readAsDataURL(blob);
7683
- });
7684
- } catch (error) {
7685
- console.error("Error converting URL to Base64:", error);
7686
- throw error;
7687
- }
7688
- };
7689
- const extractBase64Data = (base64Url) => {
7690
- if (base64Url.includes("base64,")) {
7691
- return base64Url.split("base64,")[1];
7692
- }
7693
- return base64Url;
7694
- };
7695
- const handleImageChange = async (e, onChange) => {
7696
- if (readonly) return;
7697
- const file = e?.target?.files?.[0];
7698
- if (file) {
7699
- const imageUrl = URL.createObjectURL(file);
7700
- setSelectedImage(imageUrl);
7701
- setInitialImage(null);
7702
- onChange(file);
7703
- const compressedBase64 = await convertUrlToBase64(imageUrl);
7704
- const base64Data = extractBase64Data(compressedBase64);
7705
- setValue(name, base64Data, {
7706
- shouldDirty: true
7707
- });
7545
+ const fetchData = async () => {
7546
+ if (viewData) {
7547
+ try {
7548
+ const dataModel = viewData?.models?.[model];
7549
+ const searchViews = viewData?.views?.search;
7550
+ const searchByItems = searchViews?.search_by?.filter(
7551
+ (item) => !domainHelper2.matchDomains(contextSearch, item.invisible)
7552
+ )?.map(
7553
+ ({ string, name, filter_domain, operator, widget }, index) => ({
7554
+ dataIndex: index,
7555
+ title: string ?? dataModel[name]?.string,
7556
+ name: name ?? dataModel[name]?.name,
7557
+ filter_domain,
7558
+ operator,
7559
+ widget,
7560
+ type: dataModel[name]?.type
7561
+ })
7562
+ );
7563
+ const filterByItems = searchViews?.filter_by.filter((item) => {
7564
+ return !domainHelper2.matchDomains(contextSearch, item?.invisible);
7565
+ })?.map((item) => ({ ...item, active: false }));
7566
+ const groupByItems = searchViews?.group_by.filter(
7567
+ (item) => !domainHelper2.matchDomains(contextSearch, item?.invisible)
7568
+ ).map((item) => ({
7569
+ ...item,
7570
+ string: item.string ?? viewData?.models?.[model]?.[item?.name?.split("group_by_")?.[1]]?.string
7571
+ }));
7572
+ setSearchBy(searchByItems);
7573
+ setFilterBy(filterByItems);
7574
+ setGroupBy(groupByItems);
7575
+ } catch (error) {
7576
+ console.error("Error fetching data:", error);
7577
+ }
7708
7578
  }
7709
7579
  };
7710
- const handleRemoveImage = (onChange) => {
7711
- setSelectedImage(null);
7712
- setInitialImage(null);
7713
- onChange(null);
7580
+ useEffect17(() => {
7581
+ clearSearch();
7582
+ fetchData();
7583
+ }, [aid, model, viewData]);
7584
+ const onChangeSearchInput = (search_string) => {
7585
+ setSearchString(search_string);
7714
7586
  };
7715
- const isBlobUrl = (url) => {
7716
- return /^blob:/.test(url);
7587
+ const removeKeyFromSearchMap = ({
7588
+ key,
7589
+ item
7590
+ }) => {
7591
+ const values = searchMap[key];
7592
+ if (!values) return searchMap;
7593
+ const newSearchMap = { ...searchMap };
7594
+ if (item) {
7595
+ const filtered = values.filter((value) => value.name !== item.name);
7596
+ if (filtered.length > 0) {
7597
+ newSearchMap[key] = filtered;
7598
+ } else {
7599
+ delete newSearchMap[key];
7600
+ }
7601
+ } else {
7602
+ delete newSearchMap[key];
7603
+ }
7604
+ setSearchMap(newSearchMap);
7717
7605
  };
7718
- const checkIsImageLink = (url) => {
7719
- const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
7720
- return imageExtensions.test(url) || isBase64Image(url) || isBlobUrl(url);
7606
+ const updateSearchMap = ({ key, item }) => {
7607
+ const newSearchMap = { ...searchMap };
7608
+ const currentValues = searchMap[key] ?? [];
7609
+ newSearchMap[key] = [...currentValues, item];
7610
+ setSearchMap(newSearchMap);
7721
7611
  };
7722
- const getImageBase64WithMimeType = (base64) => {
7723
- if (typeof base64 !== "string" || base64.length < 10) return null;
7724
- if (isBase64Image(base64)) return base64;
7725
- let mimeType = null;
7726
- if (base64.startsWith("iVBORw0KGgo")) mimeType = "image/png";
7727
- else if (base64.startsWith("/9j/")) mimeType = "image/jpeg";
7728
- else if (base64.startsWith("R0lGOD")) mimeType = "image/gif";
7729
- else if (base64.startsWith("Qk")) mimeType = "image/bmp";
7730
- else if (base64.startsWith("UklGR")) mimeType = "image/webp";
7731
- return mimeType ? `data:${mimeType};base64,${base64}` : null;
7612
+ const removeSearchItems = (key, item) => {
7613
+ removeKeyFromSearchMap({ key: String(key), item });
7732
7614
  };
7733
- useEffect17(() => {
7734
- return () => {
7735
- if (selectedImage) {
7736
- URL.revokeObjectURL(selectedImage);
7615
+ const addSearchItems = (key, newItem) => {
7616
+ updateSearchMap({ key, item: newItem });
7617
+ };
7618
+ const formatDomain = () => {
7619
+ if (domainAction) {
7620
+ const domain = [];
7621
+ if (domainAction?.length > 0) {
7622
+ if (Object.keys(searchMap).length > 0) {
7623
+ domain.push("&");
7624
+ }
7625
+ domainAction.forEach((domainItem) => {
7626
+ domain.push(domainItem);
7627
+ });
7737
7628
  }
7738
- };
7739
- }, [selectedImage]);
7629
+ Object.keys(searchMap).forEach((key, keyIndex, keys) => {
7630
+ if (!key?.includes(SearchType.GROUP)) {
7631
+ if (keys.length > 1 && keyIndex < keys.length - 1) {
7632
+ domain.push("&");
7633
+ }
7634
+ const valuesOfKey = searchMap[key];
7635
+ valuesOfKey.forEach((value, index) => {
7636
+ if (index < valuesOfKey.length - 1) {
7637
+ domain.push("|");
7638
+ }
7639
+ if (value.domain) {
7640
+ domain.push(...value.domain);
7641
+ return;
7642
+ }
7643
+ let valueDomainItem = value?.value;
7644
+ if (value?.modelType === "date") {
7645
+ valueDomainItem = validateAndParseDate(value?.value);
7646
+ } else if (value?.modelType === "datetime") {
7647
+ if (value?.operator === "<=" || value?.operator === "<") {
7648
+ const parsedDate = validateAndParseDate(value?.value, true);
7649
+ const hasTime = (0, import_moment2.default)(value?.value).format("HH:mm:ss") !== "00:00:00";
7650
+ valueDomainItem = hasTime ? (0, import_moment2.default)(parsedDate).format("YYYY-MM-DD HH:mm:ss") : (0, import_moment2.default)(parsedDate).add(1, "day").subtract(1, "second").format("YYYY-MM-DD HH:mm:ss");
7651
+ } else {
7652
+ valueDomainItem = validateAndParseDate(value?.value, true);
7653
+ }
7654
+ }
7655
+ const operator = value?.modelType === "date" || value?.modelType === "datetime" || value?.modelType === "boolean" || value?.modelType === "integer" ? value?.operator ?? "=" : value.operator ?? "ilike";
7656
+ domain.push([value.name, operator, valueDomainItem]);
7657
+ });
7658
+ }
7659
+ });
7660
+ return [...domain];
7661
+ }
7662
+ };
7663
+ const setTagSearch = useCallback3(
7664
+ (updatedMap) => {
7665
+ if (!updatedMap) return;
7666
+ const tagsSearch = Object.entries(updatedMap).map(
7667
+ ([key, objValues]) => {
7668
+ const {
7669
+ title,
7670
+ name,
7671
+ groupIndex,
7672
+ type,
7673
+ widget,
7674
+ modelType,
7675
+ dataIndex
7676
+ } = objValues[0];
7677
+ if (!key?.includes(SearchType.GROUP)) {
7678
+ const values = objValues?.map((objValue) => objValue.value);
7679
+ return {
7680
+ title,
7681
+ name: type === SearchType.SEARCH ? `${SearchType.SEARCH}_${String(dataIndex)}` : groupIndex ?? name,
7682
+ values,
7683
+ type,
7684
+ widget,
7685
+ modelType
7686
+ };
7687
+ } else {
7688
+ const contexts = [];
7689
+ let groupValues = [];
7690
+ objValues?.forEach((objValue) => {
7691
+ const { context, value, active, groupIndex: groupIndex2, isDefault } = objValue;
7692
+ const indexAppend = groupIndex2 != null ? groupIndex2 : viewData?.views?.search?.filters_by?.length ?? 0;
7693
+ contexts.push(
7694
+ ...Array.isArray(context?.group_by) ? context.group_by.map((item) => ({ group_by: item })) : [context]
7695
+ );
7696
+ groupValues[indexAppend] = {
7697
+ contexts: [
7698
+ ...Array.isArray(context?.group_by) ? context.group_by.map((item) => ({
7699
+ group_by: item
7700
+ })) : [context]
7701
+ ],
7702
+ strings: isDefault ? [value] : [...groupValues[indexAppend]?.strings ?? [], value]
7703
+ };
7704
+ });
7705
+ const fields = [
7706
+ ...new Set(fieldsList?.map((item) => item?.name))
7707
+ ];
7708
+ const groupByTag = {
7709
+ title,
7710
+ values: groupValues?.filter(
7711
+ (item) => item !== void 0
7712
+ ),
7713
+ type,
7714
+ contexts,
7715
+ fields
7716
+ };
7717
+ return groupByTag;
7718
+ }
7719
+ }
7720
+ );
7721
+ setSelectedTags(tagsSearch);
7722
+ setSearchString("");
7723
+ },
7724
+ [searchMap]
7725
+ );
7740
7726
  useEffect17(() => {
7741
- if (binaryRef.current) {
7742
- const isInsideTable2 = !!binaryRef.current.closest("table");
7743
- setIsInsideTable(isInsideTable2);
7727
+ setSelectedTags(null);
7728
+ setTagSearch(searchMap);
7729
+ }, [searchMap]);
7730
+ const handleAddTagSearch = (tag) => {
7731
+ const {
7732
+ domain,
7733
+ groupIndex,
7734
+ value,
7735
+ type,
7736
+ title,
7737
+ context,
7738
+ active,
7739
+ dataIndex
7740
+ } = tag;
7741
+ const domainFormat = new domainHelper2.Domain(domain);
7742
+ if (type === SearchType.FILTER) {
7743
+ addSearchItems(`${SearchType.FILTER}_${groupIndex}`, {
7744
+ ...tag,
7745
+ domain: domain ? domainFormat.toList(context) : null
7746
+ });
7747
+ } else if (type === SearchType.SEARCH) {
7748
+ addSearchItems(`${SearchType.SEARCH}_${String(dataIndex)}`, {
7749
+ ...tag,
7750
+ domain: domain ? domainFormat.toList({
7751
+ ...context,
7752
+ self: value
7753
+ }) : null
7754
+ });
7755
+ } else if (type === SearchType.GROUP) {
7756
+ addSearchItems(`${SearchType.GROUP}`, {
7757
+ ...tag,
7758
+ domain: domain ? domainFormat.toList({
7759
+ context,
7760
+ self: value
7761
+ }) : null
7762
+ });
7744
7763
  }
7745
- }, []);
7764
+ };
7746
7765
  return {
7747
- inputId,
7748
- selectedImage,
7749
- initialImage,
7750
- isInsideTable,
7751
- binaryRef,
7752
- handleImageChange,
7753
- handleRemoveImage,
7754
- checkIsImageLink,
7755
- getImageBase64WithMimeType
7766
+ groupBy,
7767
+ searchBy,
7768
+ filterBy,
7769
+ selectedTags,
7770
+ searchString,
7771
+ setFilterBy,
7772
+ setGroupBy,
7773
+ setSearchBy,
7774
+ clearSearch,
7775
+ setSelectedTags,
7776
+ removeSearchItems,
7777
+ onSearchString: onChangeSearchInput,
7778
+ handleAddTagSearch,
7779
+ domain: formatDomain()
7756
7780
  };
7757
7781
  };
7758
7782
 
7759
- // src/utils.ts
7760
- var utils_exports = {};
7761
- __export(utils_exports, {
7762
- API_APP_URL: () => API_APP_URL,
7763
- API_PRESCHOOL_URL: () => API_PRESCHOOL_URL,
7764
- STORAGES: () => STORAGES,
7765
- combineContexts: () => combineContexts,
7766
- convertFieldsToArray: () => convertFieldsToArray,
7767
- countSum: () => countSum,
7768
- getDateRange: () => getDateRange,
7769
- languages: () => languages,
7770
- mergeButtons: () => mergeButtons,
7771
- setStorageItemAsync: () => setStorageItemAsync,
7772
- useGetRowIds: () => useGetRowIds,
7773
- useSelectionState: () => useSelectionState,
7774
- useStorageState: () => useStorageState
7775
- });
7776
- __reExport(utils_exports, utils_star2);
7777
- import * as utils_star2 from "@fctc/interface-logic/utils";
7778
-
7779
7783
  // src/index.ts
7780
7784
  __reExport(index_exports, utils_exports);
7781
7785
  __reExport(index_exports, store_exports);
@@ -7788,13 +7792,6 @@ import * as constants_star from "@fctc/interface-logic/constants";
7788
7792
  // src/index.ts
7789
7793
  __reExport(index_exports, constants_exports);
7790
7794
  __reExport(index_exports, environment_exports);
7791
-
7792
- // src/provider.ts
7793
- var provider_exports = {};
7794
- __reExport(provider_exports, provider_star);
7795
- import * as provider_star from "@fctc/interface-logic/provider";
7796
-
7797
- // src/index.ts
7798
7795
  __reExport(index_exports, provider_exports);
7799
7796
 
7800
7797
  // src/services.ts