@fctc/widget-logic 5.3.7-beta.16 → 5.3.7-beta.17

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/widget.js ADDED
@@ -0,0 +1,2103 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/widget.ts
22
+ var widget_exports = {};
23
+ __export(widget_exports, {
24
+ binaryFieldController: () => binaryFieldController,
25
+ colorFieldController: () => colorFieldController,
26
+ copyLinkButtonController: () => copyLinkButtonController,
27
+ downLoadBinaryController: () => downLoadBinaryController,
28
+ downloadFileController: () => downloadFileController,
29
+ durationController: () => durationController,
30
+ many2manyBinaryController: () => many2manyBinaryController,
31
+ many2manyFieldController: () => many2manyFieldController,
32
+ many2manyTagsController: () => many2manyTagsController,
33
+ many2oneButtonController: () => many2oneButtonController,
34
+ many2oneFieldController: () => many2oneFieldController,
35
+ priorityFieldController: () => priorityFieldController,
36
+ providerEinvoiceFieldController: () => providerEinvoiceFieldController,
37
+ searchController: () => searchController,
38
+ statusDropdownController: () => statusDropdownController,
39
+ tableController: () => tableController,
40
+ tableGroupController: () => tableGroupController,
41
+ tableHeadController: () => tableHeadController
42
+ });
43
+ module.exports = __toCommonJS(widget_exports);
44
+
45
+ // src/widget/basic/status-dropdown-field/controller.ts
46
+ var import_react14 = require("react");
47
+
48
+ // src/environment.ts
49
+ var environment_exports = {};
50
+ __reExport(environment_exports, require("@fctc/interface-logic/environment"));
51
+
52
+ // src/hooks.ts
53
+ var import_hooks2 = require("@fctc/interface-logic/hooks");
54
+
55
+ // src/hooks/core/use-app-provider.tsx
56
+ var import_react8 = require("react");
57
+
58
+ // src/hooks/core/use-menu.ts
59
+ var import_react3 = require("react");
60
+
61
+ // src/hooks/core/use-call-action.ts
62
+ var import_react = require("react");
63
+
64
+ // src/provider.ts
65
+ var provider_exports = {};
66
+ __reExport(provider_exports, require("@fctc/interface-logic/provider"));
67
+
68
+ // src/utils.ts
69
+ var utils_exports = {};
70
+ __export(utils_exports, {
71
+ STORAGES: () => STORAGES,
72
+ countSum: () => countSum,
73
+ guessTypeFromUrl: () => guessTypeFromUrl,
74
+ isObjectEmpty: () => isObjectEmpty,
75
+ languages: () => languages,
76
+ mergeButtons: () => mergeButtons,
77
+ setStorageItemAsync: () => setStorageItemAsync,
78
+ useStorageState: () => useStorageState
79
+ });
80
+
81
+ // src/utils/constants.ts
82
+ var languages = [
83
+ { id: "vi_VN", name: "VIE" },
84
+ { id: "en_US", name: "ENG" }
85
+ ];
86
+ var isBlobUrl = (url) => url.startsWith("blob:");
87
+
88
+ // src/utils/function.ts
89
+ var import_react2 = require("react");
90
+ var countSum = (data, field) => {
91
+ if (!data || !field) return 0;
92
+ return data.reduce(
93
+ (total, item) => total + (item?.[`${field}_count`] || 0),
94
+ 0
95
+ );
96
+ };
97
+ var isObjectEmpty = (obj) => {
98
+ return Object.keys(obj).length === 0;
99
+ };
100
+ function mergeButtons(fields) {
101
+ const buttons = fields?.filter((f) => f.type_co === "button");
102
+ const others = fields?.filter((f) => f.type_co !== "button");
103
+ if (buttons?.length) {
104
+ others.push({
105
+ type_co: "buttons",
106
+ buttons
107
+ });
108
+ }
109
+ return others;
110
+ }
111
+ var STORAGES = {
112
+ TOKEN: "accessToken",
113
+ USER_INFO: "USER_INFO"
114
+ };
115
+ function useAsyncState(initialValue = [true, null]) {
116
+ return (0, import_react2.useReducer)(
117
+ (_state, action = null) => [false, action],
118
+ initialValue
119
+ );
120
+ }
121
+ async function setStorageItemAsync(key, value) {
122
+ try {
123
+ if (value === null) {
124
+ localStorage.removeItem(key);
125
+ } else {
126
+ localStorage.setItem(key, value);
127
+ }
128
+ } catch (e) {
129
+ console.error("Local storage is unavailable:", e);
130
+ }
131
+ }
132
+ function useStorageState(key) {
133
+ const [state, setState] = useAsyncState();
134
+ (0, import_react2.useEffect)(() => {
135
+ try {
136
+ const storedValue = localStorage.getItem(key);
137
+ setState(storedValue);
138
+ } catch (e) {
139
+ console.error("Local storage is unavailable:", e);
140
+ }
141
+ }, [key]);
142
+ const setValue = (0, import_react2.useCallback)(
143
+ (value) => {
144
+ setState(value);
145
+ setStorageItemAsync(key, value);
146
+ },
147
+ [key]
148
+ );
149
+ return [state, setValue];
150
+ }
151
+ var guessTypeFromUrl = (url) => {
152
+ const ext = url.split(".").pop()?.toLowerCase();
153
+ if (!ext) return null;
154
+ const map = {
155
+ jpg: "image/jpeg",
156
+ jpeg: "image/jpeg",
157
+ png: "image/png",
158
+ webp: "image/webp",
159
+ gif: "image/gif",
160
+ svg: "image/svg+xml",
161
+ bmp: "image/bmp",
162
+ tiff: "image/tiff",
163
+ pdf: "application/pdf",
164
+ zip: "application/zip",
165
+ rar: "application/x-rar-compressed",
166
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
167
+ xls: "application/vnd.ms-excel",
168
+ mp4: "video/mp4",
169
+ mov: "video/quicktime"
170
+ };
171
+ return map[ext] || null;
172
+ };
173
+
174
+ // src/utils.ts
175
+ __reExport(utils_exports, require("@fctc/interface-logic/utils"));
176
+
177
+ // src/hooks/core/use-detail.ts
178
+ var import_react_query = require("@tanstack/react-query");
179
+ var import_react4 = require("react");
180
+
181
+ // src/hooks/core/use-profile.ts
182
+ var import_react_query2 = require("@tanstack/react-query");
183
+ var import_react5 = require("react");
184
+
185
+ // src/hooks/core/use-view-v2.ts
186
+ var import_react6 = require("react");
187
+
188
+ // src/hooks/core/use-company.ts
189
+ var import_react_query3 = require("@tanstack/react-query");
190
+ var import_react7 = require("react");
191
+
192
+ // src/hooks/core/use-app-provider.tsx
193
+ var import_jsx_runtime = require("react/jsx-runtime");
194
+ var AppProviderInitialValue = {
195
+ user: {},
196
+ company: {},
197
+ action: {},
198
+ menu: {},
199
+ view: {}
200
+ };
201
+ var ReactContext = (0, import_react8.createContext)(AppProviderInitialValue);
202
+ var useAppProvider = () => {
203
+ const context = (0, import_react8.useContext)(ReactContext);
204
+ if (!context) {
205
+ return AppProviderInitialValue;
206
+ }
207
+ return context;
208
+ };
209
+
210
+ // src/hooks/core/use-config.ts
211
+ var import_react9 = require("react");
212
+
213
+ // src/hooks/core/use-get-specification.ts
214
+ var import_react10 = require("react");
215
+ var useGetSpecification = ({
216
+ model,
217
+ viewData,
218
+ fields
219
+ }) => {
220
+ const baseModel = (0, import_react10.useMemo)(
221
+ () => ({
222
+ name: String(model),
223
+ view: viewData,
224
+ fields
225
+ }),
226
+ [model, viewData, fields]
227
+ );
228
+ const initModel = (0, import_hooks2.useModel)();
229
+ const modelInstance = (0, import_react10.useMemo)(() => {
230
+ if (viewData) {
231
+ return initModel.initModel(baseModel);
232
+ }
233
+ return null;
234
+ }, [baseModel, viewData, model]);
235
+ const specification = (0, import_react10.useMemo)(() => {
236
+ if (modelInstance) {
237
+ return modelInstance.getSpecification();
238
+ }
239
+ return null;
240
+ }, [modelInstance, model]);
241
+ return { specification };
242
+ };
243
+
244
+ // src/hooks/core/use-list-data.ts
245
+ var import_react13 = require("react");
246
+
247
+ // src/hooks/utils/use-debounce.ts
248
+ var import_react11 = require("react");
249
+ function useDebounce(value, delay) {
250
+ const [debouncedValue, setDebouncedValue] = (0, import_react11.useState)(value);
251
+ (0, import_react11.useEffect)(() => {
252
+ const handler = setTimeout(() => {
253
+ setDebouncedValue(value);
254
+ }, delay);
255
+ return () => {
256
+ clearTimeout(handler);
257
+ };
258
+ }, [value, delay]);
259
+ return [debouncedValue];
260
+ }
261
+
262
+ // src/hooks/utils/use-get-rowids.ts
263
+ var import_react12 = require("react");
264
+ var useGetRowIds = (tableRef) => {
265
+ function isElementVisible(el) {
266
+ const style = window.getComputedStyle(el);
267
+ return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
268
+ }
269
+ function arraysAreEqual(a, b) {
270
+ if (a.length !== b.length) return false;
271
+ if (a.length === 0 && b.length === 0) return true;
272
+ const setA = new Set(a);
273
+ const setB = new Set(b);
274
+ if (setA.size !== setB.size) return false;
275
+ for (const val of setA) {
276
+ if (!setB.has(val)) return false;
277
+ }
278
+ return true;
279
+ }
280
+ const [rowIds, setRowIds] = (0, import_react12.useState)([]);
281
+ const lastRowIdsRef = (0, import_react12.useRef)([]);
282
+ const updateVisibleRowIds = (0, import_react12.useCallback)(() => {
283
+ const table = tableRef.current;
284
+ if (!table) return;
285
+ const rows = table.querySelectorAll("tr[data-row-id]");
286
+ const ids = [];
287
+ rows.forEach((row) => {
288
+ const el = row;
289
+ if (isElementVisible(el)) {
290
+ const id = el.getAttribute("data-row-id");
291
+ if (id) ids.push(id);
292
+ }
293
+ });
294
+ const uniqueIds = Array.from(new Set(ids));
295
+ if (!arraysAreEqual(lastRowIdsRef.current, uniqueIds)) {
296
+ lastRowIdsRef.current = uniqueIds;
297
+ setRowIds(uniqueIds);
298
+ }
299
+ }, [tableRef]);
300
+ (0, import_react12.useEffect)(() => {
301
+ const table = tableRef.current;
302
+ if (!table) return;
303
+ const mutationObserver = new MutationObserver(() => {
304
+ updateVisibleRowIds();
305
+ });
306
+ mutationObserver.observe(table, {
307
+ childList: true,
308
+ subtree: true,
309
+ attributes: true,
310
+ attributeFilter: ["style", "class"]
311
+ });
312
+ const resizeObserver = new ResizeObserver(() => {
313
+ updateVisibleRowIds();
314
+ });
315
+ resizeObserver.observe(table);
316
+ const handleScroll = () => updateVisibleRowIds();
317
+ table.addEventListener("scroll", handleScroll, true);
318
+ updateVisibleRowIds();
319
+ return () => {
320
+ mutationObserver.disconnect();
321
+ resizeObserver.disconnect();
322
+ table.removeEventListener("scroll", handleScroll, true);
323
+ };
324
+ }, [updateVisibleRowIds, tableRef?.current]);
325
+ return { rowIds, refresh: updateVisibleRowIds };
326
+ };
327
+
328
+ // src/hooks/core/use-list-data.ts
329
+ var useListData = ({
330
+ action,
331
+ context,
332
+ viewData,
333
+ model,
334
+ service,
335
+ xNode,
336
+ mode,
337
+ limit = 10
338
+ }) => {
339
+ const { useGetListData: useGetListData2 } = (0, provider_exports.useService)();
340
+ const [page, setPage] = (0, import_react13.useState)(0);
341
+ const [pageLimit, setPageLimit] = (0, import_react13.useState)(limit);
342
+ const [groupByList, setGroupByList] = (0, import_react13.useState)(null);
343
+ const [domain, setDomain] = (0, import_react13.useState)(null);
344
+ const [order, setOrder] = (0, import_react13.useState)("");
345
+ const [selectedRowKeys, setSelectedRowKeys] = (0, import_react13.useState)([]);
346
+ const [debouncedPage] = useDebounce(page, 500);
347
+ const [debouncedDomain] = useDebounce(domain, 500);
348
+ const { specification } = useGetSpecification({
349
+ model,
350
+ viewData,
351
+ fields: mode === "kanban" ? viewData?.views?.kanban?.fields : viewData?.views?.list?.fields
352
+ });
353
+ const listDataProps = (0, import_react13.useMemo)(() => {
354
+ if (!viewData || !action || !context) {
355
+ return null;
356
+ }
357
+ const domainParse = domain ? [...domain] : action?.domain ? Array.isArray(action?.domain) ? [...action?.domain] : (0, utils_exports.evalJSONDomain)(action?.domain, context) : [];
358
+ const limit2 = pageLimit;
359
+ const offset = debouncedPage * pageLimit;
360
+ const fields = typeof groupByList === "object" ? groupByList?.fields : void 0;
361
+ const groupby = typeof groupByList === "object" ? [groupByList?.contexts?.[0]?.group_by] : [];
362
+ const sort = order ?? (0, utils_exports.formatSortingString)(
363
+ (mode === "kanban" ? viewData?.views?.kanban : viewData?.views?.list)?.default_order
364
+ ) ?? "";
365
+ return {
366
+ model: action?.res_model,
367
+ specification,
368
+ domain: domainParse,
369
+ limit: limit2,
370
+ offset,
371
+ fields,
372
+ groupby,
373
+ context,
374
+ sort,
375
+ mode
376
+ };
377
+ }, [
378
+ action,
379
+ groupByList,
380
+ order,
381
+ debouncedPage,
382
+ pageLimit,
383
+ debouncedDomain,
384
+ context,
385
+ model
386
+ ]);
387
+ const list = useGetListData2(
388
+ { ...listDataProps },
389
+ [
390
+ listDataProps?.domain,
391
+ listDataProps?.groupby,
392
+ listDataProps?.limit,
393
+ listDataProps?.offset,
394
+ listDataProps?.sort,
395
+ listDataProps?.context,
396
+ listDataProps?.specification,
397
+ listDataProps?.mode
398
+ ],
399
+ !!listDataProps && !!specification && !isObjectEmpty(specification) && !!domain,
400
+ service,
401
+ xNode
402
+ );
403
+ return {
404
+ ...list,
405
+ state: {
406
+ specification,
407
+ page,
408
+ order,
409
+ domain: listDataProps?.domain,
410
+ pageLimit,
411
+ groupByList,
412
+ selectedRowKeys,
413
+ setPage,
414
+ setOrder,
415
+ setDomain,
416
+ setPageLimit,
417
+ setGroupByList,
418
+ setSelectedRowKeys
419
+ }
420
+ };
421
+ };
422
+
423
+ // src/widget/basic/status-dropdown-field/controller.ts
424
+ var statusDropdownController = (props) => {
425
+ const { selection, isForm, id, model, name, state, onRefetch } = props;
426
+ const env = (0, environment_exports.getEnv)();
427
+ const colors = {
428
+ normal: "bg-[#e9ecef]",
429
+ done: "bg-primary",
430
+ blocked: "bg-red-500"
431
+ };
432
+ const [isOpen, setIsOpen] = (0, import_react14.useState)(false);
433
+ const buttonRef = (0, import_react14.useRef)(null);
434
+ (0, import_react14.useEffect)(() => {
435
+ const handleClickOutside = (event) => {
436
+ if (buttonRef.current && !buttonRef.current.contains(event.target)) {
437
+ setIsOpen(false);
438
+ }
439
+ };
440
+ document.addEventListener("mousedown", handleClickOutside);
441
+ return () => {
442
+ document.removeEventListener("mousedown", handleClickOutside);
443
+ };
444
+ }, []);
445
+ const { mutate: onSave } = (0, import_hooks2.useSave)();
446
+ const handleClick = async (status) => {
447
+ setIsOpen(!isOpen);
448
+ onSave(
449
+ {
450
+ ids: id ? [id] : [],
451
+ model: model ?? "",
452
+ data: { [name ?? ""]: status },
453
+ context: env.context
454
+ },
455
+ {
456
+ onSuccess: () => {
457
+ onRefetch && onRefetch();
458
+ }
459
+ }
460
+ );
461
+ };
462
+ return {
463
+ handleClick,
464
+ buttonRef,
465
+ isForm,
466
+ setIsOpen,
467
+ isOpen,
468
+ selection,
469
+ state,
470
+ colors
471
+ };
472
+ };
473
+
474
+ // src/widget/basic/many2one-field/controller.ts
475
+ var import_react15 = require("react");
476
+ var MANY2ONE_EXTERNAL = "many2one_external";
477
+ var many2oneFieldController = (props) => {
478
+ const {
479
+ methods,
480
+ relation,
481
+ domain,
482
+ formValues,
483
+ value: propValue,
484
+ onChange,
485
+ name,
486
+ context: fieldContext,
487
+ options: fieldOptions,
488
+ showDetail,
489
+ service,
490
+ xNode,
491
+ isForm,
492
+ widget,
493
+ in_list_view,
494
+ isEditTable
495
+ } = props;
496
+ const { env } = (0, provider_exports.useEnv)();
497
+ const { action } = useAppProvider();
498
+ const { useGetSelection: useGetSelection2, useGetDetail: useGetDetail2 } = (0, provider_exports.useService)();
499
+ const [listOptions, setListOptions] = (0, import_react15.useState)([]);
500
+ const [inputValue, setInputValue] = (0, import_react15.useState)("");
501
+ const [debouncedInputValue] = useDebounce(inputValue, 1e3);
502
+ const [isShowModalMany2Many, setIsShowModalMany2Many] = (0, import_react15.useState)(false);
503
+ const [tempSelectedOption, setTempSelectedOption] = (0, import_react15.useState)(null);
504
+ const [domainModal, setDomainModal] = (0, import_react15.useState)(null);
505
+ const [domainObject, setDomainObject] = (0, import_react15.useState)(null);
506
+ const initValue = methods?.getValues(name);
507
+ const contextObject = {
508
+ ...(typeof action?.context === "string" ? (0, utils_exports.evalJSONContext)(action?.context) : action?.context) || {},
509
+ ...fieldContext,
510
+ ...env?.context
511
+ };
512
+ const optionsObject = typeof fieldOptions === "string" ? (0, utils_exports.evalJSONContext)(fieldOptions, {
513
+ ...formValues,
514
+ ...contextObject,
515
+ context: contextObject,
516
+ parent: { ...formValues }
517
+ }) : fieldOptions;
518
+ const fetchGetDetail = useGetDetail2();
519
+ const data = {
520
+ model: widget === MANY2ONE_EXTERNAL ? optionsObject?.model : relation,
521
+ domain: widget === MANY2ONE_EXTERNAL ? optionsObject?.domain : domainObject,
522
+ context: {
523
+ ...contextObject,
524
+ ...optionsObject?.context
525
+ },
526
+ specification: widget === MANY2ONE_EXTERNAL ? optionsObject?.specification : {
527
+ id: {},
528
+ name: {},
529
+ display_name: {}
530
+ }
531
+ };
532
+ const {
533
+ data: dataOfSelection,
534
+ refetch,
535
+ isFetching
536
+ } = useGetSelection2({
537
+ data,
538
+ queryKey: [`data_${relation}`, domainObject],
539
+ enabled: false,
540
+ service: widget === MANY2ONE_EXTERNAL ? optionsObject?.service : service,
541
+ xNode
542
+ });
543
+ const selectOptions = (0, import_react15.useMemo)(() => {
544
+ return dataOfSelection?.records?.map((val) => ({
545
+ value: val?.id,
546
+ label: val?.display_name || val?.name,
547
+ ...widget === MANY2ONE_EXTERNAL ? val : {}
548
+ })) || [];
549
+ }, [dataOfSelection]);
550
+ (0, import_react15.useEffect)(() => {
551
+ setListOptions(selectOptions);
552
+ setDomainModal(domainObject);
553
+ }, [selectOptions]);
554
+ const parsedFormValues = (0, import_react15.useMemo)(
555
+ () => JSON.parse(
556
+ JSON.stringify({
557
+ ...formValues,
558
+ ...contextObject,
559
+ context: contextObject
560
+ })
561
+ ) ?? {},
562
+ [formValues, contextObject]
563
+ );
564
+ (0, import_react15.useEffect)(() => {
565
+ const newDomain = (0, utils_exports.evalJSONDomain)(domain, parsedFormValues);
566
+ const parsedDomain = typeof newDomain === "string" ? JSON.parse(
567
+ newDomain.replace(/\(/g, "[").replace(/\)/g, "]").replace(/'/g, '"')
568
+ ) : newDomain;
569
+ setDomainObject((prev) => {
570
+ const prevStr = JSON.stringify(prev);
571
+ const nextStr = JSON.stringify(parsedDomain);
572
+ return prevStr === nextStr ? prev : parsedDomain;
573
+ });
574
+ }, [domain, parsedFormValues]);
575
+ (0, import_react15.useEffect)(() => {
576
+ if (!propValue && tempSelectedOption) {
577
+ methods?.setValue(name, null, { shouldDirty: true });
578
+ setTempSelectedOption(null);
579
+ } else if (propValue) {
580
+ if (isForm && !isEditTable && optionsObject?.service && optionsObject?.model && !propValue?.display_name && !in_list_view) {
581
+ fetchGetDetail.mutate(
582
+ {
583
+ model: optionsObject?.model,
584
+ ids: propValue?.id ?? propValue,
585
+ specification: widget === MANY2ONE_EXTERNAL ? optionsObject?.specification : {
586
+ id: {},
587
+ display_name: {}
588
+ },
589
+ context: { ...env.context },
590
+ service: optionsObject ? optionsObject?.service : service,
591
+ xNode
592
+ },
593
+ {
594
+ onSuccess: (dataResponse) => {
595
+ const detailData = dataResponse?.[0];
596
+ setTempSelectedOption({
597
+ value: detailData?.id,
598
+ label: detailData?.display_name
599
+ });
600
+ if (widget === MANY2ONE_EXTERNAL && optionsObject?.values_included) {
601
+ Object.keys(optionsObject?.values_included)?.forEach(
602
+ (field) => {
603
+ methods?.setValue(
604
+ optionsObject?.values_included[field]?.name,
605
+ detailData?.[field]
606
+ );
607
+ }
608
+ );
609
+ methods?.setValue(name, detailData);
610
+ }
611
+ },
612
+ onError: (error) => {
613
+ console.log("error", error);
614
+ }
615
+ }
616
+ );
617
+ return;
618
+ }
619
+ setTempSelectedOption({
620
+ value: propValue?.id,
621
+ label: propValue?.display_name
622
+ });
623
+ }
624
+ }, [propValue]);
625
+ const fetchMoreOptions = (0, import_react15.useCallback)(() => {
626
+ refetch();
627
+ }, [refetch]);
628
+ (0, import_react15.useEffect)(() => {
629
+ if (debouncedInputValue) {
630
+ const filteredDomain = [...domainObject ?? []]?.filter(
631
+ (d) => !(Array.isArray(d) && d[0] === "name" && d[1] === "ilike")
632
+ ) || [];
633
+ const newDomain = [
634
+ ...filteredDomain,
635
+ ...debouncedInputValue ? [["name", "ilike", debouncedInputValue]] : []
636
+ ];
637
+ setDomainObject(newDomain);
638
+ setTimeout(() => {
639
+ fetchMoreOptions();
640
+ }, 50);
641
+ }
642
+ }, [debouncedInputValue]);
643
+ const handleChooseRecord = (0, import_react15.useCallback)(
644
+ (idRecord) => {
645
+ const newOption = listOptions?.find(
646
+ (option) => option.value === idRecord
647
+ );
648
+ const newValue = widget === MANY2ONE_EXTERNAL && optionsObject?.field_name ? newOption?.[optionsObject?.field_name] : newOption.value;
649
+ if (widget === MANY2ONE_EXTERNAL && optionsObject?.values_included) {
650
+ Object.keys(optionsObject?.values_included)?.forEach((field) => {
651
+ methods?.setValue(
652
+ optionsObject?.values_included[field]?.name,
653
+ newOption?.[field],
654
+ { shouldDirty: true }
655
+ );
656
+ });
657
+ }
658
+ if (newOption) {
659
+ methods?.setValue(
660
+ name,
661
+ {
662
+ ...newOption,
663
+ id: newValue,
664
+ display_name: newOption?.label
665
+ },
666
+ { shouldDirty: true }
667
+ );
668
+ setTempSelectedOption(newOption);
669
+ onChange && onChange(String(name), {
670
+ ...newOption,
671
+ id: newValue,
672
+ display_name: newOption?.label
673
+ });
674
+ methods.trigger(name);
675
+ }
676
+ setIsShowModalMany2Many(false);
677
+ },
678
+ [listOptions, methods, name, onChange]
679
+ );
680
+ const handleClose = (0, import_react15.useCallback)(() => setIsShowModalMany2Many(false), []);
681
+ const handleSelectChange = (0, import_react15.useCallback)(
682
+ (selectedOption) => {
683
+ if (!selectedOption) {
684
+ if (widget === MANY2ONE_EXTERNAL && optionsObject?.values_included) {
685
+ Object.keys(optionsObject?.values_included)?.forEach((field) => {
686
+ methods?.setValue(
687
+ optionsObject?.values_included[field]?.name,
688
+ null,
689
+ { shouldDirty: true }
690
+ );
691
+ });
692
+ }
693
+ methods.setValue(name, null, { shouldDirty: true });
694
+ setTempSelectedOption(null);
695
+ onChange && onChange(String(name), null);
696
+ methods.trigger(name);
697
+ return;
698
+ }
699
+ const newValue = widget === MANY2ONE_EXTERNAL && optionsObject?.field_name ? selectedOption?.[optionsObject?.field_name] : selectedOption.value;
700
+ if (widget === MANY2ONE_EXTERNAL && optionsObject?.values_included) {
701
+ Object.keys(optionsObject?.values_included)?.forEach((field) => {
702
+ methods?.setValue(
703
+ optionsObject?.values_included[field]?.name,
704
+ selectedOption?.[field],
705
+ { shouldDirty: true }
706
+ );
707
+ });
708
+ }
709
+ methods?.setValue(
710
+ name,
711
+ {
712
+ id: newValue,
713
+ display_name: selectedOption?.label
714
+ },
715
+ { shouldDirty: true }
716
+ );
717
+ setTempSelectedOption(selectedOption);
718
+ onChange && onChange(String(name), {
719
+ id: newValue,
720
+ display_name: selectedOption.label
721
+ });
722
+ methods.trigger(name);
723
+ },
724
+ [methods, name, onChange]
725
+ );
726
+ const allowShowDetail = showDetail && contextObject?.form_view_ref && (!optionsObject || !("no_open" in optionsObject) || optionsObject.no_open === false);
727
+ return {
728
+ isShowModalMany2Many,
729
+ isFetching,
730
+ initValue,
731
+ handleChooseRecord,
732
+ handleClose,
733
+ handleSelectChange,
734
+ domainModal,
735
+ setInputValue,
736
+ allowShowDetail,
737
+ contextObject: {
738
+ ...contextObject,
739
+ ...optionsObject?.context
740
+ },
741
+ tempSelectedOption,
742
+ listOptions,
743
+ fetchMoreOptions,
744
+ domainObject: widget === MANY2ONE_EXTERNAL ? optionsObject?.domain : domainObject,
745
+ setIsShowModalMany2Many,
746
+ setDomainObject,
747
+ options: optionsObject,
748
+ relation: widget === MANY2ONE_EXTERNAL ? optionsObject?.model : relation,
749
+ service: widget === MANY2ONE_EXTERNAL ? optionsObject?.service : service
750
+ };
751
+ };
752
+
753
+ // src/widget/basic/many2one-button-field/controller.ts
754
+ var many2oneButtonController = (props) => {
755
+ const { domain, methods, relation, service, xNode } = props;
756
+ const actionDataString = sessionStorage.getItem("actionData");
757
+ const env = (0, environment_exports.getEnv)();
758
+ const domainObject = (0, utils_exports.evalJSONDomain)(domain, methods?.getValues() || {});
759
+ const actionData = actionDataString && actionDataString !== "undefined" ? JSON.parse(actionDataString) : {};
760
+ const { data: dataOfSelection } = (0, import_hooks2.useGetSelection)({
761
+ data: {
762
+ model: relation ?? "",
763
+ domain: domainObject,
764
+ context: { ...env.context, ...(0, utils_exports.evalJSONContext)(actionData?.context) }
765
+ },
766
+ queryKey: [`data_${relation}`, domainObject],
767
+ service,
768
+ xNode
769
+ });
770
+ const options = dataOfSelection?.records?.map((val) => ({
771
+ value: val.id,
772
+ label: val.name
773
+ })) || [];
774
+ return {
775
+ options
776
+ };
777
+ };
778
+
779
+ // src/widget/basic/many2many-field/controller.ts
780
+ var import_react16 = require("react");
781
+ var many2manyFieldController = (props) => {
782
+ const {
783
+ relation,
784
+ domain,
785
+ context,
786
+ options,
787
+ enabled: enabledCallAPI,
788
+ service,
789
+ validateAndParseDate,
790
+ moment
791
+ } = props;
792
+ const { env } = (0, provider_exports.useEnv)();
793
+ const { user } = useAppProvider();
794
+ const { useGetView: useGetView2 } = (0, provider_exports.useService)();
795
+ const dataUser = user?.userProfile?.data;
796
+ const contextObject = (0, import_react16.useMemo)(
797
+ () => ({
798
+ ...env.context,
799
+ ...context || {}
800
+ }),
801
+ [env?.context, context]
802
+ );
803
+ const viewParams = (0, import_react16.useMemo)(
804
+ () => ({
805
+ model: relation,
806
+ views: [
807
+ [false, "list"],
808
+ [false, "search"]
809
+ ],
810
+ context: contextObject,
811
+ service,
812
+ xNode: service == "wesap" && dataUser?.x_node
813
+ }),
814
+ [relation, contextObject, service, dataUser?.x_node]
815
+ );
816
+ const { data: viewResponse } = useGetView2({
817
+ viewParams,
818
+ enabled: enabledCallAPI
819
+ });
820
+ const default_order = viewResponse && viewResponse?.views?.list?.default_order;
821
+ const optionsObject = (0, import_react16.useMemo)(
822
+ () => (options && typeof options === "string" ? (0, utils_exports.evalJSONContext)(options) : options) || {},
823
+ [options]
824
+ );
825
+ const {
826
+ data: dataResponse,
827
+ isFetched,
828
+ isLoading,
829
+ state,
830
+ isPlaceholderData
831
+ } = useListData({
832
+ action: {
833
+ domain,
834
+ res_model: relation
835
+ },
836
+ context: contextObject,
837
+ model: relation ?? "",
838
+ viewData: viewResponse,
839
+ service,
840
+ xNode: service == "wesap" && dataUser?.x_node
841
+ });
842
+ const {
843
+ selectedRowKeys,
844
+ groupByList,
845
+ domain: domainList,
846
+ page,
847
+ pageLimit,
848
+ setDomain,
849
+ setOrder,
850
+ setPage,
851
+ setSelectedRowKeys,
852
+ setGroupByList,
853
+ setPageLimit,
854
+ specification
855
+ } = state;
856
+ (0, import_react16.useEffect)(() => {
857
+ return () => {
858
+ setDomain(null);
859
+ setOrder("");
860
+ setGroupByList(null);
861
+ setPageLimit(10);
862
+ };
863
+ }, []);
864
+ const { rows, columns, typeTable, onToggleColumnOptional } = tableController({
865
+ data: {
866
+ fields: viewResponse?.views?.list?.fields,
867
+ records: dataResponse?.records ?? dataResponse?.groups,
868
+ dataModel: viewResponse?.models?.[String(relation)],
869
+ context: contextObject,
870
+ typeTable: dataResponse?.groups ? "group" : "list"
871
+ }
872
+ });
873
+ const searchControllers = searchController({
874
+ viewData: viewResponse,
875
+ model: relation ?? "",
876
+ context: contextObject,
877
+ domain,
878
+ fieldsList: [
879
+ ...columns?.filter(
880
+ (col) => col?.field?.type_co === "field" && col?.optional !== "hide"
881
+ )?.map((col) => ({ ...col.field })) ?? []
882
+ ],
883
+ validateAndParseDate,
884
+ moment
885
+ });
886
+ return {
887
+ rows,
888
+ columns,
889
+ optionsObject,
890
+ viewData: viewResponse,
891
+ totalRows: dataResponse?.length ?? 0,
892
+ onToggleColumnOptional,
893
+ typeTable,
894
+ isLoading,
895
+ isFetched,
896
+ isPlaceholderData,
897
+ page,
898
+ pageLimit,
899
+ groupByList,
900
+ selectedRowKeys,
901
+ domain: domainList,
902
+ setPage,
903
+ setDomain,
904
+ setPageLimit,
905
+ setGroupByList,
906
+ setSelectedRowKeys,
907
+ searchController: searchControllers,
908
+ specification
909
+ };
910
+ };
911
+
912
+ // src/widget/basic/many2many-tags-field/controller.ts
913
+ var import_react17 = require("react");
914
+
915
+ // src/constants.ts
916
+ var constants_exports = {};
917
+ __reExport(constants_exports, require("@fctc/interface-logic/constants"));
918
+
919
+ // src/widget/basic/many2many-tags-field/controller.ts
920
+ var many2manyTagsController = (props) => {
921
+ const {
922
+ relation,
923
+ domain,
924
+ options: optionsFields,
925
+ widget,
926
+ formValues,
927
+ service,
928
+ xNode,
929
+ context: fieldContext,
930
+ onChange,
931
+ methods,
932
+ name
933
+ } = props;
934
+ const isUser = relation === "res.users" || relation === "res.partner";
935
+ const { env } = (0, provider_exports.useEnv)();
936
+ const { action } = useAppProvider();
937
+ const { useGetSelection: useGetSelection2 } = (0, provider_exports.useService)();
938
+ const [options, setOptions] = (0, import_react17.useState)([]);
939
+ const [domainObject, setDomainObject] = (0, import_react17.useState)(null);
940
+ const [isShowModalMany2Many, setIsShowModalMany2Many] = (0, import_react17.useState)(false);
941
+ const addtionalFields = optionsFields ? (0, utils_exports.evalJSONContext)(optionsFields) : null;
942
+ const contextObject = {
943
+ ...(0, utils_exports.evalJSONContext)(action?.context) || {},
944
+ ...fieldContext ?? {},
945
+ ...env?.context
946
+ };
947
+ const parsedFormValues = (0, import_react17.useMemo)(
948
+ () => JSON.parse(
949
+ JSON.stringify({
950
+ ...formValues,
951
+ ...contextObject,
952
+ context: contextObject,
953
+ parent: { ...formValues }
954
+ })
955
+ ) ?? {},
956
+ [formValues, contextObject]
957
+ );
958
+ (0, import_react17.useEffect)(() => {
959
+ const newDomain = (0, utils_exports.evalJSONDomain)(domain, parsedFormValues);
960
+ setDomainObject(
961
+ (prev) => JSON.stringify(prev) === JSON.stringify(newDomain) ? prev : newDomain
962
+ );
963
+ }, [domain, parsedFormValues]);
964
+ const data = {
965
+ model: relation ?? "",
966
+ domain: domainObject,
967
+ specification: {
968
+ id: {},
969
+ name: {},
970
+ display_name: {},
971
+ ...widget && constants_exports.WIDGETAVATAR[widget] ? { image_256: {} } : {},
972
+ ...widget && constants_exports.WIDGETCOLOR[widget] && addtionalFields?.color_field ? { color: {} } : {}
973
+ },
974
+ context: env.context
975
+ };
976
+ const queryKey = [`data_${relation}`, domainObject];
977
+ const {
978
+ data: dataOfSelection,
979
+ refetch,
980
+ isFetching
981
+ } = useGetSelection2({
982
+ data,
983
+ queryKey,
984
+ service,
985
+ xNode,
986
+ enabled: false
987
+ });
988
+ const selectOptions = (0, import_react17.useMemo)(() => {
989
+ return dataOfSelection?.records?.map((val) => ({
990
+ value: val.id,
991
+ label: val.name ?? val.display_name,
992
+ ...val
993
+ })) || [];
994
+ }, [dataOfSelection]);
995
+ (0, import_react17.useEffect)(() => {
996
+ setOptions(selectOptions);
997
+ }, [selectOptions]);
998
+ const fetchMoreOptions = (0, import_react17.useCallback)(() => {
999
+ refetch();
1000
+ }, [refetch]);
1001
+ const transfer = (data2) => {
1002
+ return data2?.map((val) => ({
1003
+ id: val.value,
1004
+ display_name: val.label
1005
+ })) || [];
1006
+ };
1007
+ const handleChooseRecord = (0, import_react17.useCallback)(
1008
+ (idRecord) => {
1009
+ const newOption = options.find(
1010
+ (option) => option.value === idRecord
1011
+ );
1012
+ setIsShowModalMany2Many(false);
1013
+ },
1014
+ [options, methods, name, onChange]
1015
+ );
1016
+ const handleClose = (0, import_react17.useCallback)(() => setIsShowModalMany2Many(false), []);
1017
+ return {
1018
+ options,
1019
+ transfer,
1020
+ isUser,
1021
+ isFetching,
1022
+ fetchMoreOptions,
1023
+ domainObject,
1024
+ setDomainObject,
1025
+ handleChooseRecord,
1026
+ handleClose,
1027
+ isShowModalMany2Many,
1028
+ setIsShowModalMany2Many
1029
+ };
1030
+ };
1031
+
1032
+ // src/widget/basic/status-bar-field/controller.ts
1033
+ var import_react18 = require("react");
1034
+ var durationController = (props) => {
1035
+ const { relation, domain, formValues, name, id, model, onRefetch, enabled } = props;
1036
+ const specification = {
1037
+ id: 0,
1038
+ name: "",
1039
+ fold: ""
1040
+ };
1041
+ const { useGetListData: useGetListData2, useChangeStatus: useChangeStatus2 } = (0, provider_exports.useService)();
1042
+ const { env } = (0, provider_exports.useEnv)();
1043
+ const [disabled, setDisabled] = (0, import_react18.useState)(false);
1044
+ const [modelStatus, setModalStatus] = (0, import_react18.useState)(false);
1045
+ const queryKey = [`data-status-duration`, specification];
1046
+ const listDataProps = {
1047
+ model: relation,
1048
+ specification,
1049
+ domain: (0, utils_exports.evalJSONDomain)(domain, JSON.parse(JSON.stringify(formValues))),
1050
+ limit: 10,
1051
+ offset: 0,
1052
+ fields: "",
1053
+ groupby: [],
1054
+ context: {
1055
+ lang: env.context.lang
1056
+ },
1057
+ sort: ""
1058
+ };
1059
+ const { data: dataResponse } = useGetListData2(
1060
+ listDataProps,
1061
+ queryKey,
1062
+ enabled
1063
+ );
1064
+ const { mutate: fetchChangeStatus } = useChangeStatus2();
1065
+ const handleClick = async (stage_id) => {
1066
+ setDisabled(true);
1067
+ if (stage_id) {
1068
+ fetchChangeStatus(
1069
+ {
1070
+ data: {
1071
+ stage_id,
1072
+ name,
1073
+ id,
1074
+ model,
1075
+ lang: env.context.lang
1076
+ }
1077
+ },
1078
+ {
1079
+ onSuccess: (res) => {
1080
+ if (res) {
1081
+ setDisabled(false);
1082
+ onRefetch && onRefetch();
1083
+ }
1084
+ }
1085
+ }
1086
+ );
1087
+ }
1088
+ };
1089
+ return {
1090
+ dataResponse,
1091
+ handleClick,
1092
+ disabled,
1093
+ modelStatus,
1094
+ setModalStatus
1095
+ };
1096
+ };
1097
+
1098
+ // src/widget/basic/priority-field/controller.ts
1099
+ var priorityFieldController = (props) => {
1100
+ const { name, model, index, actionData, context, onChange, specification } = props;
1101
+ const _context = { ...(0, utils_exports.evalJSONContext)(actionData?.context) };
1102
+ const contextObject = { ...context, ..._context };
1103
+ const { useSave: useSave2 } = (0, provider_exports.useService)();
1104
+ const { mutateAsync: fetchSave } = useSave2();
1105
+ const savePriorities = async ({
1106
+ value,
1107
+ resetPriority
1108
+ }) => {
1109
+ const priorityValue = value <= 0 ? 0 : value - 1;
1110
+ try {
1111
+ fetchSave({
1112
+ ids: index ? [index] : [],
1113
+ data: { [String(name)]: String(priorityValue) },
1114
+ model: String(model),
1115
+ context: contextObject,
1116
+ specification
1117
+ });
1118
+ if (typeof onChange === "function") {
1119
+ onChange(String(name), String(priorityValue));
1120
+ }
1121
+ } catch (error) {
1122
+ if (resetPriority) {
1123
+ resetPriority();
1124
+ }
1125
+ }
1126
+ };
1127
+ return {
1128
+ savePriorities
1129
+ };
1130
+ };
1131
+
1132
+ // src/widget/basic/download-file-field/controller.ts
1133
+ var import_react19 = require("react");
1134
+ var downloadFileController = () => {
1135
+ const inputId = (0, import_react19.useId)();
1136
+ const [file, setFile] = (0, import_react19.useState)(null);
1137
+ const handleFileChange = (e) => {
1138
+ setFile(e.target.files[0]);
1139
+ };
1140
+ const handleFileDownload = () => {
1141
+ const url = URL.createObjectURL(file);
1142
+ const link = document.createElement("a");
1143
+ link.href = url;
1144
+ link.download = file.name;
1145
+ document.body.appendChild(link);
1146
+ link.click();
1147
+ document.body.removeChild(link);
1148
+ };
1149
+ return {
1150
+ inputId,
1151
+ file,
1152
+ handleFileChange,
1153
+ handleFileDownload
1154
+ };
1155
+ };
1156
+
1157
+ // src/widget/basic/download-binary-field/controller.ts
1158
+ var downLoadBinaryController = (props) => {
1159
+ const { value, defaultValue, formValues } = props;
1160
+ const downloadFile = async (url, filename) => {
1161
+ try {
1162
+ const response = await fetch(url);
1163
+ if (!response.ok) throw new Error(`Failed to fetch ${url}`);
1164
+ const contentType = response.headers.get("Content-Type") || "";
1165
+ let ext = "";
1166
+ if (contentType.includes("pdf")) ext = ".pdf";
1167
+ else if (contentType.includes("png")) ext = ".png";
1168
+ else if (contentType.includes("jpeg") || contentType.includes("jpg"))
1169
+ ext = ".jpg";
1170
+ else if (contentType.includes("zip")) ext = ".zip";
1171
+ else if (contentType.includes("msword")) ext = ".doc";
1172
+ else if (contentType.includes("spreadsheet")) ext = ".xls";
1173
+ else if (contentType.includes("json")) ext = ".json";
1174
+ else if (contentType.includes("text")) ext = ".txt";
1175
+ else {
1176
+ ext = "";
1177
+ }
1178
+ const blob = await response.blob();
1179
+ const urlBlob = window.URL.createObjectURL(blob);
1180
+ const link = document.createElement("a");
1181
+ link.href = urlBlob;
1182
+ link.download = (filename || "file") + ext;
1183
+ document.body.appendChild(link);
1184
+ link.click();
1185
+ document.body.removeChild(link);
1186
+ window.URL.revokeObjectURL(urlBlob);
1187
+ } catch (error) {
1188
+ console.error("File download failed:", error);
1189
+ }
1190
+ };
1191
+ const handleFileDownload = async (e) => {
1192
+ e.stopPropagation();
1193
+ await downloadFile(value || defaultValue, formValues?.name);
1194
+ };
1195
+ return {
1196
+ handleFileDownload
1197
+ };
1198
+ };
1199
+
1200
+ // src/widget/basic/copy-link-button/controller.ts
1201
+ var import_react20 = require("react");
1202
+ var copyTextToClipboard = async (text) => {
1203
+ if ("clipboard" in navigator) {
1204
+ return await navigator.clipboard.writeText(text);
1205
+ } else {
1206
+ const textArea = document.createElement("textarea");
1207
+ textArea.value = text;
1208
+ textArea.style.position = "fixed";
1209
+ document.body.appendChild(textArea);
1210
+ textArea.focus();
1211
+ textArea.select();
1212
+ try {
1213
+ document.execCommand("copy");
1214
+ } finally {
1215
+ document.body.removeChild(textArea);
1216
+ }
1217
+ }
1218
+ };
1219
+ var copyLinkButtonController = (props) => {
1220
+ const { value, defaultValue } = props;
1221
+ const [isCopied, setIsCopied] = (0, import_react20.useState)(false);
1222
+ const handleCopyToClipboard = async (value2) => {
1223
+ await copyTextToClipboard(value2);
1224
+ setIsCopied(true);
1225
+ setTimeout(() => setIsCopied(false), 2e3);
1226
+ };
1227
+ const propValue = value || defaultValue;
1228
+ return {
1229
+ isCopied,
1230
+ handleCopyToClipboard,
1231
+ propValue
1232
+ };
1233
+ };
1234
+
1235
+ // src/widget/basic/color-field/color-controller.ts
1236
+ var colorFieldController = (props) => {
1237
+ const { value, isForm, name, formValues, idForm, model, actionData } = props;
1238
+ const { env } = (0, provider_exports.useEnv)();
1239
+ const { useSave: useSave2 } = (0, provider_exports.useService)();
1240
+ const _context = { ...(0, utils_exports.evalJSONContext)(actionData?.context) || {} };
1241
+ const contextObject = { ...env.context, ..._context };
1242
+ const idDefault = isForm ? idForm : formValues?.id;
1243
+ const { mutate: onSave } = useSave2();
1244
+ const savePickColor = async (colorObject) => {
1245
+ const { id } = colorObject;
1246
+ if (value === id) return;
1247
+ try {
1248
+ onSave({
1249
+ ids: idDefault !== null ? [idDefault] : [],
1250
+ model: String(model),
1251
+ data: { [String(name)]: id },
1252
+ specification: {
1253
+ name: {},
1254
+ color: {}
1255
+ },
1256
+ context: contextObject
1257
+ });
1258
+ } catch (error) {
1259
+ console.log(error);
1260
+ }
1261
+ };
1262
+ return {
1263
+ savePickColor
1264
+ };
1265
+ };
1266
+
1267
+ // src/widget/basic/binary-field/controller.ts
1268
+ var import_react21 = require("react");
1269
+ var binaryFieldController = (props) => {
1270
+ const {
1271
+ name,
1272
+ methods,
1273
+ readonly = false,
1274
+ filename,
1275
+ onChange: handleOnchange,
1276
+ service,
1277
+ xNode,
1278
+ path,
1279
+ rootField,
1280
+ index,
1281
+ value
1282
+ } = props;
1283
+ const { useUploadFile: useUploadFile2 } = (0, provider_exports.useService)();
1284
+ const { mutateAsync } = useUploadFile2();
1285
+ const [url, setUrl] = (0, import_react21.useState)(value || null);
1286
+ const [fileInfo, setFileInfo] = (0, import_react21.useState)(null);
1287
+ (0, import_react21.useEffect)(() => {
1288
+ if (!value) {
1289
+ setUrl(null);
1290
+ setFileInfo(null);
1291
+ return;
1292
+ }
1293
+ fetchFileMeta(value);
1294
+ }, [value]);
1295
+ const formatSize = (bytes) => {
1296
+ if (bytes < 1024) return bytes + " B";
1297
+ let kb = bytes / 1024;
1298
+ if (kb < 1024) return kb.toFixed(2) + " KB";
1299
+ let mb = kb / 1024;
1300
+ if (mb < 1024) return mb.toFixed(2) + " MB";
1301
+ return (mb / 1024).toFixed(2) + " GB";
1302
+ };
1303
+ const fetchFileMeta = async (url2) => {
1304
+ try {
1305
+ const res = await fetch(url2, { method: "HEAD" });
1306
+ const size = res.headers.get("content-length") || "";
1307
+ let type = res.headers.get("content-type") || "";
1308
+ const date = res.headers.get("last-modified") || "";
1309
+ const guessed = guessTypeFromUrl(url2);
1310
+ if (guessed) type = guessed;
1311
+ setFileInfo({
1312
+ size: size ? formatSize(Number(size)) : "--",
1313
+ type,
1314
+ date: date ? new Date(date).toLocaleString() : "--"
1315
+ });
1316
+ setUrl(url2);
1317
+ } catch (e) {
1318
+ console.error(e);
1319
+ }
1320
+ };
1321
+ const onUploadFile = async (formData) => {
1322
+ const res = await mutateAsync({
1323
+ formData,
1324
+ service,
1325
+ xNode,
1326
+ path
1327
+ });
1328
+ const url2 = res?.url;
1329
+ methods?.setValue(name, url2, { shouldDirty: true });
1330
+ handleOnchange && handleOnchange(name ?? "", url2);
1331
+ if (filename) {
1332
+ methods?.setValue(
1333
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
1334
+ url2?.split("/").pop(),
1335
+ { shouldDirty: true }
1336
+ );
1337
+ handleOnchange && handleOnchange(
1338
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
1339
+ url2?.split("/").pop()
1340
+ );
1341
+ }
1342
+ setUrl(url2);
1343
+ fetchFileMeta(url2);
1344
+ return url2;
1345
+ };
1346
+ const onDeleteFile = () => {
1347
+ if (filename) {
1348
+ methods?.setValue(
1349
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
1350
+ null,
1351
+ { shouldDirty: true }
1352
+ );
1353
+ handleOnchange && handleOnchange(
1354
+ rootField ? `${rootField?.name}.${index}.${filename}` : filename,
1355
+ null
1356
+ );
1357
+ }
1358
+ setUrl(null);
1359
+ setFileInfo(null);
1360
+ methods?.setValue(name, null, { shouldDirty: true });
1361
+ handleOnchange && handleOnchange(name ?? "", null);
1362
+ };
1363
+ return {
1364
+ onUploadFile,
1365
+ onDeleteFile,
1366
+ fileInfo,
1367
+ url
1368
+ };
1369
+ };
1370
+
1371
+ // src/widget/basic/many2many-binary-field/controller.tsx
1372
+ var import_react22 = require("react");
1373
+ var many2manyBinaryController = (props) => {
1374
+ const {
1375
+ name,
1376
+ methods,
1377
+ value,
1378
+ onChange: handleOnchange,
1379
+ service,
1380
+ xNode,
1381
+ path
1382
+ } = props;
1383
+ const inputId = (0, import_react22.useId)();
1384
+ const { useUploadFile: useUploadFile2 } = (0, provider_exports.useService)();
1385
+ const { mutateAsync } = useUploadFile2();
1386
+ const binaryRef = (0, import_react22.useRef)(null);
1387
+ const [initialFiles, setInitialFiles] = (0, import_react22.useState)(
1388
+ Array.isArray(value) ? value : value ? [value] : []
1389
+ );
1390
+ const checkIsImageLink = (url) => {
1391
+ const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff|ico)$/i;
1392
+ return imageExtensions.test(url) || isBase64Image(url) || isBlobUrl(url);
1393
+ };
1394
+ const sanitizeForBE = (list) => list.filter((x) => x?.datas && !isBlobUrl(x.datas)).map((x) => ({ name: x.name, datas: x.datas, mimetype: x.mimetype }));
1395
+ const isBase64Image = (str) => {
1396
+ const base64Regex = /^data:image\/(png|jpeg|jpg|gif|webp);base64,/;
1397
+ if (!base64Regex.test(str)) {
1398
+ return false;
1399
+ }
1400
+ try {
1401
+ const base64Data = str.split(",")[1];
1402
+ return !!base64Data && atob(base64Data).length > 0;
1403
+ } catch (error) {
1404
+ return false;
1405
+ }
1406
+ };
1407
+ const handleFileChange = async (files, e, oldValues) => {
1408
+ try {
1409
+ const uploadedUrls = await Promise.all(
1410
+ files.map(async (f) => {
1411
+ const formData = new FormData();
1412
+ formData.append("file", f);
1413
+ const res = await mutateAsync({ formData, service, xNode, path });
1414
+ return res?.url;
1415
+ })
1416
+ );
1417
+ const uploadedItems = files.map((f, i) => ({
1418
+ name: f.name,
1419
+ datas: uploadedUrls[i] ?? "",
1420
+ mimetype: f.type
1421
+ }));
1422
+ const finalList = [...oldValues, ...uploadedItems];
1423
+ methods?.setValue(name, finalList, { shouldDirty: true });
1424
+ const payloadForBE = sanitizeForBE(finalList);
1425
+ handleOnchange && handleOnchange(name ?? "", payloadForBE);
1426
+ } catch (err) {
1427
+ console.error(err);
1428
+ } finally {
1429
+ e.target.value = "";
1430
+ }
1431
+ };
1432
+ const handleRemoveAt = (idx) => {
1433
+ const current = methods?.getValues(name) || [];
1434
+ const next = current.filter((_, i) => i !== idx);
1435
+ setInitialFiles((p) => p.filter((_, i) => i !== idx));
1436
+ methods?.setValue(name, next.length ? next : null, { shouldDirty: true });
1437
+ const payloadForBE = next.length ? sanitizeForBE(next) : null;
1438
+ handleOnchange && handleOnchange(name ?? "", payloadForBE);
1439
+ };
1440
+ const handleRemoveAll = () => {
1441
+ setInitialFiles([]);
1442
+ methods?.setValue(name, null, { shouldDirty: true });
1443
+ handleOnchange && handleOnchange(name ?? "", null);
1444
+ };
1445
+ return {
1446
+ inputId,
1447
+ initialFiles,
1448
+ binaryRef,
1449
+ handleFileChange,
1450
+ handleRemoveAt,
1451
+ handleRemoveAll,
1452
+ checkIsImageLink,
1453
+ setInitialFiles
1454
+ };
1455
+ };
1456
+
1457
+ // src/widget/basic/provider-einvoice-field/controller.ts
1458
+ var providerEinvoiceFieldController = (props) => {
1459
+ const { relation, formValues, options: fieldOptions, xNode } = props;
1460
+ const { env } = (0, provider_exports.useEnv)();
1461
+ const { action } = useAppProvider();
1462
+ const { useGetSelection: useGetSelection2 } = (0, provider_exports.useService)();
1463
+ const contextObject = {
1464
+ ...(typeof action?.context === "string" ? (0, utils_exports.evalJSONContext)(action?.context) : action?.context) || {},
1465
+ ...env?.context
1466
+ };
1467
+ const optionsObject = typeof fieldOptions === "string" ? (0, utils_exports.evalJSONContext)(fieldOptions, {
1468
+ ...formValues,
1469
+ ...contextObject,
1470
+ context: contextObject,
1471
+ parent: { ...formValues }
1472
+ }) : fieldOptions;
1473
+ const data = {
1474
+ model: optionsObject?.model,
1475
+ domain: optionsObject?.domain,
1476
+ context: {
1477
+ ...contextObject,
1478
+ ...optionsObject?.context
1479
+ },
1480
+ specification: optionsObject?.specification
1481
+ };
1482
+ const { data: listDataCard } = useGetSelection2({
1483
+ data,
1484
+ queryKey: [`data_${relation}`],
1485
+ enabled: true,
1486
+ service: optionsObject?.service,
1487
+ xNode
1488
+ });
1489
+ return {
1490
+ listDataCard
1491
+ };
1492
+ };
1493
+
1494
+ // src/widget/advance/table/table-head/controller.ts
1495
+ var import_react23 = require("react");
1496
+ var tableHeadController = (props) => {
1497
+ const {
1498
+ typeTable,
1499
+ rows,
1500
+ tableRef,
1501
+ groupByList,
1502
+ selectedRowKeys,
1503
+ setSelectedRowKeys
1504
+ } = props;
1505
+ const { rowIds: recordIds } = useGetRowIds(tableRef);
1506
+ const selectedRowKeysRef = (0, import_react23.useRef)(recordIds);
1507
+ const isGroupTable = typeTable === "group";
1508
+ const recordsCheckedGroup = (0, import_react23.useMemo)(() => {
1509
+ if (!rows || !groupByList) return 0;
1510
+ const groupBy = typeof groupByList === "object" ? groupByList?.contexts?.[0]?.group_by : void 0;
1511
+ return countSum(rows, groupBy);
1512
+ }, [rows, groupByList]);
1513
+ const isAllGroupChecked = (0, import_react23.useMemo)(() => {
1514
+ if (!isGroupTable || !selectedRowKeys?.length) return false;
1515
+ const selectedLength = selectedRowKeys.filter((id) => id !== -1).length;
1516
+ const allRecordsSelected = recordIds.length === selectedRowKeys.length ? recordIds.length === selectedLength : false;
1517
+ const allGroupsSelected = recordsCheckedGroup === selectedRowKeys.length;
1518
+ return allGroupsSelected || allRecordsSelected;
1519
+ }, [isGroupTable, selectedRowKeys, recordIds, recordsCheckedGroup]);
1520
+ const isAllNormalChecked = (0, import_react23.useMemo)(() => {
1521
+ if (isGroupTable || !selectedRowKeys?.length || !rows?.length) return false;
1522
+ return selectedRowKeys.length === rows.length && selectedRowKeys.every(
1523
+ (id) => rows.some((record) => record.id === id)
1524
+ );
1525
+ }, [isGroupTable, selectedRowKeys, rows]);
1526
+ const checkedAll = isAllGroupChecked || isAllNormalChecked;
1527
+ const handleCheckBoxAll = (event) => {
1528
+ if (event?.target?.checked && typeTable === "list") {
1529
+ const allRowKeys = Array.isArray(rows) ? rows.map((record) => record?.id) : [];
1530
+ setSelectedRowKeys(allRowKeys);
1531
+ } else if (event?.target?.checked && typeTable === "group") {
1532
+ const rowsIDs = document.querySelectorAll("tr[data-row-id]");
1533
+ const ids = Array.from(rowsIDs)?.map(
1534
+ (row) => Number(row?.getAttribute("data-row-id"))
1535
+ );
1536
+ if (ids?.length > 0) {
1537
+ setSelectedRowKeys(ids);
1538
+ } else {
1539
+ const sum = countSum(
1540
+ rows,
1541
+ typeof groupByList === "object" ? groupByList?.contexts?.[0]?.group_by : void 0
1542
+ );
1543
+ const keys = Array.from({ length: sum }, (_) => void 0);
1544
+ setSelectedRowKeys(keys);
1545
+ }
1546
+ if (selectedRowKeysRef) {
1547
+ selectedRowKeysRef.current = [];
1548
+ }
1549
+ } else {
1550
+ setSelectedRowKeys([]);
1551
+ }
1552
+ };
1553
+ return {
1554
+ handleCheckBoxAll,
1555
+ checkedAll,
1556
+ selectedRowKeysRef
1557
+ };
1558
+ };
1559
+
1560
+ // src/widget/advance/table/table-view/controller.ts
1561
+ var import_react24 = require("react");
1562
+ var tableController = ({ data }) => {
1563
+ const [rows, setRows] = (0, import_react24.useState)([]);
1564
+ const [columnVisibility, setColumnVisibility] = (0, import_react24.useState)({});
1565
+ const dataModelFields = (0, import_react24.useMemo)(() => {
1566
+ return data?.fields?.map((field) => ({
1567
+ ...data.dataModel?.[field?.name],
1568
+ ...field,
1569
+ string: field?.string || data.dataModel?.[field?.name]?.string
1570
+ })) ?? [];
1571
+ }, [data?.fields, data?.dataModel]);
1572
+ const mergeFields = (0, import_react24.useMemo)(
1573
+ () => mergeButtons(dataModelFields),
1574
+ [dataModelFields]
1575
+ );
1576
+ const transformData = (0, import_react24.useCallback)(
1577
+ (dataList) => {
1578
+ if (!dataList) return [];
1579
+ return dataList.map((item) => {
1580
+ const transformedItem = { ...item };
1581
+ Object.keys(item).forEach((field) => {
1582
+ if (field !== "__domain") {
1583
+ if (item[field] && typeof item[field] === "object" && item[field].display_name) {
1584
+ transformedItem[field] = item[field];
1585
+ } else if (Array.isArray(item[field]) && item[field].length > 0) {
1586
+ if (data.typeTable === "group" && item[field]?.length === 2 && typeof item[field]?.[1] === "string") {
1587
+ transformedItem["string"] = item[field]?.[1];
1588
+ }
1589
+ transformedItem[field] = item[field];
1590
+ }
1591
+ }
1592
+ });
1593
+ return item.display_name ? { ...transformedItem, item: item.display_name } : transformedItem;
1594
+ });
1595
+ },
1596
+ [data?.typeTable]
1597
+ );
1598
+ (0, import_react24.useEffect)(() => {
1599
+ setRows(transformData(data?.records));
1600
+ }, [data?.records, transformData]);
1601
+ const columns = (0, import_react24.useMemo)(() => {
1602
+ try {
1603
+ return mergeFields?.filter((item) => {
1604
+ return item?.widget !== "details_Receive_money" && !(item?.column_invisible ? utils_exports.domainHelper.matchDomains(
1605
+ data.context,
1606
+ item?.column_invisible
1607
+ ) : item?.invisible ? utils_exports.domainHelper.matchDomains(data.context, item?.invisible) : false);
1608
+ })?.map((field) => {
1609
+ const overridden = columnVisibility[field?.name];
1610
+ return {
1611
+ name: field?.name,
1612
+ optional: overridden ?? field?.optional,
1613
+ title: field?.type_co === "button" ? "" : field?.string,
1614
+ field: { ...field }
1615
+ };
1616
+ }) ?? [];
1617
+ } catch (error) {
1618
+ console.error("Error in useTable:", error);
1619
+ return [];
1620
+ }
1621
+ }, [mergeFields, data?.context, columnVisibility]);
1622
+ const onToggleColumnOptional = (0, import_react24.useCallback)((item) => {
1623
+ setColumnVisibility((prev) => ({
1624
+ ...prev,
1625
+ [item?.name]: item?.optional === "show" ? "hide" : "show"
1626
+ }));
1627
+ }, []);
1628
+ return {
1629
+ rows,
1630
+ columns,
1631
+ onToggleColumnOptional,
1632
+ typeTable: data?.typeTable
1633
+ };
1634
+ };
1635
+
1636
+ // src/widget/advance/table/table-group/controller.ts
1637
+ var import_react25 = require("react");
1638
+ var tableGroupController = (props) => {
1639
+ const { env } = (0, provider_exports.useEnv)();
1640
+ const { useGetListData: useGetListData2 } = (0, provider_exports.useService)();
1641
+ const {
1642
+ columns,
1643
+ row,
1644
+ model,
1645
+ viewData,
1646
+ level,
1647
+ specification,
1648
+ context,
1649
+ checkedAll,
1650
+ groupByList,
1651
+ setSelectedRowKeys
1652
+ } = props;
1653
+ const [pageGroup, setPageGroup] = (0, import_react25.useState)(0);
1654
+ const [isShowGroup, setIsShowGroup] = (0, import_react25.useState)(false);
1655
+ const [colEmptyGroup, setColEmptyGroup] = (0, import_react25.useState)({
1656
+ fromStart: 1,
1657
+ fromEnd: 1
1658
+ });
1659
+ const domain = row?.__domain;
1660
+ const processedData = (0, import_react25.useMemo)(() => {
1661
+ const calculateColSpanEmpty = () => {
1662
+ const startIndex = columns.findIndex(
1663
+ (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator === "sum"
1664
+ );
1665
+ const endIndex = columns.findLastIndex(
1666
+ (col) => col.field.type === "monetary" && typeof row[col.key] === "number" || col.field.aggregator !== "sum"
1667
+ );
1668
+ const fromStart = startIndex === -1 ? columns.length : startIndex;
1669
+ const fromEnd = endIndex === -1 ? columns.length : columns.length - 1 - endIndex;
1670
+ setColEmptyGroup({ fromStart: fromStart + 1, fromEnd: fromEnd + 1 });
1671
+ return { fromStart: fromStart + 1, fromEnd: fromEnd + 1 };
1672
+ };
1673
+ return calculateColSpanEmpty();
1674
+ }, [columns, row]);
1675
+ const shouldFetchData = (0, import_react25.useMemo)(() => {
1676
+ return !!isShowGroup;
1677
+ }, [isShowGroup]);
1678
+ const enabled = shouldFetchData && !!processedData;
1679
+ const listDataProps = {
1680
+ model,
1681
+ specification,
1682
+ domain,
1683
+ context,
1684
+ offset: pageGroup * 10,
1685
+ fields: groupByList?.fields,
1686
+ groupby: [groupByList?.contexts[level]?.group_by],
1687
+ limit: 10
1688
+ };
1689
+ const queryKey = [
1690
+ `data-${model}-level_${level}-row-${row?.id}`,
1691
+ specification,
1692
+ domain,
1693
+ pageGroup
1694
+ ];
1695
+ const {
1696
+ data: dataGroup,
1697
+ isFetched: isDataGroupFetched,
1698
+ isLoading,
1699
+ isFetching,
1700
+ isPlaceholderData: isDataPlaceHolder
1701
+ } = useGetListData2(listDataProps, queryKey, enabled);
1702
+ const {
1703
+ columns: columnsGroup,
1704
+ rows: rowsGroup,
1705
+ typeTable: typeTableGroup
1706
+ } = tableController({
1707
+ data: {
1708
+ fields: viewData?.views?.list?.fields,
1709
+ records: dataGroup?.records ?? dataGroup?.groups,
1710
+ dataModel: viewData?.models?.[model],
1711
+ context: env.context,
1712
+ typeTable: dataGroup?.groups ? "group" : "list"
1713
+ }
1714
+ });
1715
+ const group_by_field_name = groupByList?.contexts[level - 1]?.group_by;
1716
+ 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(
1717
+ (selectItem) => selectItem?.[0] === row[group_by_field_name]
1718
+ )?.[1] : row[group_by_field_name];
1719
+ const toggleShowGroup = () => setIsShowGroup((prev) => !prev);
1720
+ return {
1721
+ // onExpandChildGroup,
1722
+ colEmptyGroup,
1723
+ isShowGroup,
1724
+ isDataGroupFetched,
1725
+ isDataPlaceHolder,
1726
+ // nameGroupWithCount,
1727
+ columnsGroup,
1728
+ rowsGroup,
1729
+ dataGroup,
1730
+ pageGroup,
1731
+ setPageGroup,
1732
+ typeTableGroup
1733
+ };
1734
+ };
1735
+
1736
+ // src/widget/advance/search/controller.ts
1737
+ var import_react26 = require("react");
1738
+ var searchController = ({
1739
+ viewData,
1740
+ model,
1741
+ domain,
1742
+ context,
1743
+ fieldsList,
1744
+ validateAndParseDate,
1745
+ moment
1746
+ }) => {
1747
+ const { env } = (0, provider_exports.useEnv)();
1748
+ const [filterBy, setFilterBy] = (0, import_react26.useState)([]);
1749
+ const [searchBy, setSearchBy] = (0, import_react26.useState)(null);
1750
+ const [groupBy, setGroupBy] = (0, import_react26.useState)(null);
1751
+ const [selectedTags, setSelectedTags] = (0, import_react26.useState)(null);
1752
+ const [searchString, setSearchString] = (0, import_react26.useState)("");
1753
+ const [searchMap, setSearchMap] = (0, import_react26.useState)({});
1754
+ const [hoveredIndex, setHoveredIndex] = (0, import_react26.useState)(0);
1755
+ const [hoveredIndexSearchList, setHoveredIndexSearchList] = (0, import_react26.useState)(0);
1756
+ const actionContext = typeof context === "string" ? (0, utils_exports.evalJSONContext)(context) : context;
1757
+ const contextSearch = { ...env.context, ...actionContext };
1758
+ const domainAction = domain ? Array.isArray(domain) ? [...domain] : (0, utils_exports.evalJSONDomain)(domain, contextSearch) : [];
1759
+ const resetAllStateSearch = () => {
1760
+ setFilterBy([]);
1761
+ setGroupBy([]);
1762
+ setSearchBy([]);
1763
+ setSelectedTags(null);
1764
+ setSearchString("");
1765
+ setSearchMap({});
1766
+ };
1767
+ const fetchData = async () => {
1768
+ if (viewData) {
1769
+ try {
1770
+ const dataModel = viewData?.models?.[model];
1771
+ const searchViews = viewData?.views?.search;
1772
+ const searchByItems = searchViews?.search_by?.filter(
1773
+ (item) => !utils_exports.domainHelper.matchDomains(contextSearch, item.invisible)
1774
+ )?.map(
1775
+ ({
1776
+ string,
1777
+ name,
1778
+ filter_domain,
1779
+ operator,
1780
+ widget,
1781
+ class: classSearchItem,
1782
+ placeholder
1783
+ }, index) => ({
1784
+ placeholder,
1785
+ class: classSearchItem,
1786
+ dataIndex: index,
1787
+ title: string ?? dataModel?.[name]?.string,
1788
+ name: name ?? dataModel?.[name]?.name,
1789
+ filter_domain,
1790
+ operator,
1791
+ widget,
1792
+ type: dataModel?.[name]?.type
1793
+ })
1794
+ );
1795
+ const filterByItems = searchViews?.filter_by.filter((item) => {
1796
+ return !utils_exports.domainHelper.matchDomains(contextSearch, item?.invisible);
1797
+ })?.map((item) => ({ ...item, active: false }));
1798
+ const groupByItems = searchViews?.group_by.filter(
1799
+ (item) => !utils_exports.domainHelper.matchDomains(contextSearch, item?.invisible)
1800
+ ).map((item) => ({
1801
+ ...item,
1802
+ string: item.string ?? viewData?.models?.[model]?.[item?.name?.split("group_by_")?.[1]]?.string
1803
+ }));
1804
+ setSearchBy(searchByItems);
1805
+ setFilterBy(filterByItems);
1806
+ setGroupBy(groupByItems);
1807
+ } catch (error) {
1808
+ console.error("Error fetching data:", error);
1809
+ }
1810
+ }
1811
+ };
1812
+ (0, import_react26.useEffect)(() => {
1813
+ fetchData();
1814
+ }, [model, viewData]);
1815
+ const onChangeSearchInput = (search_string) => {
1816
+ setSearchString(search_string);
1817
+ };
1818
+ const removeKeyFromSearchMap = ({
1819
+ key,
1820
+ item
1821
+ }) => {
1822
+ const values = searchMap[key];
1823
+ if (!values) return searchMap;
1824
+ const newSearchMap = { ...searchMap };
1825
+ if (item) {
1826
+ const filtered = values.filter((value) => value.name !== item.name);
1827
+ if (filtered.length > 0) {
1828
+ newSearchMap[key] = filtered;
1829
+ } else {
1830
+ delete newSearchMap[key];
1831
+ }
1832
+ } else {
1833
+ delete newSearchMap[key];
1834
+ }
1835
+ setSearchMap(newSearchMap);
1836
+ };
1837
+ const updateSearchMap = ({ key, item }) => {
1838
+ const newSearchMap = { ...searchMap };
1839
+ const currentValues = searchMap[key] ?? [];
1840
+ newSearchMap[key] = [...currentValues, item];
1841
+ setSearchMap(newSearchMap);
1842
+ };
1843
+ const removeSearchItems = (key, item) => {
1844
+ removeKeyFromSearchMap({ key: String(key), item });
1845
+ };
1846
+ const addSearchItems = (key, newItem) => {
1847
+ updateSearchMap({ key, item: newItem });
1848
+ };
1849
+ const formatDomain = () => {
1850
+ if (domainAction) {
1851
+ const domain2 = [];
1852
+ if (Array.isArray(domainAction) && domainAction.length > 0) {
1853
+ if (Object.keys(searchMap).some((key) => !key.includes(constants_exports.SearchType.GROUP))) {
1854
+ domain2.push("&");
1855
+ }
1856
+ domainAction.forEach((domainItem) => {
1857
+ domain2.push(domainItem);
1858
+ });
1859
+ }
1860
+ Object.keys(searchMap).forEach((key, keyIndex, keys) => {
1861
+ if (!key?.includes(constants_exports.SearchType.GROUP)) {
1862
+ if (keys.length > 1 && keyIndex < keys.length - 1) {
1863
+ domain2.push("&");
1864
+ }
1865
+ const valuesOfKey = searchMap[key];
1866
+ valuesOfKey.forEach((value, index) => {
1867
+ if (index < valuesOfKey.length - 1) {
1868
+ domain2.push("|");
1869
+ }
1870
+ if (value.domain) {
1871
+ domain2.push(...value.domain);
1872
+ return;
1873
+ }
1874
+ let valueDomainItem = value?.value;
1875
+ if (value?.modelType === "date") {
1876
+ valueDomainItem = validateAndParseDate(value?.value);
1877
+ } else if (value?.modelType === "datetime") {
1878
+ if (value?.operator === "<=" || value?.operator === "<") {
1879
+ const parsedDate = validateAndParseDate(value?.value, true);
1880
+ const hasTime = moment(value?.value).format("HH:mm:ss") !== "00:00:00";
1881
+ valueDomainItem = hasTime ? moment(parsedDate).format("YYYY-MM-DD HH:mm:ss") : moment(parsedDate).add(1, "day").subtract(1, "second").format("YYYY-MM-DD HH:mm:ss");
1882
+ } else {
1883
+ valueDomainItem = validateAndParseDate(value?.value, true);
1884
+ }
1885
+ }
1886
+ const operator = value?.modelType === "date" || value?.modelType === "datetime" || value?.modelType === "boolean" || value?.modelType === "integer" ? value?.operator ?? "=" : value.operator ?? "ilike";
1887
+ domain2.push([value.name, operator, valueDomainItem]);
1888
+ });
1889
+ }
1890
+ });
1891
+ return [...domain2];
1892
+ }
1893
+ };
1894
+ const setTagSearch = (0, import_react26.useCallback)(
1895
+ (updatedMap) => {
1896
+ if (!updatedMap) return;
1897
+ const tagsSearch = Object.entries(updatedMap).map(
1898
+ ([key, objValues]) => {
1899
+ const {
1900
+ title,
1901
+ name,
1902
+ groupIndex,
1903
+ type,
1904
+ widget,
1905
+ modelType,
1906
+ dataIndex,
1907
+ date
1908
+ } = objValues[0];
1909
+ if (!key?.includes(constants_exports.SearchType.GROUP)) {
1910
+ const values = objValues?.map((objValue) => objValue.value);
1911
+ return {
1912
+ title,
1913
+ name: type === constants_exports.SearchType.SEARCH ? `${constants_exports.SearchType.SEARCH}_${String(dataIndex)}` : groupIndex ?? name,
1914
+ values,
1915
+ type,
1916
+ widget,
1917
+ modelType,
1918
+ date
1919
+ };
1920
+ } else {
1921
+ const contexts = [];
1922
+ let groupValues = [];
1923
+ objValues?.forEach((objValue) => {
1924
+ const { context: context2, value, active, groupIndex: groupIndex2, isDefault } = objValue;
1925
+ const indexAppend = groupIndex2 != null ? groupIndex2 : viewData?.views?.search?.filters_by?.length ?? 0;
1926
+ contexts.push(
1927
+ ...Array.isArray(context2?.group_by) ? context2.group_by.map((item) => ({ group_by: item })) : [context2]
1928
+ );
1929
+ groupValues[indexAppend] = {
1930
+ contexts: [
1931
+ ...Array.isArray(context2?.group_by) ? context2.group_by.map((item) => ({
1932
+ group_by: item
1933
+ })) : [context2]
1934
+ ],
1935
+ strings: isDefault ? [value] : [...groupValues[indexAppend]?.strings ?? [], value]
1936
+ };
1937
+ });
1938
+ const fields = [
1939
+ ...new Set(fieldsList?.map((item) => item?.name))
1940
+ ];
1941
+ const groupByTag = {
1942
+ title,
1943
+ values: groupValues?.filter(
1944
+ (item) => item !== void 0
1945
+ ),
1946
+ type,
1947
+ contexts,
1948
+ fields
1949
+ };
1950
+ return groupByTag;
1951
+ }
1952
+ }
1953
+ );
1954
+ setSelectedTags(tagsSearch);
1955
+ setSearchString("");
1956
+ },
1957
+ [searchMap]
1958
+ );
1959
+ const removeSearchItemsByType = (type) => {
1960
+ const newSearchMap = {};
1961
+ Object.entries(searchMap).forEach(([key, values]) => {
1962
+ const isGroup = key.includes(constants_exports.SearchType.GROUP);
1963
+ const isFilter = key.includes(constants_exports.SearchType.FILTER);
1964
+ const isSearch = key.includes(constants_exports.SearchType.SEARCH);
1965
+ if (type === constants_exports.SearchType.GROUP && isGroup) return;
1966
+ if (type === constants_exports.SearchType.FILTER && isFilter) return;
1967
+ if (type === constants_exports.SearchType.SEARCH && isSearch) return;
1968
+ newSearchMap[key] = values;
1969
+ });
1970
+ setSearchMap(newSearchMap);
1971
+ };
1972
+ (0, import_react26.useEffect)(() => {
1973
+ setTagSearch(searchMap);
1974
+ }, [searchMap]);
1975
+ const handleAddTagSearch = (tag) => {
1976
+ const { domain: domain2, groupIndex, value, type, context: context2, dataIndex } = tag;
1977
+ const domainFormat = new utils_exports.domainHelper.Domain(domain2);
1978
+ if (type === constants_exports.SearchType.FILTER) {
1979
+ addSearchItems(`${constants_exports.SearchType.FILTER}_${groupIndex}`, {
1980
+ ...tag,
1981
+ domain: domain2 ? domainFormat.toList(context2) : null
1982
+ });
1983
+ } else if (type === constants_exports.SearchType.SEARCH) {
1984
+ addSearchItems(`${constants_exports.SearchType.SEARCH}_${String(dataIndex)}`, {
1985
+ ...tag,
1986
+ domain: domain2 ? domainFormat.toList({
1987
+ ...context2,
1988
+ self: value
1989
+ }) : null
1990
+ });
1991
+ } else if (type === constants_exports.SearchType.GROUP) {
1992
+ addSearchItems(`${constants_exports.SearchType.GROUP}`, {
1993
+ ...tag,
1994
+ domain: domain2 ? domainFormat.toList({
1995
+ context: context2,
1996
+ self: value
1997
+ }) : null
1998
+ });
1999
+ }
2000
+ };
2001
+ const onKeyDown = (e) => {
2002
+ if (!searchBy || searchBy.length === 0) return;
2003
+ switch (e.key) {
2004
+ case "Backspace": {
2005
+ if (!searchString && selectedTags && selectedTags.length > 0) {
2006
+ const lastTag = selectedTags[selectedTags.length - 1];
2007
+ if (!lastTag) return;
2008
+ const key = lastTag.type === constants_exports.SearchType.GROUP ? constants_exports.SearchType.GROUP : lastTag.name;
2009
+ removeKeyFromSearchMap({ key: String(key) });
2010
+ }
2011
+ break;
2012
+ }
2013
+ case "ArrowDown": {
2014
+ e.preventDefault();
2015
+ setHoveredIndex((prev) => {
2016
+ const maxIndex = searchBy.length - 1;
2017
+ const next = prev < maxIndex ? prev + 1 : prev;
2018
+ setHoveredIndexSearchList(next);
2019
+ return next;
2020
+ });
2021
+ break;
2022
+ }
2023
+ case "ArrowUp": {
2024
+ e.preventDefault();
2025
+ setHoveredIndex((prev) => {
2026
+ const next = prev > 0 ? prev - 1 : prev;
2027
+ setHoveredIndexSearchList(next);
2028
+ return next;
2029
+ });
2030
+ break;
2031
+ }
2032
+ case "Enter": {
2033
+ e.preventDefault();
2034
+ if (!searchString.trim()) return;
2035
+ const head = searchBy[hoveredIndex];
2036
+ if (!head) return;
2037
+ handleAddTagSearch({
2038
+ title: head.title,
2039
+ name: head.name,
2040
+ value: searchString,
2041
+ type: constants_exports.SearchType.SEARCH,
2042
+ domain: head.filter_domain,
2043
+ operator: head.operator,
2044
+ dataIndex: head.dataIndex,
2045
+ widget: head.widget,
2046
+ modelType: head.type
2047
+ });
2048
+ break;
2049
+ }
2050
+ default:
2051
+ break;
2052
+ }
2053
+ };
2054
+ const handleMouseEnter = (index) => {
2055
+ setHoveredIndexSearchList(index);
2056
+ };
2057
+ const handleMouseLeave = () => {
2058
+ setHoveredIndexSearchList(null);
2059
+ };
2060
+ return {
2061
+ groupBy,
2062
+ searchBy,
2063
+ filterBy,
2064
+ selectedTags,
2065
+ searchString,
2066
+ setFilterBy,
2067
+ setGroupBy,
2068
+ setSearchBy,
2069
+ resetAllStateSearch,
2070
+ setSelectedTags,
2071
+ removeSearchItems,
2072
+ onSearchString: onChangeSearchInput,
2073
+ handleAddTagSearch,
2074
+ domain: formatDomain(),
2075
+ context: contextSearch,
2076
+ onKeyDown,
2077
+ handleMouseEnter,
2078
+ handleMouseLeave,
2079
+ hoveredIndexSearchList,
2080
+ removeSearchItemsByType
2081
+ };
2082
+ };
2083
+ // Annotate the CommonJS export names for ESM import in node:
2084
+ 0 && (module.exports = {
2085
+ binaryFieldController,
2086
+ colorFieldController,
2087
+ copyLinkButtonController,
2088
+ downLoadBinaryController,
2089
+ downloadFileController,
2090
+ durationController,
2091
+ many2manyBinaryController,
2092
+ many2manyFieldController,
2093
+ many2manyTagsController,
2094
+ many2oneButtonController,
2095
+ many2oneFieldController,
2096
+ priorityFieldController,
2097
+ providerEinvoiceFieldController,
2098
+ searchController,
2099
+ statusDropdownController,
2100
+ tableController,
2101
+ tableGroupController,
2102
+ tableHeadController
2103
+ });