@sustaina/shared-ui 1.24.0 → 1.25.0

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.d.mts CHANGED
@@ -104,6 +104,7 @@ interface AdvanceSearchProps {
104
104
  onSearch?: (param: Params) => void;
105
105
  onClear?: () => void;
106
106
  shortDateFormat?: string;
107
+ filterFieldMap?: Record<string, string>;
107
108
  }
108
109
 
109
110
  declare const AdvanceSearch: React__default.FC<AdvanceSearchProps>;
package/dist/index.d.ts CHANGED
@@ -104,6 +104,7 @@ interface AdvanceSearchProps {
104
104
  onSearch?: (param: Params) => void;
105
105
  onClear?: () => void;
106
106
  shortDateFormat?: string;
107
+ filterFieldMap?: Record<string, string>;
107
108
  }
108
109
 
109
110
  declare const AdvanceSearch: React__default.FC<AdvanceSearchProps>;
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ var clsx2 = require('clsx');
5
5
  var tailwindMerge = require('tailwind-merge');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var React4 = require('react');
8
- var dateFns = require('date-fns');
9
8
  var lucideReact = require('lucide-react');
10
9
  var reactDom = require('react-dom');
11
10
  var SelectPrimitive = require('@radix-ui/react-select');
@@ -13,6 +12,7 @@ var reactHookForm = require('react-hook-form');
13
12
  var reactSlot = require('@radix-ui/react-slot');
14
13
  var LabelPrimitive = require('@radix-ui/react-label');
15
14
  var classVarianceAuthority = require('class-variance-authority');
15
+ var dateFns = require('date-fns');
16
16
  var PopoverPrimitive = require('@radix-ui/react-popover');
17
17
  var CheckboxPrimitive = require('@radix-ui/react-checkbox');
18
18
  var CollapsiblePrimitive = require('@radix-ui/react-collapsible');
@@ -3461,6 +3461,66 @@ function getBuilder(fieldType) {
3461
3461
  return new JSONBuilder();
3462
3462
  }
3463
3463
  }
