Skip to content

Commit dae821d

Browse files
Adrienjcabannes
authored andcommitted
feat(catalog-ui): use url to fetch treeNode for tree search filter
1 parent e510e42 commit dae821d

5 files changed

Lines changed: 554 additions & 30 deletions

File tree

apps/catalog-ui/docs/components/smart-filter/LinidSmartFilter.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ The component uses scoped i18n with the following translation keys:
352352
| `[INSTANCE_ID].LinidSmartFilter.LinidFavoritePanel.overrideFavorite` | Label for override button | QBtn label | - |
353353
| `[INSTANCE_ID].LinidSmartFilter.LinidFavoritePanel.deleteButton` | Label for delete button (optional, defaults to empty string) | QBtn label | - |
354354
| `[INSTANCE_ID].LinidSmartFilter.TreeSearchFilterPanel.searchButton` | Search button label | QBtn label | - |
355+
| `[INSTANCE_ID].LinidSmartFilter.TreeSearchFilterPanel.errorLoadingData` | Error message when failing to load tree data | Notification message | - |
355356
| `[INSTANCE_ID].LinidSmartFilter.ListSearchFilterPanel.searchButton` | Search button label | QBtn label | - |
356357
| `[INSTANCE_ID].LinidSmartFilter.DateSearchFilterPanel.inputLabel` | Date input label (optional, defaults to empty string) | QInput label | - |
357358
| `[INSTANCE_ID].LinidSmartFilter.DateSearchFilterPanel.inputHint` | Date input hint (optional, defaults to empty string) | QInput hint | - |
@@ -428,7 +429,8 @@ Example:
428429
}
429430
},
430431
"TreeSearchFilterPanel": {
431-
"searchButton": "Search"
432+
"searchButton": "Search",
433+
"errorLoadingData": "Failed to load tree data."
432434
},
433435
"ListSearchFilterPanel": {
434436
"searchButton": "Search"

apps/catalog-ui/docs/i18n.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ The `FormDialog` component uses the following keys for internationalization:
490490
| `[INSTANCE_ID].LinidSmartFilter.LinidFavoritePanel.overrideFavorite` | Label for override button | QBtn label | - |
491491
| `[INSTANCE_ID].LinidSmartFilter.LinidFavoritePanel.deleteButton` | Label for delete button (optional, defaults to empty string) | QBtn label | - |
492492
| `[INSTANCE_ID].LinidSmartFilter.TreeSearchFilterPanel.searchButton` | Search button label | QBtn label | - |
493+
| `[INSTANCE_ID].LinidSmartFilter.TreeSearchFilterPanel.errorLoadingData` | Error message when failing to load tree data | Notification message | - |
493494
| `[INSTANCE_ID].LinidSmartFilter.ListSearchFilterPanel.searchButton` | Search button label | QBtn label | - |
494495
| `[INSTANCE_ID].LinidSmartFilter.DateSearchFilterPanel.inputLabel` | Date input label (optional, defaults to empty string) | QInput label | - |
495496
| `[INSTANCE_ID].LinidSmartFilter.DateSearchFilterPanel.inputHint` | Date input hint (optional, defaults to empty string) | QInput hint | - |
@@ -567,7 +568,8 @@ The `FormDialog` component uses the following keys for internationalization:
567568
}
568569
},
569570
"TreeSearchFilterPanel": {
570-
"searchButton": "Search"
571+
"searchButton": "Search",
572+
"errorLoadingData": "Failed to load tree data."
571573
},
572574
"ListSearchFilterPanel": {
573575
"searchButton": "Search"
@@ -709,6 +711,7 @@ export default {
709711
},
710712
TreeSearchFilterPanel: {
711713
searchButton: 'Search',
714+
errorLoadingData: 'Failed to load tree data.',
712715
},
713716
ListSearchFilterPanel: {
714717
searchButton: 'Search',
@@ -885,6 +888,7 @@ export default {
885888
},
886889
TreeSearchFilterPanel: {
887890
searchButton: 'Search',
891+
errorLoadingData: 'Failed to load tree data.',
888892
},
889893
ListSearchFilterPanel: {
890894
searchButton: 'Search',

apps/catalog-ui/src/components/smart-filter/TreeSearchFilterPanel.vue

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
:tickeable="true"
3333
:ui-namespace="localUiNamespace"
3434
:i18n-scope="localI18n"
35-
:nodes="props.items"
35+
:nodes="items"
3636
:node-types="[]"
3737
/>
3838
<q-btn
@@ -47,12 +47,22 @@
4747
</template>
4848

4949
<script setup lang="ts">
50-
import type { LinidQBtnProps } from '@linagora/linid-im-front-corelib';
51-
import { LinidFilterValue } from '@linagora/linid-im-front-corelib';
52-
import { useScopedI18n, useUiDesign } from '@linagora/linid-im-front-corelib';
50+
import type {
51+
LinidQBtnProps,
52+
Page,
53+
Pagination,
54+
TreeNode,
55+
} from '@linagora/linid-im-front-corelib';
56+
import {
57+
useNotify,
58+
getHttpClient,
59+
LinidFilterValue,
60+
useScopedI18n,
61+
useUiDesign,
62+
} from '@linagora/linid-im-front-corelib';
5363
import GenericTree from '../tree/GenericTree.vue';
5464
import type { TreeSearchFilterProps } from '../../types/TreeSearchFilterPanel';
55-
import { ref } from 'vue';
65+
import { onMounted, ref } from 'vue';
5666
import type { LinidFilterPanelSearchOutputs } from '../../types/linidFilterPanel';
5767
5868
const props = defineProps<TreeSearchFilterProps>();
@@ -64,6 +74,8 @@ const { t } = useScopedI18n(localI18n);
6474
const localUiNamespace = `${props.uiNamespace}.tree-search-filter-panel`;
6575
6676
const tickedNodeKeys = ref<string[]>([]);
77+
const items = ref<TreeNode<Record<string, unknown>>[]>([]);
78+
const { Notify } = useNotify();
6779
6880
const uiProps = {
6981
searchButton: ui<LinidQBtnProps>(localUiNamespace, 'q-btn'),
@@ -83,6 +95,121 @@ function onSearch() {
8395
values: filterValues,
8496
});
8597
}
98+
99+
/**
100+
* Fetches nodes from the specified URL with pagination.
101+
* @param pagination - Pagination parameters for the request.
102+
* @returns A promise that resolves to a page of nodes.
103+
*/
104+
async function getNodes(pagination: Pagination) {
105+
const response = await getHttpClient().get<Page<Record<string, unknown>>>(
106+
props.url,
107+
{ params: { ...pagination } }
108+
);
109+
if (!response || !response.data) {
110+
return { content: [], last: true };
111+
}
112+
const { data } = response;
113+
return data;
114+
}
115+
116+
/**
117+
* Fetches all nodes from the specified URL by iterating through paginated results.
118+
* @returns A promise that resolves to an array of all nodes.
119+
*/
120+
async function getAllNodes(): Promise<Record<string, unknown>[]> {
121+
const result: Record<string, unknown>[] = [];
122+
let page = 0;
123+
let isLast = false;
124+
125+
while (!isLast) {
126+
const response = await getNodes({ page, size: props.nodesQuerySize });
127+
128+
if (!response || !response.content) {
129+
break;
130+
}
131+
132+
result.push(...response.content);
133+
isLast = response.last ?? true;
134+
page++;
135+
}
136+
137+
return result;
138+
}
139+
140+
/**
141+
* Maps an array of nodes to an array of TreeNode objects.
142+
* @param nodes - The array of nodes to map.
143+
* @param idKey - The key used to identify each node.
144+
* @returns An array of TreeNode objects.
145+
*/
146+
function toTreeNode(
147+
nodes: Record<string, unknown>[],
148+
idKey: string
149+
): TreeNode<Record<string, unknown>>[] {
150+
const childNodes = nodes.filter((item) => {
151+
const parentValue = item[props.parentsKey];
152+
return (
153+
Array.isArray(parentValue) &&
154+
parentValue.some((parent) => parent[props.parentIdKey] === idKey)
155+
);
156+
});
157+
if (childNodes && childNodes.length > 0) {
158+
const result: TreeNode<Record<string, unknown>>[] = [];
159+
childNodes.forEach((childNode: Record<string, unknown>) => {
160+
result.push({
161+
key: String(childNode[props.idKey]),
162+
value: childNode,
163+
nodes: toTreeNode(nodes, String(childNode[props.idKey])),
164+
type:
165+
(childNode[props.typeKey] as string) || props.defaultTypeValue || '',
166+
});
167+
});
168+
return result;
169+
}
170+
return [];
171+
}
172+
173+
/**
174+
* Fetches all nodes, identifies the root node, and constructs the tree structure for the component.
175+
*/
176+
async function loadData() {
177+
try {
178+
const nodes: Record<string, unknown>[] = await getAllNodes();
179+
180+
if (!nodes || nodes.length === 0) {
181+
return;
182+
}
183+
184+
const root = nodes.find((item) => {
185+
const parentValue = item[props.parentsKey];
186+
return (
187+
Array.isArray(parentValue) &&
188+
parentValue.length > 0 &&
189+
parentValue[0][props.parentIdKey] === null
190+
);
191+
});
192+
193+
if (root) {
194+
items.value.push({
195+
key: String(root[props.idKey]),
196+
value: root,
197+
nodes: toTreeNode(nodes, String(root[props.idKey])),
198+
type: 'root',
199+
});
200+
}
201+
} catch (error) {
202+
Notify({
203+
type: 'negative',
204+
message: t('errorLoadingData'),
205+
});
206+
throw error;
207+
}
208+
}
209+
210+
onMounted(() => {
211+
loadData();
212+
});
86213
</script>
87214

88215
<style scoped></style>

apps/catalog-ui/src/types/TreeSearchFilterPanel.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
*/
2626

2727
import type { CommonComponentProps } from './common';
28-
import type { TreeNode } from '@linagora/linid-im-front-corelib';
2928

3029
/**
3130
* Props definition for the TreeSearchFilter component.
@@ -37,7 +36,37 @@ export interface TreeSearchFilterProps extends CommonComponentProps {
3736
fieldName: string;
3837

3938
/**
40-
* Hierarchical tree structure used by GenericTree.
39+
* URL endpoint to fetch tree data.
4140
*/
42-
items: TreeNode<unknown>[];
41+
url: string;
42+
43+
/**
44+
* Property name used as the node identifier.
45+
*/
46+
idKey: string;
47+
48+
/**
49+
* Property name used as the parent reference in hierarchical relationships.
50+
*/
51+
parentsKey: string;
52+
53+
/**
54+
* Property name used as the parent id in the parentsKey value.
55+
*/
56+
parentIdKey: string;
57+
58+
/**
59+
* Number of nodes to fetch per page when querying the API.
60+
*/
61+
nodesQuerySize: number;
62+
63+
/**
64+
* Property name used as the node type.
65+
*/
66+
typeKey: string;
67+
68+
/**
69+
* Property name used as the node type.
70+
*/
71+
defaultTypeValue?: string;
4372
}

0 commit comments

Comments
 (0)