strapi-plugin-magic-mark 3.3.2 → 3.4.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/_chunks/{App-DFDDcq8K.js → App-DNyxeldt.js} +2 -62
- package/dist/_chunks/{App-CozIqdTt.mjs → App-D_Vllkur.mjs} +1 -61
- package/dist/_chunks/{UpgradePage-9godO_5E.mjs → UpgradePage-DuyIskve.mjs} +1 -1
- package/dist/_chunks/{UpgradePage-CwYDN8bT.js → UpgradePage-SzlmS6po.js} +1 -1
- package/dist/_chunks/{index-DCsuhvUc.js → index-CBwb0AEf.js} +1081 -161
- package/dist/_chunks/{index-DNoxLWi0.mjs → index-QkINaIgJ.mjs} +1083 -163
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/package.json +3 -3
|
@@ -6,7 +6,7 @@ import { BookmarkIcon, SwatchIcon, ChevronDownIcon, BookOpenIcon, StarIcon, Hear
|
|
|
6
6
|
import { useIntl } from "react-intl";
|
|
7
7
|
import { useNavigate, useLocation } from "react-router-dom";
|
|
8
8
|
import styled, { keyframes } from "styled-components";
|
|
9
|
-
import { Box, Typography, Flex, TextInput, Textarea, Button, Loader } from "@strapi/design-system";
|
|
9
|
+
import { Box, Typography, Flex, TextInput, Textarea, Button, Loader, Tag, DatePicker, NumberInput, DateTimePicker, TimePicker, SingleSelect, SingleSelectOption } from "@strapi/design-system";
|
|
10
10
|
import ReactDOM from "react-dom";
|
|
11
11
|
import qs from "qs";
|
|
12
12
|
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
@@ -154,7 +154,7 @@ const FilterChip = styled.div`
|
|
|
154
154
|
gap: 4px;
|
|
155
155
|
color: ${(props) => props.theme.colors.neutral800};
|
|
156
156
|
`;
|
|
157
|
-
const LogicBadge = styled.span`
|
|
157
|
+
const LogicBadge$1 = styled.span`
|
|
158
158
|
font-weight: bold;
|
|
159
159
|
color: ${(props) => props.logic === "OR" ? props.theme.colors.warning700 : props.theme.colors.primary700};
|
|
160
160
|
font-size: 10px;
|
|
@@ -191,7 +191,7 @@ const FilterPreview = ({ query }) => {
|
|
|
191
191
|
parsed.filters.length > 0 && /* @__PURE__ */ jsxs(Box, { marginBottom: 2, children: [
|
|
192
192
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Filters:" }),
|
|
193
193
|
/* @__PURE__ */ jsx(Flex, { wrap: "wrap", gap: 1, children: parsed.filters.map((filter, idx) => /* @__PURE__ */ jsxs(FilterChip, { logic: filter.logic, children: [
|
|
194
|
-
filter.logic && /* @__PURE__ */ jsx(LogicBadge, { logic: filter.logic, children: filter.logic }),
|
|
194
|
+
filter.logic && /* @__PURE__ */ jsx(LogicBadge$1, { logic: filter.logic, children: filter.logic }),
|
|
195
195
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
196
196
|
filter.field,
|
|
197
197
|
" ",
|
|
@@ -354,7 +354,7 @@ const ModalBody = styled.div`
|
|
|
354
354
|
overflow-y: auto;
|
|
355
355
|
flex: 1;
|
|
356
356
|
`;
|
|
357
|
-
const ModalFooter = styled.div`
|
|
357
|
+
const ModalFooter$1 = styled.div`
|
|
358
358
|
padding: 16px 28px;
|
|
359
359
|
background: ${(p) => p.theme.colors.neutral100};
|
|
360
360
|
border-top: 1px solid rgba(128, 128, 128, 0.25);
|
|
@@ -1006,7 +1006,7 @@ const CreateEditModal = ({
|
|
|
1006
1006
|
)
|
|
1007
1007
|
] })
|
|
1008
1008
|
] }),
|
|
1009
|
-
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
1009
|
+
/* @__PURE__ */ jsxs(ModalFooter$1, { children: [
|
|
1010
1010
|
/* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({ id: `${pluginId2}.button.cancel`, defaultMessage: "Cancel" }) }),
|
|
1011
1011
|
/* @__PURE__ */ jsx(Button, { onClick: handleSubmit, loading: isSubmitting, children: isEditing ? formatMessage({ id: `${pluginId2}.button.update`, defaultMessage: "Update" }) : formatMessage({ id: `${pluginId2}.button.save`, defaultMessage: "Save Bookmark" }) })
|
|
1012
1012
|
] })
|
|
@@ -1453,7 +1453,8 @@ const SelectContainer = styled.div`
|
|
|
1453
1453
|
`;
|
|
1454
1454
|
const SelectButton = styled.button`
|
|
1455
1455
|
width: 100%;
|
|
1456
|
-
|
|
1456
|
+
height: 32px;
|
|
1457
|
+
padding: 0 36px 0 12px;
|
|
1457
1458
|
border: 1px solid ${(props) => props.$isOpen ? "#4945ff" : "rgba(128, 128, 128, 0.25)"};
|
|
1458
1459
|
border-radius: 4px;
|
|
1459
1460
|
background: ${(p) => p.theme.colors.neutral0};
|
|
@@ -1476,12 +1477,13 @@ const SelectButton = styled.button`
|
|
|
1476
1477
|
}
|
|
1477
1478
|
|
|
1478
1479
|
@media (max-width: 768px) {
|
|
1479
|
-
|
|
1480
|
+
height: 32px;
|
|
1481
|
+
padding: 0 32px 0 10px;
|
|
1480
1482
|
font-size: 13px;
|
|
1481
1483
|
}
|
|
1482
1484
|
`;
|
|
1483
1485
|
const SelectText = styled.span`
|
|
1484
|
-
color: ${(props) => props.$isPlaceholder ?
|
|
1486
|
+
color: ${(props) => props.$isPlaceholder ? props.theme.colors.neutral500 : props.theme.colors.neutral800};
|
|
1485
1487
|
overflow: hidden;
|
|
1486
1488
|
text-overflow: ellipsis;
|
|
1487
1489
|
white-space: nowrap;
|
|
@@ -1502,7 +1504,7 @@ const DropdownList = styled.div`
|
|
|
1502
1504
|
top: ${(props) => props.top}px;
|
|
1503
1505
|
left: ${(props) => props.left}px;
|
|
1504
1506
|
width: ${(props) => props.width}px;
|
|
1505
|
-
max-height:
|
|
1507
|
+
max-height: 400px;
|
|
1506
1508
|
overflow-y: auto;
|
|
1507
1509
|
background: ${(p) => p.theme.colors.neutral0};
|
|
1508
1510
|
border: 1px solid rgba(128, 128, 128, 0.25);
|
|
@@ -1512,14 +1514,14 @@ const DropdownList = styled.div`
|
|
|
1512
1514
|
display: ${(props) => props.$isOpen ? "block" : "none"};
|
|
1513
1515
|
|
|
1514
1516
|
@media (max-width: 768px) {
|
|
1515
|
-
max-height:
|
|
1517
|
+
max-height: 320px;
|
|
1516
1518
|
}
|
|
1517
1519
|
`;
|
|
1518
1520
|
const DropdownItem = styled.button`
|
|
1519
1521
|
width: 100%;
|
|
1520
1522
|
padding: 10px 12px;
|
|
1521
1523
|
border: none;
|
|
1522
|
-
background: ${(props) => props.$isSelected ? "
|
|
1524
|
+
background: ${(props) => props.$isSelected ? "rgba(73, 69, 255, 0.06)" : props.theme.colors.neutral0};
|
|
1523
1525
|
text-align: left;
|
|
1524
1526
|
cursor: pointer;
|
|
1525
1527
|
font-size: 14px;
|
|
@@ -1546,6 +1548,8 @@ const SearchInput = styled.input`
|
|
|
1546
1548
|
border: none;
|
|
1547
1549
|
border-bottom: 1px solid rgba(128, 128, 128, 0.25);
|
|
1548
1550
|
font-size: 14px;
|
|
1551
|
+
background: ${(p) => p.theme.colors.neutral0};
|
|
1552
|
+
color: ${(p) => p.theme.colors.neutral800};
|
|
1549
1553
|
|
|
1550
1554
|
&:focus {
|
|
1551
1555
|
outline: none;
|
|
@@ -1757,18 +1761,110 @@ const useRelationSchema = () => {
|
|
|
1757
1761
|
errors
|
|
1758
1762
|
};
|
|
1759
1763
|
};
|
|
1760
|
-
const
|
|
1764
|
+
const BASE_FILTERS = [
|
|
1765
|
+
{ value: "eq", label: "is" },
|
|
1766
|
+
{ value: "ne", label: "is not" },
|
|
1767
|
+
{ value: "null", label: "is null" },
|
|
1768
|
+
{ value: "notNull", label: "is not null" }
|
|
1769
|
+
];
|
|
1770
|
+
const NUMERIC_FILTERS = [
|
|
1771
|
+
{ value: "gt", label: "is greater than" },
|
|
1772
|
+
{ value: "gte", label: "is greater than or equal to" },
|
|
1773
|
+
{ value: "lt", label: "is less than" },
|
|
1774
|
+
{ value: "lte", label: "is less than or equal to" }
|
|
1775
|
+
];
|
|
1776
|
+
const CONTAINS_FILTERS = [
|
|
1777
|
+
{ value: "contains", label: "contains" },
|
|
1778
|
+
{ value: "containsi", label: "contains (case insensitive)" },
|
|
1779
|
+
{ value: "notContains", label: "not contains" },
|
|
1780
|
+
{ value: "notContainsi", label: "not contains (case insensitive)" }
|
|
1781
|
+
];
|
|
1782
|
+
const STRING_PARSE_FILTERS = [
|
|
1783
|
+
{ value: "startsWith", label: "starts with" },
|
|
1784
|
+
{ value: "startsWithi", label: "starts with (case insensitive)" },
|
|
1785
|
+
{ value: "endsWith", label: "ends with" },
|
|
1786
|
+
{ value: "endsWithi", label: "ends with (case insensitive)" }
|
|
1787
|
+
];
|
|
1788
|
+
const IS_SENSITIVE_FILTERS = [
|
|
1789
|
+
{ value: "eqi", label: "is (case insensitive)" },
|
|
1790
|
+
{ value: "nei", label: "is not (case insensitive)" }
|
|
1791
|
+
];
|
|
1792
|
+
const ARRAY_FILTERS = [
|
|
1793
|
+
{ value: "in", label: "is in" },
|
|
1794
|
+
{ value: "notIn", label: "is not in" }
|
|
1795
|
+
];
|
|
1796
|
+
const RANGE_FILTERS = [
|
|
1797
|
+
{ value: "between", label: "is between" }
|
|
1798
|
+
];
|
|
1799
|
+
function getOperatorsForType(fieldType) {
|
|
1800
|
+
const t = (fieldType || "string").toLowerCase();
|
|
1801
|
+
if (["string", "text", "email", "uid", "richtext"].includes(t)) {
|
|
1802
|
+
return [
|
|
1803
|
+
...BASE_FILTERS,
|
|
1804
|
+
...IS_SENSITIVE_FILTERS,
|
|
1805
|
+
...CONTAINS_FILTERS,
|
|
1806
|
+
...STRING_PARSE_FILTERS,
|
|
1807
|
+
...ARRAY_FILTERS
|
|
1808
|
+
];
|
|
1809
|
+
}
|
|
1810
|
+
if (["integer", "float", "decimal", "biginteger"].includes(t)) {
|
|
1811
|
+
return [
|
|
1812
|
+
...BASE_FILTERS,
|
|
1813
|
+
...NUMERIC_FILTERS,
|
|
1814
|
+
...ARRAY_FILTERS,
|
|
1815
|
+
...RANGE_FILTERS
|
|
1816
|
+
];
|
|
1817
|
+
}
|
|
1818
|
+
if (["datetime"].includes(t)) {
|
|
1819
|
+
return [
|
|
1820
|
+
...BASE_FILTERS,
|
|
1821
|
+
...NUMERIC_FILTERS,
|
|
1822
|
+
...RANGE_FILTERS
|
|
1823
|
+
];
|
|
1824
|
+
}
|
|
1825
|
+
if (["date", "time"].includes(t)) {
|
|
1826
|
+
return [
|
|
1827
|
+
...BASE_FILTERS,
|
|
1828
|
+
...NUMERIC_FILTERS,
|
|
1829
|
+
...CONTAINS_FILTERS,
|
|
1830
|
+
...RANGE_FILTERS
|
|
1831
|
+
];
|
|
1832
|
+
}
|
|
1833
|
+
if (["boolean"].includes(t)) {
|
|
1834
|
+
return BASE_FILTERS;
|
|
1835
|
+
}
|
|
1836
|
+
if (["enumeration"].includes(t)) {
|
|
1837
|
+
return [...BASE_FILTERS, ...ARRAY_FILTERS];
|
|
1838
|
+
}
|
|
1839
|
+
return [...BASE_FILTERS, ...IS_SENSITIVE_FILTERS];
|
|
1840
|
+
}
|
|
1841
|
+
const buildFilterFromPath = (fieldPath, operator, value, valueTo, negate) => {
|
|
1761
1842
|
const parts = fieldPath.split(".").filter(Boolean);
|
|
1762
1843
|
if (parts.length === 0) return {};
|
|
1763
1844
|
const opKey = operator.startsWith("$") ? operator : `$${operator}`;
|
|
1764
|
-
|
|
1845
|
+
let filterValue;
|
|
1846
|
+
if (["null", "notNull"].includes(operator)) {
|
|
1847
|
+
filterValue = true;
|
|
1848
|
+
} else if (["in", "notIn"].includes(operator)) {
|
|
1849
|
+
filterValue = value.split(",").map((v) => v.trim()).filter(Boolean);
|
|
1850
|
+
} else if (operator === "between" && valueTo) {
|
|
1851
|
+
filterValue = [value, valueTo];
|
|
1852
|
+
} else {
|
|
1853
|
+
filterValue = value;
|
|
1854
|
+
}
|
|
1855
|
+
let result;
|
|
1765
1856
|
if (parts.length === 1) {
|
|
1766
|
-
|
|
1857
|
+
result = { [parts[0]]: { [opKey]: filterValue } };
|
|
1858
|
+
} else {
|
|
1859
|
+
const [first, ...rest] = parts;
|
|
1860
|
+
result = {
|
|
1861
|
+
[first]: buildFilterFromPath(rest.join("."), operator, value, valueTo, false)
|
|
1862
|
+
};
|
|
1767
1863
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1864
|
+
if (negate) {
|
|
1865
|
+
return { $not: result };
|
|
1866
|
+
}
|
|
1867
|
+
return result;
|
|
1772
1868
|
};
|
|
1773
1869
|
const extractRelationFromField = (field) => {
|
|
1774
1870
|
if (!field || !field.includes(".")) return null;
|
|
@@ -1782,12 +1878,12 @@ const rowsToFilterStructure = (rows, connectors) => {
|
|
|
1782
1878
|
});
|
|
1783
1879
|
if (validRows.length === 0) return void 0;
|
|
1784
1880
|
if (validRows.length === 1) {
|
|
1785
|
-
return buildFilterFromPath(validRows[0].field, validRows[0].operator, validRows[0].value);
|
|
1881
|
+
return buildFilterFromPath(validRows[0].field, validRows[0].operator, validRows[0].value, validRows[0].valueTo, validRows[0].negate);
|
|
1786
1882
|
}
|
|
1787
1883
|
const groups = [];
|
|
1788
1884
|
let currentAndGroup = [];
|
|
1789
1885
|
validRows.forEach((row, i) => {
|
|
1790
|
-
const filter = buildFilterFromPath(row.field, row.operator, row.value);
|
|
1886
|
+
const filter = buildFilterFromPath(row.field, row.operator, row.value, row.valueTo, row.negate);
|
|
1791
1887
|
currentAndGroup.push(filter);
|
|
1792
1888
|
const nextConnector = connectors[i];
|
|
1793
1889
|
if (nextConnector?.logic === "OR" || !nextConnector) {
|
|
@@ -1829,6 +1925,58 @@ const generateFromRows = (rows, connectors) => {
|
|
|
1829
1925
|
}
|
|
1830
1926
|
return qs.stringify(queryObject, { encodeValuesOnly: true });
|
|
1831
1927
|
};
|
|
1928
|
+
const buildFilterFromGroup = (group) => {
|
|
1929
|
+
const validItems = group.items.filter((item) => {
|
|
1930
|
+
if ("isGroup" in item && item.isGroup) return true;
|
|
1931
|
+
const row = item;
|
|
1932
|
+
if (!row.field) return false;
|
|
1933
|
+
if (["null", "notNull"].includes(row.operator)) return true;
|
|
1934
|
+
if (row.operator === "between") return row.value?.trim() && row.valueTo?.trim();
|
|
1935
|
+
return row.value?.trim();
|
|
1936
|
+
});
|
|
1937
|
+
if (validItems.length === 0) return void 0;
|
|
1938
|
+
const conditions = validItems.map((item) => {
|
|
1939
|
+
if ("isGroup" in item && item.isGroup) {
|
|
1940
|
+
return buildFilterFromGroup(item);
|
|
1941
|
+
}
|
|
1942
|
+
const row = item;
|
|
1943
|
+
return buildFilterFromPath(row.field, row.operator, row.value, row.valueTo, row.negate);
|
|
1944
|
+
}).filter(Boolean);
|
|
1945
|
+
if (conditions.length === 0) return void 0;
|
|
1946
|
+
if (conditions.length === 1) return conditions[0];
|
|
1947
|
+
const logicKey = group.logic === "AND" ? "$and" : "$or";
|
|
1948
|
+
return { [logicKey]: conditions };
|
|
1949
|
+
};
|
|
1950
|
+
const extractRelationsFromGroup = (group) => {
|
|
1951
|
+
const relations = /* @__PURE__ */ new Set();
|
|
1952
|
+
group.items.forEach((item) => {
|
|
1953
|
+
if ("isGroup" in item && item.isGroup) {
|
|
1954
|
+
const nested = extractRelationsFromGroup(item);
|
|
1955
|
+
nested.forEach((r) => relations.add(r));
|
|
1956
|
+
} else {
|
|
1957
|
+
const row = item;
|
|
1958
|
+
const rel = extractRelationFromField(row.field);
|
|
1959
|
+
if (rel) relations.add(rel);
|
|
1960
|
+
}
|
|
1961
|
+
});
|
|
1962
|
+
return relations;
|
|
1963
|
+
};
|
|
1964
|
+
const generateFromGroup = (group) => {
|
|
1965
|
+
const queryObject = {};
|
|
1966
|
+
const filterStructure = buildFilterFromGroup(group);
|
|
1967
|
+
if (filterStructure) {
|
|
1968
|
+
queryObject.filters = filterStructure;
|
|
1969
|
+
}
|
|
1970
|
+
const relations = extractRelationsFromGroup(group);
|
|
1971
|
+
if (relations.size > 0) {
|
|
1972
|
+
const populate = {};
|
|
1973
|
+
relations.forEach((rel) => {
|
|
1974
|
+
populate[rel] = true;
|
|
1975
|
+
});
|
|
1976
|
+
queryObject.populate = populate;
|
|
1977
|
+
}
|
|
1978
|
+
return qs.stringify(queryObject, { encodeValuesOnly: true });
|
|
1979
|
+
};
|
|
1832
1980
|
const extractFieldPathAndOperator = (parts) => {
|
|
1833
1981
|
let operatorIndex = -1;
|
|
1834
1982
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
@@ -2003,7 +2151,7 @@ const ModalOverlay = styled.div`
|
|
|
2003
2151
|
display: flex;
|
|
2004
2152
|
align-items: center;
|
|
2005
2153
|
justify-content: center;
|
|
2006
|
-
z-index:
|
|
2154
|
+
z-index: 400;
|
|
2007
2155
|
padding: 20px;
|
|
2008
2156
|
@media (max-width: 768px) {
|
|
2009
2157
|
padding: 10px;
|
|
@@ -2013,61 +2161,328 @@ const ModalContent = styled(Box)`
|
|
|
2013
2161
|
background: ${(p) => p.theme.colors.neutral0};
|
|
2014
2162
|
border-radius: 8px;
|
|
2015
2163
|
max-height: 90vh;
|
|
2016
|
-
|
|
2017
|
-
max-width: 560px;
|
|
2164
|
+
max-width: 720px;
|
|
2018
2165
|
width: 100%;
|
|
2166
|
+
min-height: 400px;
|
|
2019
2167
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
|
2168
|
+
display: flex;
|
|
2169
|
+
flex-direction: column;
|
|
2170
|
+
overflow: hidden;
|
|
2020
2171
|
@media (max-width: 768px) {
|
|
2021
2172
|
max-width: 100%;
|
|
2022
|
-
max-height:
|
|
2173
|
+
max-height: 92vh;
|
|
2023
2174
|
}
|
|
2024
2175
|
`;
|
|
2176
|
+
const ModalScrollArea = styled.div`
|
|
2177
|
+
flex: 1;
|
|
2178
|
+
overflow-y: auto;
|
|
2179
|
+
overflow-x: hidden;
|
|
2180
|
+
`;
|
|
2025
2181
|
const RowWrapper = styled.div`
|
|
2026
2182
|
display: flex;
|
|
2027
2183
|
flex-direction: column;
|
|
2028
2184
|
gap: 8px;
|
|
2029
2185
|
margin-bottom: 8px;
|
|
2030
2186
|
`;
|
|
2031
|
-
const
|
|
2187
|
+
const FilterRowCard = styled.div`
|
|
2188
|
+
display: flex;
|
|
2189
|
+
flex-direction: column;
|
|
2190
|
+
gap: 10px;
|
|
2191
|
+
padding: 12px;
|
|
2192
|
+
background: ${(p) => p.theme.colors.neutral100};
|
|
2193
|
+
border-radius: 6px;
|
|
2194
|
+
border: 1px solid ${(p) => p.theme.colors.neutral200};
|
|
2195
|
+
`;
|
|
2196
|
+
const FilterRowTop = styled.div`
|
|
2032
2197
|
display: flex;
|
|
2033
2198
|
align-items: center;
|
|
2034
2199
|
gap: 8px;
|
|
2035
2200
|
flex-wrap: wrap;
|
|
2036
2201
|
`;
|
|
2037
|
-
const
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2202
|
+
const NegateCheckbox = styled.label`
|
|
2203
|
+
display: flex;
|
|
2204
|
+
align-items: center;
|
|
2205
|
+
gap: 6px;
|
|
2206
|
+
font-size: 12px;
|
|
2207
|
+
color: ${(p) => p.theme.colors.neutral700};
|
|
2208
|
+
cursor: pointer;
|
|
2209
|
+
user-select: none;
|
|
2210
|
+
input {
|
|
2211
|
+
cursor: pointer;
|
|
2212
|
+
}
|
|
2213
|
+
`;
|
|
2214
|
+
const FilterRowValue = styled.div`
|
|
2215
|
+
width: 100%;
|
|
2216
|
+
padding-top: 14px;
|
|
2217
|
+
padding-bottom: 4px;
|
|
2218
|
+
border-top: 1px solid ${(p) => p.theme.colors.neutral200};
|
|
2219
|
+
`;
|
|
2220
|
+
const ValueLabel = styled.span`
|
|
2221
|
+
display: block;
|
|
2222
|
+
font-size: 12px;
|
|
2223
|
+
font-weight: 500;
|
|
2224
|
+
color: ${(p) => p.theme.colors.neutral600};
|
|
2225
|
+
margin-bottom: 8px;
|
|
2226
|
+
`;
|
|
2227
|
+
styled.span`
|
|
2228
|
+
font-size: 10px;
|
|
2229
|
+
font-weight: 500;
|
|
2230
|
+
padding: 2px 6px;
|
|
2042
2231
|
border-radius: 4px;
|
|
2232
|
+
background: ${(p) => p.theme.colors.neutral200};
|
|
2233
|
+
color: ${(p) => p.theme.colors.neutral700};
|
|
2234
|
+
text-transform: uppercase;
|
|
2235
|
+
`;
|
|
2236
|
+
const MultiValueWrapper = styled.div`
|
|
2237
|
+
display: flex;
|
|
2238
|
+
flex-wrap: wrap;
|
|
2239
|
+
gap: 6px;
|
|
2240
|
+
min-height: 32px;
|
|
2241
|
+
padding: 6px 8px;
|
|
2242
|
+
border: 1px solid ${(p) => p.theme.colors.neutral200};
|
|
2243
|
+
border-radius: 4px;
|
|
2244
|
+
background: ${(p) => p.theme.colors.neutral0};
|
|
2245
|
+
&:focus-within {
|
|
2246
|
+
border-color: #4945ff;
|
|
2247
|
+
box-shadow: 0 0 0 2px rgba(73, 69, 255, 0.1);
|
|
2248
|
+
}
|
|
2249
|
+
`;
|
|
2250
|
+
const MultiValueInput = styled.input`
|
|
2251
|
+
flex: 1;
|
|
2252
|
+
min-width: 80px;
|
|
2253
|
+
border: none;
|
|
2254
|
+
background: transparent;
|
|
2043
2255
|
font-size: 14px;
|
|
2256
|
+
color: ${(p) => p.theme.colors.neutral800};
|
|
2044
2257
|
&:focus {
|
|
2045
2258
|
outline: none;
|
|
2046
|
-
border-color: #4945ff;
|
|
2047
|
-
box-shadow: 0 0 0 2px rgba(73, 69, 255, 0.1);
|
|
2048
2259
|
}
|
|
2260
|
+
&::placeholder {
|
|
2261
|
+
color: ${(p) => p.theme.colors.neutral500};
|
|
2262
|
+
}
|
|
2263
|
+
`;
|
|
2264
|
+
const BetweenWrapper = styled.div`
|
|
2265
|
+
display: flex;
|
|
2266
|
+
align-items: center;
|
|
2267
|
+
gap: 8px;
|
|
2268
|
+
width: 100%;
|
|
2049
2269
|
`;
|
|
2050
|
-
const
|
|
2270
|
+
const BetweenSeparator = styled.span`
|
|
2271
|
+
font-size: 13px;
|
|
2272
|
+
font-weight: 500;
|
|
2273
|
+
color: ${(p) => p.theme.colors.neutral600};
|
|
2274
|
+
`;
|
|
2275
|
+
function SmartValueInput({
|
|
2276
|
+
fieldType,
|
|
2277
|
+
operator,
|
|
2278
|
+
value,
|
|
2279
|
+
valueTo,
|
|
2280
|
+
onChange,
|
|
2281
|
+
onValueToChange,
|
|
2282
|
+
placeholder = "Value"
|
|
2283
|
+
}) {
|
|
2284
|
+
const t = (fieldType || "string").toLowerCase();
|
|
2285
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
2286
|
+
if (["in", "notIn"].includes(operator)) {
|
|
2287
|
+
const values = value ? value.split(",").map((v) => v.trim()).filter(Boolean) : [];
|
|
2288
|
+
const handleAddValue = (e) => {
|
|
2289
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
2290
|
+
e.preventDefault();
|
|
2291
|
+
const newVal = inputValue.trim();
|
|
2292
|
+
if (newVal && !values.includes(newVal)) {
|
|
2293
|
+
const updated = [...values, newVal].join(",");
|
|
2294
|
+
onChange(updated);
|
|
2295
|
+
setInputValue("");
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
};
|
|
2299
|
+
const handleRemoveValue = (val) => {
|
|
2300
|
+
const updated = values.filter((v) => v !== val).join(",");
|
|
2301
|
+
onChange(updated);
|
|
2302
|
+
};
|
|
2303
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%" }, children: /* @__PURE__ */ jsxs(MultiValueWrapper, { children: [
|
|
2304
|
+
values.map((val) => /* @__PURE__ */ jsx(
|
|
2305
|
+
Tag,
|
|
2306
|
+
{
|
|
2307
|
+
icon: /* @__PURE__ */ jsx(Cross, {}),
|
|
2308
|
+
onClick: () => handleRemoveValue(val),
|
|
2309
|
+
children: val
|
|
2310
|
+
},
|
|
2311
|
+
val
|
|
2312
|
+
)),
|
|
2313
|
+
/* @__PURE__ */ jsx(
|
|
2314
|
+
MultiValueInput,
|
|
2315
|
+
{
|
|
2316
|
+
value: inputValue,
|
|
2317
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
2318
|
+
onKeyDown: handleAddValue,
|
|
2319
|
+
placeholder: values.length === 0 ? "Type and press Enter" : ""
|
|
2320
|
+
}
|
|
2321
|
+
)
|
|
2322
|
+
] }) });
|
|
2323
|
+
}
|
|
2324
|
+
if (operator === "between") {
|
|
2325
|
+
if (["date"].includes(t)) {
|
|
2326
|
+
const dateVal1 = value ? (() => {
|
|
2327
|
+
try {
|
|
2328
|
+
const d = new Date(value);
|
|
2329
|
+
return isNaN(d.getTime()) ? void 0 : d;
|
|
2330
|
+
} catch {
|
|
2331
|
+
return void 0;
|
|
2332
|
+
}
|
|
2333
|
+
})() : void 0;
|
|
2334
|
+
const dateVal2 = valueTo ? (() => {
|
|
2335
|
+
try {
|
|
2336
|
+
const d = new Date(valueTo);
|
|
2337
|
+
return isNaN(d.getTime()) ? void 0 : d;
|
|
2338
|
+
} catch {
|
|
2339
|
+
return void 0;
|
|
2340
|
+
}
|
|
2341
|
+
})() : void 0;
|
|
2342
|
+
return /* @__PURE__ */ jsxs(BetweenWrapper, { children: [
|
|
2343
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(DatePicker, { value: dateVal1, onChange: (d) => onChange(d ? d.toISOString().slice(0, 10) : ""), clearLabel: "Clear", size: "S" }) }),
|
|
2344
|
+
/* @__PURE__ */ jsx(BetweenSeparator, { children: "and" }),
|
|
2345
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(DatePicker, { value: dateVal2, onChange: (d) => onValueToChange?.(d ? d.toISOString().slice(0, 10) : ""), clearLabel: "Clear", size: "S" }) })
|
|
2346
|
+
] });
|
|
2347
|
+
}
|
|
2348
|
+
if (["integer", "float", "decimal", "biginteger"].includes(t)) {
|
|
2349
|
+
const num1 = value === "" ? void 0 : (() => {
|
|
2350
|
+
const n = parseFloat(value);
|
|
2351
|
+
return isNaN(n) ? void 0 : n;
|
|
2352
|
+
})();
|
|
2353
|
+
const num2 = valueTo === "" || !valueTo ? void 0 : (() => {
|
|
2354
|
+
const n = parseFloat(valueTo);
|
|
2355
|
+
return isNaN(n) ? void 0 : n;
|
|
2356
|
+
})();
|
|
2357
|
+
return /* @__PURE__ */ jsxs(BetweenWrapper, { children: [
|
|
2358
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(NumberInput, { value: num1, onValueChange: (v) => onChange(v !== void 0 ? String(v) : ""), size: "S" }) }),
|
|
2359
|
+
/* @__PURE__ */ jsx(BetweenSeparator, { children: "and" }),
|
|
2360
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(NumberInput, { value: num2, onValueChange: (v) => onValueToChange?.(v !== void 0 ? String(v) : ""), size: "S" }) })
|
|
2361
|
+
] });
|
|
2362
|
+
}
|
|
2363
|
+
return /* @__PURE__ */ jsxs(BetweenWrapper, { children: [
|
|
2364
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(TextInput, { value, onChange: (e) => onChange(e.target.value), placeholder: "From", size: "S" }) }),
|
|
2365
|
+
/* @__PURE__ */ jsx(BetweenSeparator, { children: "and" }),
|
|
2366
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(TextInput, { value: valueTo || "", onChange: (e) => onValueToChange?.(e.target.value), placeholder: "To", size: "S" }) })
|
|
2367
|
+
] });
|
|
2368
|
+
}
|
|
2369
|
+
if (["date"].includes(t)) {
|
|
2370
|
+
const dateVal = value ? (() => {
|
|
2371
|
+
try {
|
|
2372
|
+
const d = new Date(value);
|
|
2373
|
+
return isNaN(d.getTime()) ? void 0 : d;
|
|
2374
|
+
} catch {
|
|
2375
|
+
return void 0;
|
|
2376
|
+
}
|
|
2377
|
+
})() : void 0;
|
|
2378
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 140 }, children: /* @__PURE__ */ jsx(
|
|
2379
|
+
DatePicker,
|
|
2380
|
+
{
|
|
2381
|
+
value: dateVal,
|
|
2382
|
+
onChange: (d) => onChange(d ? d.toISOString().slice(0, 10) : ""),
|
|
2383
|
+
clearLabel: "Clear",
|
|
2384
|
+
size: "S"
|
|
2385
|
+
}
|
|
2386
|
+
) });
|
|
2387
|
+
}
|
|
2388
|
+
if (["datetime"].includes(t)) {
|
|
2389
|
+
const dateVal = value ? (() => {
|
|
2390
|
+
try {
|
|
2391
|
+
const d = new Date(value);
|
|
2392
|
+
return isNaN(d.getTime()) ? void 0 : d;
|
|
2393
|
+
} catch {
|
|
2394
|
+
return void 0;
|
|
2395
|
+
}
|
|
2396
|
+
})() : void 0;
|
|
2397
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 200 }, children: /* @__PURE__ */ jsx(
|
|
2398
|
+
DateTimePicker,
|
|
2399
|
+
{
|
|
2400
|
+
value: dateVal ?? void 0,
|
|
2401
|
+
onChange: (d) => onChange(d ? d.toISOString() : ""),
|
|
2402
|
+
clearLabel: "Clear",
|
|
2403
|
+
size: "S"
|
|
2404
|
+
}
|
|
2405
|
+
) });
|
|
2406
|
+
}
|
|
2407
|
+
if (["time"].includes(t)) {
|
|
2408
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 120 }, children: /* @__PURE__ */ jsx(
|
|
2409
|
+
TimePicker,
|
|
2410
|
+
{
|
|
2411
|
+
value: value || void 0,
|
|
2412
|
+
onChange: (v) => onChange(v ?? ""),
|
|
2413
|
+
size: "S"
|
|
2414
|
+
}
|
|
2415
|
+
) });
|
|
2416
|
+
}
|
|
2417
|
+
if (["integer", "float", "decimal", "biginteger"].includes(t)) {
|
|
2418
|
+
const numVal = value === "" ? void 0 : (() => {
|
|
2419
|
+
const n = parseFloat(value);
|
|
2420
|
+
return isNaN(n) ? void 0 : n;
|
|
2421
|
+
})();
|
|
2422
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 120 }, children: /* @__PURE__ */ jsx(
|
|
2423
|
+
NumberInput,
|
|
2424
|
+
{
|
|
2425
|
+
value: numVal,
|
|
2426
|
+
onValueChange: (v) => onChange(v !== void 0 ? String(v) : ""),
|
|
2427
|
+
size: "S"
|
|
2428
|
+
}
|
|
2429
|
+
) });
|
|
2430
|
+
}
|
|
2431
|
+
if (["boolean"].includes(t)) {
|
|
2432
|
+
const selVal = value === "true" ? "true" : value === "false" ? "false" : "";
|
|
2433
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 120 }, children: /* @__PURE__ */ jsxs(
|
|
2434
|
+
SingleSelect,
|
|
2435
|
+
{
|
|
2436
|
+
value: selVal || void 0,
|
|
2437
|
+
onChange: (v) => onChange(v != null ? String(v) : ""),
|
|
2438
|
+
placeholder,
|
|
2439
|
+
size: "S",
|
|
2440
|
+
children: [
|
|
2441
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "true", children: "true" }),
|
|
2442
|
+
/* @__PURE__ */ jsx(SingleSelectOption, { value: "false", children: "false" })
|
|
2443
|
+
]
|
|
2444
|
+
}
|
|
2445
|
+
) });
|
|
2446
|
+
}
|
|
2447
|
+
return /* @__PURE__ */ jsx(Box, { style: { width: "100%", minWidth: 120 }, children: /* @__PURE__ */ jsx(
|
|
2448
|
+
TextInput,
|
|
2449
|
+
{
|
|
2450
|
+
value,
|
|
2451
|
+
onChange: (e) => onChange(e.target.value),
|
|
2452
|
+
placeholder,
|
|
2453
|
+
size: "S"
|
|
2454
|
+
}
|
|
2455
|
+
) });
|
|
2456
|
+
}
|
|
2457
|
+
const ConnectorPill = styled.div`
|
|
2051
2458
|
display: flex;
|
|
2052
2459
|
align-items: center;
|
|
2053
2460
|
justify-content: center;
|
|
2054
|
-
padding:
|
|
2461
|
+
padding: 6px 0;
|
|
2055
2462
|
`;
|
|
2056
2463
|
const ConnectorButton = styled.button`
|
|
2057
|
-
padding:
|
|
2464
|
+
padding: 6px 14px;
|
|
2058
2465
|
font-size: 12px;
|
|
2059
2466
|
font-weight: 500;
|
|
2060
|
-
border: 1px solid ${(
|
|
2061
|
-
border-radius:
|
|
2062
|
-
background: ${(
|
|
2063
|
-
color: ${(
|
|
2467
|
+
border: 1px solid ${(p) => p.$active ? "#4945ff" : p.theme.colors.neutral300};
|
|
2468
|
+
border-radius: 999px;
|
|
2469
|
+
background: ${(p) => p.$active ? "#EEF0FF" : "transparent"};
|
|
2470
|
+
color: ${(p) => p.$active ? "#4945ff" : p.theme.colors.neutral700};
|
|
2064
2471
|
cursor: pointer;
|
|
2065
2472
|
transition: all 0.15s ease;
|
|
2066
2473
|
&:hover {
|
|
2067
2474
|
border-color: #4945ff;
|
|
2068
|
-
background: ${(p) => p.theme.colors.neutral100};
|
|
2069
2475
|
}
|
|
2070
2476
|
`;
|
|
2477
|
+
const ActiveBadge = styled.span`
|
|
2478
|
+
font-size: 12px;
|
|
2479
|
+
font-weight: 500;
|
|
2480
|
+
padding: 2px 8px;
|
|
2481
|
+
border-radius: 999px;
|
|
2482
|
+
background: rgba(73, 69, 255, 0.12);
|
|
2483
|
+
color: #4945ff;
|
|
2484
|
+
margin-left: 8px;
|
|
2485
|
+
`;
|
|
2071
2486
|
const DeleteButton = styled.button`
|
|
2072
2487
|
display: flex;
|
|
2073
2488
|
align-items: center;
|
|
@@ -2112,34 +2527,101 @@ const InfoText = styled.div`
|
|
|
2112
2527
|
margin-top: 8px;
|
|
2113
2528
|
font-style: italic;
|
|
2114
2529
|
`;
|
|
2115
|
-
const
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
{
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
{
|
|
2126
|
-
|
|
2530
|
+
const ModalFooter = styled.div`
|
|
2531
|
+
flex-shrink: 0;
|
|
2532
|
+
padding-top: 16px;
|
|
2533
|
+
border-top: 1px solid ${(p) => p.theme.colors.neutral200};
|
|
2534
|
+
margin-top: 8px;
|
|
2535
|
+
`;
|
|
2536
|
+
const GroupContainer = styled.div`
|
|
2537
|
+
border: 2px solid ${(p) => p.$logic === "AND" ? "#0EA5E9" : "#F97316"};
|
|
2538
|
+
border-radius: 8px;
|
|
2539
|
+
padding: 16px;
|
|
2540
|
+
margin: ${(p) => p.$level > 0 ? "12px 0 12px 20px" : "0"};
|
|
2541
|
+
background: ${(p) => p.$logic === "AND" ? "rgba(14, 165, 233, 0.03)" : "rgba(249, 115, 22, 0.03)"};
|
|
2542
|
+
position: relative;
|
|
2543
|
+
${(p) => p.$level > 0 && `border-style: dashed;`}
|
|
2544
|
+
`;
|
|
2545
|
+
const GroupHeader = styled.div`
|
|
2546
|
+
display: flex;
|
|
2547
|
+
align-items: center;
|
|
2548
|
+
justify-content: space-between;
|
|
2549
|
+
margin-bottom: 12px;
|
|
2550
|
+
padding-bottom: 10px;
|
|
2551
|
+
border-bottom: 1px solid ${(p) => p.theme.colors.neutral200};
|
|
2552
|
+
`;
|
|
2553
|
+
const LogicBadge = styled.div`
|
|
2554
|
+
display: inline-flex;
|
|
2555
|
+
align-items: center;
|
|
2556
|
+
gap: 8px;
|
|
2557
|
+
font-size: 12px;
|
|
2558
|
+
font-weight: 600;
|
|
2559
|
+
color: ${(p) => p.$logic === "AND" ? "#0284C7" : "#EA580C"};
|
|
2560
|
+
`;
|
|
2561
|
+
const LogicToggleButton = styled.button`
|
|
2562
|
+
padding: 4px 12px;
|
|
2563
|
+
border-radius: 999px;
|
|
2564
|
+
border: 1px solid ${(p) => p.$logic === "AND" ? "#0EA5E9" : "#F97316"};
|
|
2565
|
+
background: ${(p) => p.$active ? p.$logic === "AND" ? "#0EA5E9" : "#F97316" : "transparent"};
|
|
2566
|
+
color: ${(p) => p.$active ? "white" : p.$logic === "AND" ? "#0284C7" : "#EA580C"};
|
|
2567
|
+
font-size: 11px;
|
|
2568
|
+
font-weight: 600;
|
|
2569
|
+
cursor: pointer;
|
|
2570
|
+
transition: all 0.15s ease;
|
|
2571
|
+
&:hover {
|
|
2572
|
+
transform: scale(1.05);
|
|
2573
|
+
}
|
|
2574
|
+
`;
|
|
2575
|
+
const GroupActions = styled.div`
|
|
2576
|
+
display: flex;
|
|
2577
|
+
gap: 6px;
|
|
2578
|
+
align-items: center;
|
|
2579
|
+
`;
|
|
2580
|
+
const SmallButton = styled.button`
|
|
2581
|
+
padding: 4px 10px;
|
|
2582
|
+
font-size: 11px;
|
|
2583
|
+
font-weight: 500;
|
|
2584
|
+
border: 1px solid ${(p) => p.theme.colors.neutral300};
|
|
2585
|
+
border-radius: 4px;
|
|
2586
|
+
background: ${(p) => p.theme.colors.neutral0};
|
|
2587
|
+
color: ${(p) => p.theme.colors.neutral700};
|
|
2588
|
+
cursor: pointer;
|
|
2589
|
+
&:hover {
|
|
2590
|
+
border-color: #4945ff;
|
|
2591
|
+
background: rgba(73, 69, 255, 0.06);
|
|
2592
|
+
}
|
|
2593
|
+
&:disabled {
|
|
2594
|
+
opacity: 0.5;
|
|
2595
|
+
cursor: not-allowed;
|
|
2596
|
+
}
|
|
2597
|
+
`;
|
|
2598
|
+
const RemoveGroupButton = styled(SmallButton)`
|
|
2599
|
+
color: #dc2626;
|
|
2600
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
2601
|
+
&:hover {
|
|
2602
|
+
border-color: #dc2626;
|
|
2603
|
+
background: rgba(239, 68, 68, 0.06);
|
|
2604
|
+
}
|
|
2605
|
+
`;
|
|
2127
2606
|
const StrapiStyleFilterModal = ({
|
|
2128
2607
|
onClose,
|
|
2129
2608
|
onApply,
|
|
2130
2609
|
availableFields,
|
|
2131
2610
|
availableRelations = [],
|
|
2132
|
-
currentQuery = ""
|
|
2611
|
+
currentQuery = "",
|
|
2612
|
+
enableNestedGroups = false
|
|
2133
2613
|
}) => {
|
|
2134
2614
|
const { getRelationFields } = useRelationSchema();
|
|
2135
2615
|
const [fieldOptions, setFieldOptions] = useState([]);
|
|
2136
2616
|
const [rows, setRows] = useState([]);
|
|
2137
2617
|
const [connectors, setConnectors] = useState([]);
|
|
2618
|
+
const [rootGroup, setRootGroup] = useState(null);
|
|
2138
2619
|
const buildFieldOptions = useCallback(async () => {
|
|
2139
2620
|
const direct = availableFields.map((f) => ({
|
|
2140
2621
|
value: f.name,
|
|
2141
2622
|
label: f.name,
|
|
2142
|
-
group: "Fields"
|
|
2623
|
+
group: "Fields",
|
|
2624
|
+
type: f.type
|
|
2143
2625
|
}));
|
|
2144
2626
|
const relationOptions = [];
|
|
2145
2627
|
for (const rel of availableRelations) {
|
|
@@ -2151,7 +2633,8 @@ const StrapiStyleFilterModal = ({
|
|
|
2151
2633
|
relationOptions.push({
|
|
2152
2634
|
value: `${rel.name}.${f.name}`,
|
|
2153
2635
|
label: `${rel.name} > ${f.name}`,
|
|
2154
|
-
group: "Relations"
|
|
2636
|
+
group: "Relations",
|
|
2637
|
+
type: f.type
|
|
2155
2638
|
});
|
|
2156
2639
|
});
|
|
2157
2640
|
}
|
|
@@ -2165,27 +2648,94 @@ const StrapiStyleFilterModal = ({
|
|
|
2165
2648
|
buildFieldOptions();
|
|
2166
2649
|
}, [buildFieldOptions]);
|
|
2167
2650
|
useEffect(() => {
|
|
2168
|
-
if (
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2651
|
+
if (enableNestedGroups) {
|
|
2652
|
+
setRootGroup({
|
|
2653
|
+
id: "root",
|
|
2654
|
+
logic: "AND",
|
|
2655
|
+
items: [{
|
|
2656
|
+
id: `row_${Date.now()}`,
|
|
2657
|
+
field: "",
|
|
2658
|
+
operator: "eq",
|
|
2659
|
+
value: ""
|
|
2660
|
+
}],
|
|
2661
|
+
isGroup: true
|
|
2662
|
+
});
|
|
2663
|
+
} else {
|
|
2664
|
+
if (currentQuery) {
|
|
2665
|
+
const { rows: parsedRows, connectors: parsedConnectors } = parseQueryToRows(currentQuery);
|
|
2666
|
+
if (parsedRows.length > 0) {
|
|
2667
|
+
setRows(parsedRows);
|
|
2668
|
+
setConnectors(parsedConnectors);
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2174
2671
|
}
|
|
2672
|
+
setRows([{
|
|
2673
|
+
id: `row_${Date.now()}`,
|
|
2674
|
+
field: "",
|
|
2675
|
+
operator: "eq",
|
|
2676
|
+
value: ""
|
|
2677
|
+
}]);
|
|
2678
|
+
setConnectors([]);
|
|
2175
2679
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2680
|
+
}, [currentQuery, enableNestedGroups]);
|
|
2681
|
+
useEffect(() => {
|
|
2682
|
+
if (fieldOptions.length === 0) return;
|
|
2683
|
+
if (!enableNestedGroups) {
|
|
2684
|
+
setRows((prev) => prev.map((r) => {
|
|
2685
|
+
if (!r.field || r.fieldType) return r;
|
|
2686
|
+
const opt = fieldOptions.find((f) => f.value === r.field);
|
|
2687
|
+
return opt?.type ? { ...r, fieldType: opt.type } : r;
|
|
2688
|
+
}));
|
|
2689
|
+
} else if (rootGroup) {
|
|
2690
|
+
const enrichGroup = (group) => ({
|
|
2691
|
+
...group,
|
|
2692
|
+
items: group.items.map((item) => {
|
|
2693
|
+
if ("isGroup" in item && item.isGroup) {
|
|
2694
|
+
return enrichGroup(item);
|
|
2695
|
+
}
|
|
2696
|
+
const row = item;
|
|
2697
|
+
if (!row.field || row.fieldType) return row;
|
|
2698
|
+
const opt = fieldOptions.find((f) => f.value === row.field);
|
|
2699
|
+
return opt?.type ? { ...row, fieldType: opt.type } : row;
|
|
2700
|
+
})
|
|
2701
|
+
});
|
|
2702
|
+
setRootGroup((prev) => prev ? enrichGroup(prev) : prev);
|
|
2703
|
+
}
|
|
2704
|
+
}, [fieldOptions, enableNestedGroups]);
|
|
2705
|
+
const relationsInUse = enableNestedGroups && rootGroup ? (() => {
|
|
2706
|
+
const relations = /* @__PURE__ */ new Set();
|
|
2707
|
+
const extract = (group) => {
|
|
2708
|
+
group.items.forEach((item) => {
|
|
2709
|
+
if ("isGroup" in item && item.isGroup) {
|
|
2710
|
+
extract(item);
|
|
2711
|
+
} else {
|
|
2712
|
+
const row = item;
|
|
2713
|
+
if (row.field?.includes(".")) {
|
|
2714
|
+
relations.add(row.field.split(".")[0]);
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
};
|
|
2719
|
+
extract(rootGroup);
|
|
2720
|
+
return relations;
|
|
2721
|
+
})() : new Set(
|
|
2185
2722
|
rows.map((r) => r.field.includes(".") ? r.field.split(".")[0] : null).filter(Boolean)
|
|
2186
2723
|
);
|
|
2187
2724
|
const updateRow = (index2, updates) => {
|
|
2188
|
-
setRows((prev) => prev.map((r, i) =>
|
|
2725
|
+
setRows((prev) => prev.map((r, i) => {
|
|
2726
|
+
if (i !== index2) return r;
|
|
2727
|
+
const merged = { ...r, ...updates };
|
|
2728
|
+
if ("field" in updates && updates.field !== void 0) {
|
|
2729
|
+
const opt = fieldOptions.find((f) => f.value === updates.field);
|
|
2730
|
+
merged.fieldType = opt?.type;
|
|
2731
|
+
const ops = getOperatorsForType(opt?.type || "string");
|
|
2732
|
+
const firstOp = ops[0]?.value ?? "eq";
|
|
2733
|
+
if (!ops.some((o) => o.value === merged.operator)) {
|
|
2734
|
+
merged.operator = firstOp;
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
return merged;
|
|
2738
|
+
}));
|
|
2189
2739
|
};
|
|
2190
2740
|
const updateConnector = (index2, logic) => {
|
|
2191
2741
|
setConnectors((prev) => {
|
|
@@ -2211,8 +2761,153 @@ const StrapiStyleFilterModal = ({
|
|
|
2211
2761
|
return prev.filter((_, i) => i !== connectorIndex);
|
|
2212
2762
|
});
|
|
2213
2763
|
};
|
|
2764
|
+
const updateGroupLogic = (groupId, logic) => {
|
|
2765
|
+
if (!rootGroup) return;
|
|
2766
|
+
const update = (group) => {
|
|
2767
|
+
if (group.id === groupId) {
|
|
2768
|
+
return { ...group, logic };
|
|
2769
|
+
}
|
|
2770
|
+
return {
|
|
2771
|
+
...group,
|
|
2772
|
+
items: group.items.map(
|
|
2773
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2774
|
+
)
|
|
2775
|
+
};
|
|
2776
|
+
};
|
|
2777
|
+
setRootGroup(update(rootGroup));
|
|
2778
|
+
};
|
|
2779
|
+
const updateRowInGroup = (groupId, rowId, updates) => {
|
|
2780
|
+
if (!rootGroup) return;
|
|
2781
|
+
const update = (group) => {
|
|
2782
|
+
if (group.id === groupId) {
|
|
2783
|
+
return {
|
|
2784
|
+
...group,
|
|
2785
|
+
items: group.items.map((item) => {
|
|
2786
|
+
if (!("isGroup" in item) || !item.isGroup) {
|
|
2787
|
+
const row = item;
|
|
2788
|
+
if (row.id === rowId) {
|
|
2789
|
+
const merged = { ...row, ...updates };
|
|
2790
|
+
if ("field" in updates && updates.field !== void 0) {
|
|
2791
|
+
const opt = fieldOptions.find((f) => f.value === updates.field);
|
|
2792
|
+
merged.fieldType = opt?.type;
|
|
2793
|
+
const ops = getOperatorsForType(opt?.type || "string");
|
|
2794
|
+
const firstOp = ops[0]?.value ?? "eq";
|
|
2795
|
+
if (!ops.some((o) => o.value === merged.operator)) {
|
|
2796
|
+
merged.operator = firstOp;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
return merged;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return item;
|
|
2803
|
+
})
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
return {
|
|
2807
|
+
...group,
|
|
2808
|
+
items: group.items.map(
|
|
2809
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2810
|
+
)
|
|
2811
|
+
};
|
|
2812
|
+
};
|
|
2813
|
+
setRootGroup(update(rootGroup));
|
|
2814
|
+
};
|
|
2815
|
+
const addRowToGroup = (groupId) => {
|
|
2816
|
+
if (!rootGroup) return;
|
|
2817
|
+
const newRow = {
|
|
2818
|
+
id: `row_${Date.now()}`,
|
|
2819
|
+
field: "",
|
|
2820
|
+
operator: "eq",
|
|
2821
|
+
value: ""
|
|
2822
|
+
};
|
|
2823
|
+
const update = (group) => {
|
|
2824
|
+
if (group.id === groupId) {
|
|
2825
|
+
return { ...group, items: [...group.items, newRow] };
|
|
2826
|
+
}
|
|
2827
|
+
return {
|
|
2828
|
+
...group,
|
|
2829
|
+
items: group.items.map(
|
|
2830
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2831
|
+
)
|
|
2832
|
+
};
|
|
2833
|
+
};
|
|
2834
|
+
setRootGroup(update(rootGroup));
|
|
2835
|
+
};
|
|
2836
|
+
const removeRowFromGroup = (groupId, rowId) => {
|
|
2837
|
+
if (!rootGroup) return;
|
|
2838
|
+
const update = (group) => {
|
|
2839
|
+
if (group.id === groupId) {
|
|
2840
|
+
return {
|
|
2841
|
+
...group,
|
|
2842
|
+
items: group.items.filter((item) => {
|
|
2843
|
+
if ("isGroup" in item && item.isGroup) return true;
|
|
2844
|
+
return item.id !== rowId;
|
|
2845
|
+
})
|
|
2846
|
+
};
|
|
2847
|
+
}
|
|
2848
|
+
return {
|
|
2849
|
+
...group,
|
|
2850
|
+
items: group.items.map(
|
|
2851
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2852
|
+
)
|
|
2853
|
+
};
|
|
2854
|
+
};
|
|
2855
|
+
setRootGroup(update(rootGroup));
|
|
2856
|
+
};
|
|
2857
|
+
const addNestedGroup = (parentGroupId) => {
|
|
2858
|
+
if (!rootGroup) return;
|
|
2859
|
+
const newGroup = {
|
|
2860
|
+
id: `group_${Date.now()}`,
|
|
2861
|
+
logic: "AND",
|
|
2862
|
+
items: [{
|
|
2863
|
+
id: `row_${Date.now()}`,
|
|
2864
|
+
field: "",
|
|
2865
|
+
operator: "eq",
|
|
2866
|
+
value: ""
|
|
2867
|
+
}],
|
|
2868
|
+
isGroup: true
|
|
2869
|
+
};
|
|
2870
|
+
const update = (group) => {
|
|
2871
|
+
if (group.id === parentGroupId) {
|
|
2872
|
+
return { ...group, items: [...group.items, newGroup] };
|
|
2873
|
+
}
|
|
2874
|
+
return {
|
|
2875
|
+
...group,
|
|
2876
|
+
items: group.items.map(
|
|
2877
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2878
|
+
)
|
|
2879
|
+
};
|
|
2880
|
+
};
|
|
2881
|
+
setRootGroup(update(rootGroup));
|
|
2882
|
+
};
|
|
2883
|
+
const removeGroup = (groupId) => {
|
|
2884
|
+
if (!rootGroup || groupId === "root") return;
|
|
2885
|
+
const update = (group) => ({
|
|
2886
|
+
...group,
|
|
2887
|
+
items: group.items.filter((item) => {
|
|
2888
|
+
if ("isGroup" in item && item.isGroup) {
|
|
2889
|
+
return item.id !== groupId;
|
|
2890
|
+
}
|
|
2891
|
+
return true;
|
|
2892
|
+
}).map(
|
|
2893
|
+
(item) => "isGroup" in item && item.isGroup ? update(item) : item
|
|
2894
|
+
)
|
|
2895
|
+
});
|
|
2896
|
+
setRootGroup(update(rootGroup));
|
|
2897
|
+
};
|
|
2214
2898
|
const handleApply = () => {
|
|
2215
|
-
|
|
2899
|
+
if (enableNestedGroups && rootGroup) {
|
|
2900
|
+
const queryString2 = generateFromGroup(rootGroup);
|
|
2901
|
+
onApply(queryString2);
|
|
2902
|
+
onClose();
|
|
2903
|
+
return;
|
|
2904
|
+
}
|
|
2905
|
+
const validRows = rows.filter((r) => {
|
|
2906
|
+
if (!r.field) return false;
|
|
2907
|
+
if (["null", "notNull"].includes(r.operator)) return true;
|
|
2908
|
+
if (r.operator === "between") return r.value.trim() && r.valueTo?.trim();
|
|
2909
|
+
return r.value.trim();
|
|
2910
|
+
});
|
|
2216
2911
|
if (validRows.length === 0) {
|
|
2217
2912
|
onApply("");
|
|
2218
2913
|
onClose();
|
|
@@ -2223,100 +2918,322 @@ const StrapiStyleFilterModal = ({
|
|
|
2223
2918
|
onClose();
|
|
2224
2919
|
};
|
|
2225
2920
|
const handleClearAll = () => {
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2921
|
+
if (enableNestedGroups) {
|
|
2922
|
+
setRootGroup({
|
|
2923
|
+
id: "root",
|
|
2924
|
+
logic: "AND",
|
|
2925
|
+
items: [{
|
|
2926
|
+
id: `row_${Date.now()}`,
|
|
2927
|
+
field: "",
|
|
2928
|
+
operator: "eq",
|
|
2929
|
+
value: ""
|
|
2930
|
+
}],
|
|
2931
|
+
isGroup: true
|
|
2932
|
+
});
|
|
2933
|
+
} else {
|
|
2934
|
+
setRows([{
|
|
2935
|
+
id: `row_${Date.now()}`,
|
|
2936
|
+
field: "",
|
|
2937
|
+
operator: "eq",
|
|
2938
|
+
value: ""
|
|
2939
|
+
}]);
|
|
2940
|
+
setConnectors([]);
|
|
2941
|
+
}
|
|
2942
|
+
};
|
|
2943
|
+
const countActiveFilters = (group) => {
|
|
2944
|
+
let count = 0;
|
|
2945
|
+
group.items.forEach((item) => {
|
|
2946
|
+
if ("isGroup" in item && item.isGroup) {
|
|
2947
|
+
count += countActiveFilters(item);
|
|
2948
|
+
} else {
|
|
2949
|
+
const row = item;
|
|
2950
|
+
if (row.field && (["null", "notNull"].includes(row.operator) || row.value && row.value.trim())) {
|
|
2951
|
+
count++;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
return count;
|
|
2956
|
+
};
|
|
2957
|
+
const activeCount = enableNestedGroups && rootGroup ? countActiveFilters(rootGroup) : rows.filter((r) => r.field && (["null", "notNull"].includes(r.operator) || r.value && r.value.trim())).length;
|
|
2958
|
+
const renderGroup = (group, level = 0) => {
|
|
2959
|
+
return /* @__PURE__ */ jsxs(GroupContainer, { $logic: group.logic, $level: level, children: [
|
|
2960
|
+
/* @__PURE__ */ jsxs(GroupHeader, { children: [
|
|
2961
|
+
/* @__PURE__ */ jsxs(LogicBadge, { $logic: group.logic, children: [
|
|
2962
|
+
/* @__PURE__ */ jsx(
|
|
2963
|
+
LogicToggleButton,
|
|
2964
|
+
{
|
|
2965
|
+
$active: group.logic === "AND",
|
|
2966
|
+
$logic: "AND",
|
|
2967
|
+
onClick: () => updateGroupLogic(group.id, "AND"),
|
|
2968
|
+
type: "button",
|
|
2969
|
+
children: "AND"
|
|
2970
|
+
}
|
|
2971
|
+
),
|
|
2972
|
+
/* @__PURE__ */ jsx(
|
|
2973
|
+
LogicToggleButton,
|
|
2974
|
+
{
|
|
2975
|
+
$active: group.logic === "OR",
|
|
2976
|
+
$logic: "OR",
|
|
2977
|
+
onClick: () => updateGroupLogic(group.id, "OR"),
|
|
2978
|
+
type: "button",
|
|
2979
|
+
children: "OR"
|
|
2980
|
+
}
|
|
2981
|
+
)
|
|
2982
|
+
] }),
|
|
2983
|
+
/* @__PURE__ */ jsxs(GroupActions, { children: [
|
|
2984
|
+
/* @__PURE__ */ jsx(SmallButton, { onClick: () => addRowToGroup(group.id), type: "button", children: "+ Row" }),
|
|
2985
|
+
/* @__PURE__ */ jsx(SmallButton, { onClick: () => addNestedGroup(group.id), type: "button", children: "+ Group" }),
|
|
2986
|
+
group.id !== "root" && /* @__PURE__ */ jsx(RemoveGroupButton, { onClick: () => removeGroup(group.id), type: "button", children: "Remove" })
|
|
2987
|
+
] })
|
|
2988
|
+
] }),
|
|
2989
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: group.items.map((item) => {
|
|
2990
|
+
if ("isGroup" in item && item.isGroup) {
|
|
2991
|
+
return renderGroup(item, level + 1);
|
|
2992
|
+
}
|
|
2993
|
+
const row = item;
|
|
2994
|
+
return /* @__PURE__ */ jsxs(FilterRowCard, { children: [
|
|
2995
|
+
/* @__PURE__ */ jsxs(FilterRowTop, { children: [
|
|
2996
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 200px", minWidth: "140px" }, children: /* @__PURE__ */ jsx(
|
|
2997
|
+
CustomSelect,
|
|
2998
|
+
{
|
|
2999
|
+
value: row.field,
|
|
3000
|
+
onChange: (v) => updateRowInGroup(group.id, row.id, { field: v }),
|
|
3001
|
+
options: [
|
|
3002
|
+
{ value: "", label: "Select field..." },
|
|
3003
|
+
...fieldOptions
|
|
3004
|
+
],
|
|
3005
|
+
placeholder: "Select field",
|
|
3006
|
+
searchable: true
|
|
3007
|
+
}
|
|
3008
|
+
) }),
|
|
3009
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 140px", minWidth: "120px" }, children: /* @__PURE__ */ jsx(
|
|
3010
|
+
CustomSelect,
|
|
3011
|
+
{
|
|
3012
|
+
value: row.operator,
|
|
3013
|
+
onChange: (v) => updateRowInGroup(group.id, row.id, { operator: v }),
|
|
3014
|
+
options: getOperatorsForType(row.fieldType).map((o) => ({ value: o.value, label: o.label })),
|
|
3015
|
+
placeholder: "Operator",
|
|
3016
|
+
searchable: false
|
|
3017
|
+
}
|
|
3018
|
+
) }),
|
|
3019
|
+
/* @__PURE__ */ jsxs(NegateCheckbox, { title: "Negate this condition (NOT)", children: [
|
|
3020
|
+
/* @__PURE__ */ jsx(
|
|
3021
|
+
"input",
|
|
3022
|
+
{
|
|
3023
|
+
type: "checkbox",
|
|
3024
|
+
checked: row.negate || false,
|
|
3025
|
+
onChange: (e) => updateRowInGroup(group.id, row.id, { negate: e.target.checked })
|
|
3026
|
+
}
|
|
3027
|
+
),
|
|
3028
|
+
"NOT"
|
|
3029
|
+
] }),
|
|
3030
|
+
/* @__PURE__ */ jsx(
|
|
3031
|
+
DeleteButton,
|
|
3032
|
+
{
|
|
3033
|
+
type: "button",
|
|
3034
|
+
onClick: () => removeRowFromGroup(group.id, row.id),
|
|
3035
|
+
title: "Remove filter",
|
|
3036
|
+
children: /* @__PURE__ */ jsx(Cross, {})
|
|
3037
|
+
}
|
|
3038
|
+
)
|
|
3039
|
+
] }),
|
|
3040
|
+
!["null", "notNull"].includes(row.operator) && /* @__PURE__ */ jsxs(FilterRowValue, { children: [
|
|
3041
|
+
/* @__PURE__ */ jsx(ValueLabel, { children: ["in", "notIn"].includes(row.operator) ? "Enter values (press Enter after each)" : ["between"].includes(row.operator) ? "Enter range" : "Enter value" }),
|
|
3042
|
+
/* @__PURE__ */ jsx(
|
|
3043
|
+
SmartValueInput,
|
|
3044
|
+
{
|
|
3045
|
+
fieldType: row.fieldType,
|
|
3046
|
+
operator: row.operator,
|
|
3047
|
+
value: row.value,
|
|
3048
|
+
valueTo: row.valueTo,
|
|
3049
|
+
onChange: (v) => updateRowInGroup(group.id, row.id, { value: v }),
|
|
3050
|
+
onValueToChange: (v) => updateRowInGroup(group.id, row.id, { valueTo: v }),
|
|
3051
|
+
placeholder: "Value"
|
|
3052
|
+
}
|
|
3053
|
+
)
|
|
3054
|
+
] })
|
|
3055
|
+
] }, row.id);
|
|
3056
|
+
}) })
|
|
3057
|
+
] }, group.id);
|
|
2233
3058
|
};
|
|
2234
|
-
const operatorOptions = OPERATORS.map((o) => ({ value: o.value, label: o.label }));
|
|
2235
3059
|
return /* @__PURE__ */ jsx(ModalOverlay, { onClick: onClose, children: /* @__PURE__ */ jsxs(ModalContent, { padding: 6, onClick: (e) => e.stopPropagation(), children: [
|
|
2236
|
-
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, children: [
|
|
2237
|
-
/* @__PURE__ */
|
|
3060
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 4, style: { flexShrink: 0 }, children: [
|
|
3061
|
+
/* @__PURE__ */ jsxs(Flex, { alignItems: "center", children: [
|
|
3062
|
+
/* @__PURE__ */ jsx(Typography, { as: "h2", variant: "beta", children: "Advanced Filter" }),
|
|
3063
|
+
activeCount > 0 && /* @__PURE__ */ jsx(ActiveBadge, { children: activeCount })
|
|
3064
|
+
] }),
|
|
2238
3065
|
/* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "ghost", type: "button", children: /* @__PURE__ */ jsx(Cross, {}) })
|
|
2239
3066
|
] }),
|
|
2240
|
-
/* @__PURE__ */ jsx(
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
children: "and"
|
|
2249
|
-
}
|
|
2250
|
-
),
|
|
2251
|
-
/* @__PURE__ */ jsx("span", { style: { margin: "0 8px", color: "var(--colors-neutral500, #8e8ea9)" }, children: "|" }),
|
|
2252
|
-
/* @__PURE__ */ jsx(
|
|
2253
|
-
ConnectorButton,
|
|
2254
|
-
{
|
|
2255
|
-
type: "button",
|
|
2256
|
-
$active: connectors[i - 1]?.logic === "OR",
|
|
2257
|
-
onClick: () => updateConnector(i - 1, "OR"),
|
|
2258
|
-
children: "or"
|
|
2259
|
-
}
|
|
2260
|
-
)
|
|
2261
|
-
] }),
|
|
2262
|
-
/* @__PURE__ */ jsxs(FilterRowContainer, { children: [
|
|
2263
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 140px", minWidth: "120px" }, children: /* @__PURE__ */ jsx(
|
|
2264
|
-
CustomSelect,
|
|
2265
|
-
{
|
|
2266
|
-
value: row.field,
|
|
2267
|
-
onChange: (v) => updateRow(i, { field: v }),
|
|
2268
|
-
options: [
|
|
2269
|
-
{ value: "", label: "Select field..." },
|
|
2270
|
-
...fieldOptions
|
|
2271
|
-
],
|
|
2272
|
-
placeholder: "Select field",
|
|
2273
|
-
searchable: true
|
|
2274
|
-
}
|
|
2275
|
-
) }),
|
|
2276
|
-
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 130px", minWidth: "100px" }, children: /* @__PURE__ */ jsx(
|
|
2277
|
-
CustomSelect,
|
|
2278
|
-
{
|
|
2279
|
-
value: row.operator,
|
|
2280
|
-
onChange: (v) => updateRow(i, { operator: v }),
|
|
2281
|
-
options: [
|
|
2282
|
-
{ value: "eq", label: "is" },
|
|
2283
|
-
...operatorOptions.filter((o) => o.value !== "eq")
|
|
2284
|
-
],
|
|
2285
|
-
placeholder: "Operator",
|
|
2286
|
-
searchable: false
|
|
2287
|
-
}
|
|
2288
|
-
) }),
|
|
2289
|
-
!["null", "notNull"].includes(row.operator) && /* @__PURE__ */ jsx(
|
|
2290
|
-
ValueInput,
|
|
2291
|
-
{
|
|
2292
|
-
type: "text",
|
|
2293
|
-
value: row.value,
|
|
2294
|
-
onChange: (e) => updateRow(i, { value: e.target.value }),
|
|
2295
|
-
placeholder: "Value"
|
|
2296
|
-
}
|
|
2297
|
-
),
|
|
2298
|
-
/* @__PURE__ */ jsx(
|
|
2299
|
-
DeleteButton,
|
|
2300
|
-
{
|
|
2301
|
-
type: "button",
|
|
2302
|
-
onClick: () => removeRow(i),
|
|
2303
|
-
title: "Remove filter",
|
|
2304
|
-
children: /* @__PURE__ */ jsx(Cross, {})
|
|
2305
|
-
}
|
|
2306
|
-
)
|
|
3067
|
+
/* @__PURE__ */ jsx(ModalScrollArea, { children: enableNestedGroups && rootGroup ? (
|
|
3068
|
+
// Nested Groups Mode
|
|
3069
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3070
|
+
renderGroup(rootGroup, 0),
|
|
3071
|
+
relationsInUse.size > 0 && /* @__PURE__ */ jsxs(InfoText, { children: [
|
|
3072
|
+
Array.from(relationsInUse).join(", "),
|
|
3073
|
+
" will be loaded automatically"
|
|
3074
|
+
] })
|
|
2307
3075
|
] })
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
3076
|
+
) : (
|
|
3077
|
+
// Flat Mode
|
|
3078
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3079
|
+
/* @__PURE__ */ jsx(RowWrapper, { children: rows.map((row, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
3080
|
+
i > 0 && /* @__PURE__ */ jsxs(ConnectorPill, { children: [
|
|
3081
|
+
/* @__PURE__ */ jsx(
|
|
3082
|
+
ConnectorButton,
|
|
3083
|
+
{
|
|
3084
|
+
type: "button",
|
|
3085
|
+
$active: connectors[i - 1]?.logic === "AND",
|
|
3086
|
+
onClick: () => updateConnector(i - 1, "AND"),
|
|
3087
|
+
children: "and"
|
|
3088
|
+
}
|
|
3089
|
+
),
|
|
3090
|
+
/* @__PURE__ */ jsx("span", { style: { margin: "0 8px", color: "var(--colors-neutral500, #8e8ea9)" }, children: "|" }),
|
|
3091
|
+
/* @__PURE__ */ jsx(
|
|
3092
|
+
ConnectorButton,
|
|
3093
|
+
{
|
|
3094
|
+
type: "button",
|
|
3095
|
+
$active: connectors[i - 1]?.logic === "OR",
|
|
3096
|
+
onClick: () => updateConnector(i - 1, "OR"),
|
|
3097
|
+
children: "or"
|
|
3098
|
+
}
|
|
3099
|
+
)
|
|
3100
|
+
] }),
|
|
3101
|
+
/* @__PURE__ */ jsxs(FilterRowCard, { children: [
|
|
3102
|
+
/* @__PURE__ */ jsxs(FilterRowTop, { children: [
|
|
3103
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 200px", minWidth: "140px" }, children: /* @__PURE__ */ jsx(
|
|
3104
|
+
CustomSelect,
|
|
3105
|
+
{
|
|
3106
|
+
value: row.field,
|
|
3107
|
+
onChange: (v) => updateRow(i, { field: v }),
|
|
3108
|
+
options: [
|
|
3109
|
+
{ value: "", label: "Select field..." },
|
|
3110
|
+
...fieldOptions
|
|
3111
|
+
],
|
|
3112
|
+
placeholder: "Select field",
|
|
3113
|
+
searchable: true
|
|
3114
|
+
}
|
|
3115
|
+
) }),
|
|
3116
|
+
/* @__PURE__ */ jsx(Box, { style: { flex: "1 1 140px", minWidth: "120px" }, children: /* @__PURE__ */ jsx(
|
|
3117
|
+
CustomSelect,
|
|
3118
|
+
{
|
|
3119
|
+
value: row.operator,
|
|
3120
|
+
onChange: (v) => updateRow(i, { operator: v }),
|
|
3121
|
+
options: getOperatorsForType(row.fieldType).map((o) => ({ value: o.value, label: o.label })),
|
|
3122
|
+
placeholder: "Operator",
|
|
3123
|
+
searchable: false
|
|
3124
|
+
}
|
|
3125
|
+
) }),
|
|
3126
|
+
/* @__PURE__ */ jsxs(NegateCheckbox, { title: "Negate this condition (NOT)", children: [
|
|
3127
|
+
/* @__PURE__ */ jsx(
|
|
3128
|
+
"input",
|
|
3129
|
+
{
|
|
3130
|
+
type: "checkbox",
|
|
3131
|
+
checked: row.negate || false,
|
|
3132
|
+
onChange: (e) => updateRow(i, { negate: e.target.checked })
|
|
3133
|
+
}
|
|
3134
|
+
),
|
|
3135
|
+
"NOT"
|
|
3136
|
+
] }),
|
|
3137
|
+
/* @__PURE__ */ jsx(
|
|
3138
|
+
DeleteButton,
|
|
3139
|
+
{
|
|
3140
|
+
type: "button",
|
|
3141
|
+
onClick: () => removeRow(i),
|
|
3142
|
+
title: "Remove filter",
|
|
3143
|
+
children: /* @__PURE__ */ jsx(Cross, {})
|
|
3144
|
+
}
|
|
3145
|
+
)
|
|
3146
|
+
] }),
|
|
3147
|
+
!["null", "notNull"].includes(row.operator) && /* @__PURE__ */ jsxs(FilterRowValue, { children: [
|
|
3148
|
+
/* @__PURE__ */ jsx(ValueLabel, { children: ["in", "notIn"].includes(row.operator) ? "Enter values (press Enter after each)" : ["between"].includes(row.operator) ? "Enter range" : "Enter value" }),
|
|
3149
|
+
/* @__PURE__ */ jsx(
|
|
3150
|
+
SmartValueInput,
|
|
3151
|
+
{
|
|
3152
|
+
fieldType: row.fieldType,
|
|
3153
|
+
operator: row.operator,
|
|
3154
|
+
value: row.value,
|
|
3155
|
+
valueTo: row.valueTo,
|
|
3156
|
+
onChange: (v) => updateRow(i, { value: v }),
|
|
3157
|
+
onValueToChange: (v) => updateRow(i, { valueTo: v }),
|
|
3158
|
+
placeholder: "Value"
|
|
3159
|
+
}
|
|
3160
|
+
)
|
|
3161
|
+
] })
|
|
3162
|
+
] })
|
|
3163
|
+
] }, row.id)) }),
|
|
3164
|
+
relationsInUse.size > 0 && /* @__PURE__ */ jsxs(InfoText, { children: [
|
|
3165
|
+
Array.from(relationsInUse).join(", "),
|
|
3166
|
+
" will be loaded automatically"
|
|
3167
|
+
] })
|
|
3168
|
+
] })
|
|
3169
|
+
) }),
|
|
3170
|
+
/* @__PURE__ */ jsx(ModalFooter, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", gap: 2, children: [
|
|
2315
3171
|
/* @__PURE__ */ jsx(Button, { onClick: handleClearAll, variant: "tertiary", children: "Clear All" }),
|
|
2316
|
-
/* @__PURE__ */ jsx(
|
|
2317
|
-
|
|
3172
|
+
!enableNestedGroups && /* @__PURE__ */ jsx(AddButton, { type: "button", onClick: addRow, children: "+ Add Filter" }),
|
|
3173
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleApply, variant: "default", children: "Apply" })
|
|
3174
|
+
] }) })
|
|
2318
3175
|
] }) });
|
|
2319
3176
|
};
|
|
3177
|
+
const FEATURES = {
|
|
3178
|
+
// Free tier features
|
|
3179
|
+
basicBookmarks: { tier: "free", limit: 10 },
|
|
3180
|
+
basicFilters: { tier: "free" },
|
|
3181
|
+
// Premium tier features
|
|
3182
|
+
extendedBookmarks: { tier: "premium", limit: 50 },
|
|
3183
|
+
queryHistory: { tier: "premium" },
|
|
3184
|
+
exportBookmarks: { tier: "premium" },
|
|
3185
|
+
sharedBookmarks: { tier: "premium" },
|
|
3186
|
+
// Advanced tier features
|
|
3187
|
+
unlimitedBookmarks: { tier: "advanced", limit: -1 },
|
|
3188
|
+
advancedFilters: { tier: "advanced" },
|
|
3189
|
+
subGroups: { tier: "advanced" },
|
|
3190
|
+
bulkOperations: { tier: "advanced" },
|
|
3191
|
+
analytics: { tier: "advanced" },
|
|
3192
|
+
customIntegrations: { tier: "advanced" }
|
|
3193
|
+
};
|
|
3194
|
+
const useLicenseInfo = () => {
|
|
3195
|
+
const { get } = useFetchClient();
|
|
3196
|
+
const [limits, setLimits] = useState(null);
|
|
3197
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
3198
|
+
useEffect(() => {
|
|
3199
|
+
const fetchLimits = async () => {
|
|
3200
|
+
try {
|
|
3201
|
+
console.log("[useLicenseInfo] Fetching license limits...");
|
|
3202
|
+
const response = await get("/magic-mark/license/limits");
|
|
3203
|
+
console.log("[useLicenseInfo] Raw API response:", response);
|
|
3204
|
+
console.log("[useLicenseInfo] Response data:", response.data);
|
|
3205
|
+
if (response.data?.success) {
|
|
3206
|
+
console.log("[useLicenseInfo] Setting limits:", response.data.data);
|
|
3207
|
+
setLimits(response.data.data);
|
|
3208
|
+
} else {
|
|
3209
|
+
console.warn("[useLicenseInfo] API returned success=false or missing data");
|
|
3210
|
+
}
|
|
3211
|
+
} catch (error) {
|
|
3212
|
+
console.error("[useLicenseInfo] Error fetching license limits:", error);
|
|
3213
|
+
} finally {
|
|
3214
|
+
setIsLoading(false);
|
|
3215
|
+
}
|
|
3216
|
+
};
|
|
3217
|
+
fetchLimits();
|
|
3218
|
+
}, []);
|
|
3219
|
+
const tier = limits?.tier || "free";
|
|
3220
|
+
const isPremium = tier === "premium" || tier === "advanced";
|
|
3221
|
+
const isAdvanced = tier === "advanced";
|
|
3222
|
+
console.log("[useLicenseInfo] Computed values:", { tier, isPremium, isAdvanced, limits });
|
|
3223
|
+
return {
|
|
3224
|
+
isLoading,
|
|
3225
|
+
limits,
|
|
3226
|
+
tier,
|
|
3227
|
+
isFree: tier === "free",
|
|
3228
|
+
isPremium,
|
|
3229
|
+
isAdvanced,
|
|
3230
|
+
canUseFeature: (feature) => {
|
|
3231
|
+
const featureConfig = FEATURES[feature];
|
|
3232
|
+
const tierOrder = { free: 0, premium: 1, advanced: 2 };
|
|
3233
|
+
return tierOrder[tier] >= tierOrder[featureConfig.tier];
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
};
|
|
2320
3237
|
const FilterButtonGroup = styled.div`
|
|
2321
3238
|
display: flex;
|
|
2322
3239
|
align-items: center;
|
|
@@ -2394,6 +3311,7 @@ const AdvancedFilterButton = () => {
|
|
|
2394
3311
|
const navigate = useNavigate();
|
|
2395
3312
|
const location = useLocation();
|
|
2396
3313
|
const { get } = useFetchClient();
|
|
3314
|
+
const { isPremium, isAdvanced } = useLicenseInfo();
|
|
2397
3315
|
const [availableFields, setAvailableFields] = useState([]);
|
|
2398
3316
|
const [availableRelations, setAvailableRelations] = useState([]);
|
|
2399
3317
|
const [hasActiveFilters, setHasActiveFilters] = useState(false);
|
|
@@ -2538,7 +3456,7 @@ const AdvancedFilterButton = () => {
|
|
|
2538
3456
|
title: "Open advanced filter builder",
|
|
2539
3457
|
children: [
|
|
2540
3458
|
/* @__PURE__ */ jsx(Filter, {}),
|
|
2541
|
-
"
|
|
3459
|
+
"Advanced Filter",
|
|
2542
3460
|
hasActiveFilters && /* @__PURE__ */ jsx(ActiveDot, {})
|
|
2543
3461
|
]
|
|
2544
3462
|
}
|
|
@@ -2558,7 +3476,8 @@ const AdvancedFilterButton = () => {
|
|
|
2558
3476
|
onApply: handleApplyFilters,
|
|
2559
3477
|
availableFields,
|
|
2560
3478
|
availableRelations,
|
|
2561
|
-
currentQuery: getCurrentFilters()
|
|
3479
|
+
currentQuery: getCurrentFilters(),
|
|
3480
|
+
enableNestedGroups: isPremium || isAdvanced
|
|
2562
3481
|
}
|
|
2563
3482
|
)
|
|
2564
3483
|
] });
|
|
@@ -2581,7 +3500,7 @@ const index = {
|
|
|
2581
3500
|
id: `${pluginId}.Admin.MainMenu.PluginName`,
|
|
2582
3501
|
defaultMessage: name
|
|
2583
3502
|
},
|
|
2584
|
-
Component: () => import("./App-
|
|
3503
|
+
Component: () => import("./App-D_Vllkur.mjs"),
|
|
2585
3504
|
permissions: []
|
|
2586
3505
|
});
|
|
2587
3506
|
app.createSettingSection(
|
|
@@ -2602,7 +3521,7 @@ const index = {
|
|
|
2602
3521
|
to: `${pluginId}/upgrade`,
|
|
2603
3522
|
Component: () => import(
|
|
2604
3523
|
/* webpackChunkName: "magic-mark-upgrade" */
|
|
2605
|
-
"./UpgradePage-
|
|
3524
|
+
"./UpgradePage-DuyIskve.mjs"
|
|
2606
3525
|
),
|
|
2607
3526
|
permissions: []
|
|
2608
3527
|
},
|
|
@@ -2690,5 +3609,6 @@ export {
|
|
|
2690
3609
|
CreateEditModal as C,
|
|
2691
3610
|
getIconById as g,
|
|
2692
3611
|
index as i,
|
|
2693
|
-
pluginId as p
|
|
3612
|
+
pluginId as p,
|
|
3613
|
+
useLicenseInfo as u
|
|
2694
3614
|
};
|