3464
+ var FILTER_FIELD_MAP = {
3465
+ timezone: "timezoneId",
3466
+ decimalSeparator: "decimalSeparatorId",
3467
+ country: "countryId",
3468
+ currency: "currencyId"
3469
+ };
3470
+ function transformFilterKeys(obj, fieldMap = FILTER_FIELD_MAP) {
3471
+ if (Array.isArray(obj)) {
3472
+ return obj.map((item) => transformFilterKeys(item, fieldMap));
3473
+ }
3474
+ if (obj && typeof obj === "object") {
3475
+ const newObj = {};
3476
+ for (const key in obj) {
3477
+ const mappedKey = fieldMap[key] ?? key;
3478
+ newObj[mappedKey] = transformFilterKeys(obj[key], fieldMap);
3479
+ }
3480
+ return newObj;
3481
+ }
3482
+ return obj;
3483
+ }
3484
+ var sanitizeInput = (val) => {
3485
+ if (!val) return val;
3486
+ if (typeof val !== "string") return "__INVALID_TYPE__";
3487
+ if (val.includes("\n") || val.includes("\r") || /[\u2028\u2029]/u.test(val))
3488
+ return "__INVALID_NEWLINE__";
3489
+ if (/\\\\/.test(val)) return "__INVALID_ESCAPE__";
3490
+ if (/\\(n|t|r|b|f|u[0-9a-fA-F]{4})/.test(val)) return "__INVALID_ESCAPE__";
3491
+ if (/\p{Cf}/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
3492
+ if (/[\u00A0\u1680\u180E\u202F\u205F\u3000]/u.test(val)) return "__INVALID_UNICODE_WHITESPACE__";
3493
+ const trimmed = val.trim();
3494
+ if (/^\{.*\}$/s.test(trimmed) || /^\[.*\]$/s.test(trimmed)) return "__INVALID_JSON_LITERAL__";
3495
+ if (/\\\{/.test(val) || /\\\}/.test(val)) return "__INVALID_JSON_ESCAPE__";
3496
+ if (/[%_*~^]/.test(trimmed)) return "__INVALID_WILDCARD__";
3497
+ if (/[%><={}\\[\]"']/u.test(trimmed)) return "__INVALID_CHAR__";
3498
+ if (/\p{Cc}/u.test(val)) return "__INVALID_CONTROL_CHAR__";
3499
+ return trimmed.replace(/\s+/g, " ");
3500
+ };
3501
+ var numericTypes = ["number", "integer", "decimal"];
3502
+ var dateTypes = ["date", "datemonth"];
3503
+ var validateByFieldType = (value, fieldType) => {
3504
+ if (!value) return { valid: true };
3505
+ if (numericTypes.includes(fieldType)) {
3506
+ if (!/^\d+(\.\d+)?$/.test(value)) {
3507
+ return { valid: false, message: "Please enter a valid number." };
3508
+ }
3509
+ }
3510
+ if (fieldType === "boolean") {
3511
+ if (!["true", "false"].includes(value.toLowerCase())) {
3512
+ return { valid: false, message: "Please enter a boolean value (true/false)." };
3513
+ }
3514
+ }
3515
+ if (dateTypes.includes(fieldType)) {
3516
+ const normalized = fieldType === "datemonth" ? `${value}-01` : value;
3517
+ const parsed = dateFns.parseISO(normalized);
3518
+ if (!dateFns.isValid(parsed)) {
3519
+ return { valid: false, message: "Invalid date format." };
3520
+ }
3521
+ }
3522
+ return { valid: true };
3523
+ };
3464
3524
  var AdvanceSearch = ({
3465
3525
  fields,
3466
3526
  portalId,
@@ -3468,7 +3528,8 @@ var AdvanceSearch = ({
3468
3528
  limitRows = 4,
3469
3529
  onSearch,
3470
3530
  onClear,
3471
- shortDateFormat
3531
+ shortDateFormat,
3532
+ filterFieldMap = FILTER_FIELD_MAP
3472
3533
  }) => {
3473
3534
  const fieldsData = React4.useMemo(() => {
3474
3535
  if (fields.length === 0) throw new Error("fields cannot be an empty array");
@@ -3520,72 +3581,75 @@ var AdvanceSearch = ({
3520
3581
  const onSubmit = React4.useCallback(() => {
3521
3582
  const operatorValidation = {};
3522
3583
  rows.forEach((r) => {
3523
- const availableOperators = operatorsForField(r.fieldName);
3524
- if (!availableOperators.length || !availableOperators.includes(r.operator)) {
3584
+ const ops = operatorsForField(r.fieldName);
3585
+ if (!ops.length || !ops.includes(r.operator))
3525
3586
  operatorValidation[r.id] = "Please select an operator.";
3526
- }
3527
3587
  });
3528
3588
  setOperatorErrors(operatorValidation);
3529
- if (Object.keys(operatorValidation).length > 0) {
3530
- return;
3531
- }
3589
+ if (Object.keys(operatorValidation).length > 0) return;
3532
3590
  const currentValues = getValues();
3533
- let hasRangeError = false;
3534
- const rawRows = rows.map((r) => {
3535
- const startFieldName = `value_${r.id}`;
3536
- const startValue = currentValues[startFieldName] ?? "";
3591
+ let hasError = false;
3592
+ const processedRows = rows.map((r) => {
3593
+ const startField = `value_${r.id}`;
3594
+ const endField = `value2_${r.id}`;
3595
+ let v1 = currentValues[startField] ?? "";
3596
+ let v2 = currentValues[endField] ?? "";
3597
+ const s1 = sanitizeInput(v1);
3598
+ if (s1?.startsWith("__INVALID")) {
3599
+ hasError = true;
3600
+ setError(startField, { type: "validate", message: "Invalid input." });
3601
+ return null;
3602
+ }
3603
+ v1 = s1 || "";
3604
+ const valid1 = validateByFieldType(v1, r.fieldType);
3605
+ if (!valid1.valid) {
3606
+ hasError = true;
3607
+ setError(startField, { type: "validate", message: valid1.message });
3608
+ return null;
3609
+ }
3537
3610
  if (r.operator === "between") {
3538
- const endFieldName = `value2_${r.id}`;
3539
- const endValue = currentValues[endFieldName] ?? "";
3540
- if (startValue && endValue) {
3541
- const startDate = parseRangeValue(startValue, r.fieldType);
3542
- const endDate = parseRangeValue(endValue, r.fieldType);
3543
- if (startDate && endDate && dateFns.isAfter(startDate, endDate)) {
3544
- hasRangeError = true;
3545
- setError(startFieldName, {
3546
- type: "validate",
3547
- message: "Start value must be before end value."
3548
- });
3549
- setError(endFieldName, {
3550
- type: "validate",
3551
- message: "End value must be after start value."
3552
- });
3553
- } else {
3554
- clearErrors([startFieldName, endFieldName]);
3611
+ const s2 = sanitizeInput(v2);
3612
+ if (s2?.startsWith("__INVALID")) {
3613
+ hasError = true;
3614
+ setError(endField, { type: "validate", message: "Invalid input." });
3615
+ return null;
3616
+ }
3617
+ v2 = s2 || "";
3618
+ const valid2 = validateByFieldType(v2, r.fieldType);
3619
+ if (!valid2.valid) {
3620
+ hasError = true;
3621
+ setError(endField, { type: "validate", message: valid2.message });
3622
+ return null;
3623
+ }
3624
+ if (v1 && v2 && ["date", "datemonth"].includes(r.fieldType)) {
3625
+ const d1 = parseRangeValue(v1, r.fieldType);
3626
+ const d2 = parseRangeValue(v2, r.fieldType);
3627
+ if (d1 && d2 && dateFns.isAfter(d1, d2)) {
3628
+ hasError = true;
3629
+ setError(startField, { type: "validate", message: "Start value must be before end value." });
3630
+ setError(endField, { type: "validate", message: "End value must be after start value." });
3631
+ return null;
3555
3632
  }
3556
3633
  }
3557
- return {
3558
- ...r,
3559
- value: startValue ?? "",
3560
- value2: endValue ?? ""
3561
- };
3634
+ return { ...r, value: v1, value2: v2 };
3562
3635
  }
3563
- return {
3564
- ...r,
3565
- value: startValue ?? ""
3566
- };
3636
+ return { ...r, value: v1 };
3567
3637
  });
3568
- if (hasRangeError) {
3569
- return;
3570
- }
3638
+ if (hasError) return;
3639
+ const cleanedRows = processedRows.filter(Boolean);
3571
3640
  const param = {
3572
- AND: rawRows.map((r) => {
3573
- const builder = getBuilder(r.fieldType);
3574
- return builder.build(r);
3575
- }).filter(Boolean)
3641
+ AND: cleanedRows.map((r) => getBuilder(r.fieldType).build(r)).filter(Boolean)
3576
3642
  };
3577
- if (onSearch) {
3578
- onSearch(param);
3579
- }
3643
+ if (onSearch) onSearch(transformFilterKeys(param, filterFieldMap));
3580
3644
  }, [
3581
- clearErrors,
3582
- getValues,
3583
- onSearch,
3645
+ rows,
3584
3646
  operatorsForField,
3647
+ getValues,
3585
3648
  parseRangeValue,
3586
- rows,
3587
3649
  setError,
3588
- setOperatorErrors
3650
+ setOperatorErrors,
3651
+ filterFieldMap,
3652
+ onSearch
3589
3653
  ]);
3590
3654
  return /* @__PURE__ */ jsxRuntime.jsx(
3591
3655
  ExpandCollapse_default,
@@ -3623,9 +3687,7 @@ var AdvanceSearch = ({
3623
3687
  unregister(`value_${row.id}`);
3624
3688
  unregister(`value2_${row.id}`);
3625
3689
  },
3626
- onClearValue: (which) => {
3627
- clearValue(row.id, which);
3628
- },
3690
+ onClearValue: (which) => clearValue(row.id, which),
3629
3691
  disableAdd: limitRows !== void 0 && rows.length >= limitRows
3630
3692
  },
3631
3693
  row.id
@@ -3650,7 +3712,7 @@ var AdvanceSearch = ({
3650
3712
  Button,
3651
3713
  {
3652
3714
  type: "submit",
3653
- className: "w-full bg-[#379a2a] text-white hover:bg-[#2f7c21] md:w-auto md:min-w-[120px]",
3715
+ className: "w-full bg-sus-green-2 text-white hover:bg-[#2f7c21] md:w-auto md:min-w-[120px]",
3654
3716
  "data-testid": "advsearch-btn-search",
3655
3717
  children: "Search"
3656
3718
  }
@@ -6587,8 +6649,8 @@ var GridSettingsModal = ({
6587
6649
  }
6588
6650
  }, [isOpen, currentColumns, form]);
6589
6651
  const addColumn = async () => {
6590
- const isValid5 = await trigger("columns");
6591
- if (isValid5) {
6652
+ const isValid6 = await trigger("columns");
6653
+ if (isValid6) {
6592
6654
  append({ id: "" });
6593
6655
  requestAnimationFrame(() => {
6594
6656
  const container = scrollRef.current;