@c-rex/templates 0.1.28 → 0.1.30

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.
@@ -1,384 +1,384 @@
1
- "use client";
2
-
3
- import React, { FC, useEffect, useMemo, useState } from "react";
4
- import { Funnel, Settings2, X } from "lucide-react"
5
- import { useTranslations } from 'next-intl'
6
- import { parseAsBoolean, parseAsInteger, parseAsString, useQueryStates } from 'nuqs'
7
- import { informationUnitsResponse } from "@c-rex/interfaces";
8
- import { Button } from "@c-rex/ui/button";
9
- import { Badge } from "@c-rex/ui/badge";
10
- import { ResultContainer } from "@c-rex/components/result-container";
11
- import { DialogFilter } from "@c-rex/components/dialog-filter";
12
- import { AutoComplete } from "@c-rex/components/autocomplete";
13
- import { DEVICE_OPTIONS, OPERATOR_OPTIONS } from "@c-rex/constants";
14
- import { useSearchContext } from "@c-rex/contexts/search";
15
- import { FilterSidebar } from "./components/filter-sidebar";
16
- import { Sheet } from "@c-rex/ui/sheet";
17
- import { useBreakpoint } from "@c-rex/ui/hooks";
18
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
19
-
20
- interface HomePageProps {
21
- data: informationUnitsResponse;
22
- }
23
-
24
- type filterModel = {
25
- key: string
26
- name?: string
27
- value: string
28
- default?: string | boolean | null
29
- removable: boolean
30
- }
31
-
32
- type TagsType = {
33
- [key: string]: any[]
34
- }
35
-
36
- export const HomePage: FC<HomePageProps> = ({ data }) => {
37
- const t = useTranslations();
38
- const { setLoading } = useSearchContext();
39
- const device = useBreakpoint();
40
- const isMobile = device !== null && device === DEVICE_OPTIONS.MOBILE;
41
- const [open, setOpen] = useState<boolean>(false)
42
- const [params, setParams] = useQueryStates({
43
- language: parseAsString,
44
- page: parseAsInteger,
45
- wildcard: parseAsString,
46
- operator: parseAsString,
47
- packages: parseAsString,
48
- filter: parseAsString,
49
- like: parseAsBoolean,
50
- search: {
51
- defaultValue: "",
52
- parse(value) {
53
- return value
54
- },
55
- },
56
- }, {
57
- history: 'push',
58
- shallow: false,
59
- });
60
-
61
- const tags: TagsType = useMemo(() => {
62
- if (!data) return {};
63
- const newTags = { ...data.tags }
64
-
65
- if (params.filter !== null) {
66
- const splittedParam = params.filter.split(",")
67
-
68
- splittedParam.forEach((item) => {
69
- const aux = item.split(".shortId=")
70
- const name = aux[0] as string
71
- const shortId = aux[1] as string
72
-
73
- if (!Object.keys(newTags).includes(name)) {
74
- newTags[name] = []
75
- }
76
-
77
- newTags[name].forEach((el: any) => {
78
- if (el.shortId == shortId) {
79
- el.active = true
80
- } else {
81
- el.active = false
82
- }
83
- })
84
- })
85
- } else {
86
- Object.keys(newTags).forEach((key) => {
87
- newTags[key].forEach((el: any) => {
88
- el.active = false
89
- })
90
- })
91
- }
92
-
93
- if (params.packages !== null && newTags["packages"]) {
94
- newTags["packages"].forEach((el: any) => {
95
- if (el.shortId == params.packages) {
96
- el.active = true
97
- } else {
98
- el.active = false
99
- }
100
- })
101
- }
102
-
103
- return newTags
104
- }, [data, params]);
105
-
106
- const filters = useMemo(() => {
107
- if (!tags || params.search.length === 0) return [];
108
-
109
- const filters: filterModel[] = [{
110
- key: "operator",
111
- name: t("filter.operator"),
112
- value: `${params?.operator !== OPERATOR_OPTIONS.OR}`,
113
- removable: false
114
- }, {
115
- key: "like",
116
- name: t("filter.like"),
117
- value: `${params.like}`,
118
- removable: false
119
- }, {
120
- key: "wildcard",
121
- name: "Wildcard",
122
- value: params.wildcard as string,
123
- removable: false,
124
- }]
125
-
126
- const languages = params.language?.split(",")
127
- languages?.forEach((item) => {
128
- const aux = languages?.filter(langItem => langItem !== item)
129
- filters.push({ key: "language", name: t(`languages`), value: item.toUpperCase(), removable: languages.length > 1, default: aux.join(",") })
130
- })
131
-
132
- if (params.filter !== null) {
133
- const splittedParam = params.filter.split(",")
134
-
135
- splittedParam.forEach((item, index) => {
136
- const aux = item.split(".shortId=")
137
- const name = aux[0] as string
138
- const shortId = aux[1] as string
139
-
140
- const defaultValue = [...splittedParam]
141
- defaultValue.splice(index, 1)
142
-
143
- if (!Object.keys(tags).includes(name) || !tags[name]) return;
144
-
145
- const tag = tags[name].find(el => el.shortId === shortId)
146
- if (!tag) return;
147
-
148
- const value = defaultValue.length == 0 ? null : defaultValue.join(",")
149
-
150
- filters.push({
151
- key: "filter",
152
- name: t(`filter.tags.${name}`),
153
- value: tag.label,
154
- removable: true,
155
- default: value
156
- })
157
- })
158
- }
159
-
160
- if (params.packages !== null) {
161
- let packageTag = {
162
- label: params.packages,
163
- }
164
-
165
- if (tags["packages"]) {
166
- packageTag = tags["packages"]?.find(el => el.shortId === params.packages)
167
- }
168
-
169
- filters.push({
170
- key: "packages",
171
- name: t("filter.tags.packages"),
172
- value: packageTag.label,
173
- removable: true,
174
- default: null
175
- })
176
- }
177
-
178
- Object.keys(params)
179
- .filter(item => !["page", "search", "language", "operator", "like", "wildcard", "filter", "packages"].includes(item))
180
- .forEach(item => {
181
- if (params[item as keyof typeof params] === null || params[item as keyof typeof params] === undefined) return;
182
- const value = params[item as keyof typeof params] as string
183
- filters.push({ key: item, value: value, removable: true, default: null })
184
- })
185
-
186
- return filters
187
- }, [tags, params]);
188
-
189
- useEffect(() => setLoading(false), [data])
190
-
191
- const updateFilterParam = (key: string, item: any) => {
192
- setLoading(true)
193
-
194
- if (item.active) {
195
- removeFilterItem(key, item)
196
- setOpen(false);
197
- return;
198
- }
199
-
200
- if (key === "packages") {
201
- setParams({ packages: item.shortId })
202
- return;
203
- }
204
-
205
- const value = `${key}.shortId=${item.shortId}`
206
- let aux = value
207
-
208
- if (params.filter != null) {
209
- const splittedParam = params.filter.split(",")
210
- const finalValue = [...splittedParam]
211
-
212
- const hasParams = params.filter.includes(key)
213
-
214
- if (hasParams) {
215
- let mainIndex = -1
216
-
217
- splittedParam.forEach((el, index) => {
218
- if (el.includes(key)) {
219
- mainIndex = index
220
- }
221
- })
222
- finalValue[mainIndex] = value
223
- } else {
224
- finalValue.push(value)
225
- }
226
-
227
- aux = finalValue.join(",")
228
- }
229
-
230
- setParams({ filter: aux })
231
- setOpen(false);
232
- };
233
-
234
- const removeFilterItem = (key: string, item: any) => {
235
- if (key === "packages") {
236
- setParams({ packages: null })
237
- return;
238
- }
239
-
240
- if (params.filter !== null) {
241
- const value = `${key}.shortId=${item.shortId}`
242
- const newValue = params.filter.split(",").filter(item => item !== value).join(",")
243
- setParams({ filter: newValue.length === 0 ? null : newValue })
244
- }
245
- }
246
-
247
- return (
248
- <div className="container">
249
- <div className="flex gap-4 py-6">
250
- <div className="flex-1">
251
- <AutoComplete
252
- embedded={false}
253
- initialValue={params.search}
254
- searchByPackage={false}
255
- />
256
- </div>
257
- <DialogFilter
258
- trigger={(
259
- <Button variant="default">
260
- <span className="hidden sm:inline">
261
- {t("searchSettings")}
262
- </span>
263
- <Settings2 className="h-4 w-4" />
264
- </Button>
265
- )}
266
- />
267
- </div>
268
-
269
- {filters != null && filters.length > 0 && (
270
- <div className="pb-4 flex justify-between">
271
-
272
- <div className="flex flex-wrap gap-2">
273
- <Button
274
- size="sm"
275
- variant="secondary"
276
- onClick={() => setOpen(true)}
277
- className="md:hidden"
278
- >
279
- <Funnel className="h-2" />
280
- </Button>
281
-
282
- {filters.length > 0 && (
283
- <>
284
- {filters.slice(0, 1).map((item) => (
285
- <Badge
286
- key={`${item.key}-${item?.value}`}
287
- variant="outline"
288
- className="h-8"
289
- >
290
- {item?.name ? item.name : item.key}: {item.value}
291
-
292
- {item.removable && (
293
- <Button size="xs" variant="ghost" onClick={() => {
294
- setLoading(true)
295
- setParams({ [item.key]: item?.default })
296
- }}>
297
- <X className="h-2" />
298
- </Button>
299
- )}
300
- </Badge>
301
- ))}
302
-
303
- {isMobile ? (
304
- <TooltipProvider>
305
- <Tooltip delayDuration={100}>
306
- <TooltipTrigger>
307
- <Badge
308
- key={`grouped-filters`}
309
- variant="outline"
310
- className="h-8"
311
- >
312
- +{filters.length - 1} {t("filter.filters")}
313
- </Badge>
314
-
315
- </TooltipTrigger>
316
-
317
- <TooltipContent>
318
- {filters.slice(1).map((item) => {
319
- const label = item?.name ? item.name : item.key
320
- const returnString = `${label}: ${item.value}`;
321
- return <div className="capitalize" key={returnString}>{returnString}</div>;
322
- })}
323
- </TooltipContent>
324
- </Tooltip>
325
- </TooltipProvider>
326
- ) : (
327
- <>
328
- {filters.slice(1).map((item) => (
329
- <Badge
330
- key={`${item.key}-${item?.value}`}
331
- variant="outline"
332
- className="h-8"
333
- >
334
- {item?.name ? item.name : item.key}: {item.value}
335
-
336
- {item.removable && (
337
- <Button size="xs" variant="ghost" onClick={() => {
338
- setLoading(true)
339
- setParams({ [item.key]: item?.default })
340
- }}>
341
- <X className="h-2" />
342
- </Button>
343
- )}
344
- </Badge>
345
- ))}
346
- </>
347
- )}
348
- </>
349
- )}
350
- </div>
351
-
352
- <Button
353
- size="sm"
354
- variant="secondary"
355
- onClick={() => {
356
- setLoading(true)
357
- setParams({ filter: null, packages: null })
358
- }}
359
- >
360
- {t("reset")}
361
- </Button>
362
-
363
- </div>
364
- )}
365
-
366
- <div className="flex flex-row gap-6 pb-6">
367
- <Sheet open={open} onOpenChange={setOpen}>
368
- <FilterSidebar
369
- tags={tags}
370
- totalItemCount={data.pageInfo.totalItemCount}
371
- updateFilterParam={updateFilterParam}
372
- />
373
- </Sheet>
374
-
375
- <div className="flex-1">
376
- <ResultContainer
377
- items={data.items}
378
- pagination={data.pageInfo}
379
- />
380
- </div>
381
- </div>
382
- </div>
383
- );
1
+ "use client";
2
+
3
+ import React, { FC, useEffect, useMemo, useState } from "react";
4
+ import { Funnel, Settings2, X } from "lucide-react"
5
+ import { useTranslations } from 'next-intl'
6
+ import { parseAsBoolean, parseAsInteger, parseAsString, useQueryStates } from 'nuqs'
7
+ import { informationUnitsResponse } from "@c-rex/interfaces";
8
+ import { Button } from "@c-rex/ui/button";
9
+ import { Badge } from "@c-rex/ui/badge";
10
+ import { ResultContainer } from "@c-rex/components/result-container";
11
+ import { DialogFilter } from "@c-rex/components/dialog-filter";
12
+ import { AutoComplete } from "@c-rex/components/autocomplete";
13
+ import { DEVICE_OPTIONS, OPERATOR_OPTIONS } from "@c-rex/constants";
14
+ import { useSearchContext } from "@c-rex/contexts/search";
15
+ import { FilterSidebar } from "./components/filter-sidebar";
16
+ import { Sheet } from "@c-rex/ui/sheet";
17
+ import { useBreakpoint } from "@c-rex/ui/hooks";
18
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
19
+
20
+ interface HomePageProps {
21
+ data: informationUnitsResponse;
22
+ }
23
+
24
+ type filterModel = {
25
+ key: string
26
+ name?: string
27
+ value: string
28
+ default?: string | boolean | null
29
+ removable: boolean
30
+ }
31
+
32
+ type TagsType = {
33
+ [key: string]: any[]
34
+ }
35
+
36
+ export const HomePage: FC<HomePageProps> = ({ data }) => {
37
+ const t = useTranslations();
38
+ const { setLoading } = useSearchContext();
39
+ const device = useBreakpoint();
40
+ const isMobile = device !== null && device === DEVICE_OPTIONS.MOBILE;
41
+ const [open, setOpen] = useState<boolean>(false)
42
+ const [params, setParams] = useQueryStates({
43
+ language: parseAsString,
44
+ page: parseAsInteger,
45
+ wildcard: parseAsString,
46
+ operator: parseAsString,
47
+ packages: parseAsString,
48
+ filter: parseAsString,
49
+ like: parseAsBoolean,
50
+ search: {
51
+ defaultValue: "",
52
+ parse(value) {
53
+ return value
54
+ },
55
+ },
56
+ }, {
57
+ history: 'push',
58
+ shallow: false,
59
+ });
60
+
61
+ const tags: TagsType = useMemo(() => {
62
+ if (!data) return {};
63
+ const newTags = { ...data.tags }
64
+
65
+ if (params.filter !== null) {
66
+ const splittedParam = params.filter.split(",")
67
+
68
+ splittedParam.forEach((item) => {
69
+ const aux = item.split(".shortId=")
70
+ const name = aux[0] as string
71
+ const shortId = aux[1] as string
72
+
73
+ if (!Object.keys(newTags).includes(name)) {
74
+ newTags[name] = []
75
+ }
76
+
77
+ newTags[name].forEach((el: any) => {
78
+ if (el.shortId == shortId) {
79
+ el.active = true
80
+ } else {
81
+ el.active = false
82
+ }
83
+ })
84
+ })
85
+ } else {
86
+ Object.keys(newTags).forEach((key) => {
87
+ newTags[key].forEach((el: any) => {
88
+ el.active = false
89
+ })
90
+ })
91
+ }
92
+
93
+ if (params.packages !== null && newTags["packages"]) {
94
+ newTags["packages"].forEach((el: any) => {
95
+ if (el.shortId == params.packages) {
96
+ el.active = true
97
+ } else {
98
+ el.active = false
99
+ }
100
+ })
101
+ }
102
+
103
+ return newTags
104
+ }, [data, params]);
105
+
106
+ const filters = useMemo(() => {
107
+ if (!tags || params.search.length === 0) return [];
108
+
109
+ const filters: filterModel[] = [{
110
+ key: "operator",
111
+ name: t("filter.operator"),
112
+ value: `${params?.operator !== OPERATOR_OPTIONS.OR}`,
113
+ removable: false
114
+ }, {
115
+ key: "like",
116
+ name: t("filter.like"),
117
+ value: `${params.like}`,
118
+ removable: false
119
+ }, {
120
+ key: "wildcard",
121
+ name: "Wildcard",
122
+ value: params.wildcard as string,
123
+ removable: false,
124
+ }]
125
+
126
+ const languages = params.language?.split(",")
127
+ languages?.forEach((item) => {
128
+ const aux = languages?.filter(langItem => langItem !== item)
129
+ filters.push({ key: "language", name: t(`languages`), value: item.toUpperCase(), removable: languages.length > 1, default: aux.join(",") })
130
+ })
131
+
132
+ if (params.filter !== null) {
133
+ const splittedParam = params.filter.split(",")
134
+
135
+ splittedParam.forEach((item, index) => {
136
+ const aux = item.split(".shortId=")
137
+ const name = aux[0] as string
138
+ const shortId = aux[1] as string
139
+
140
+ const defaultValue = [...splittedParam]
141
+ defaultValue.splice(index, 1)
142
+
143
+ if (!Object.keys(tags).includes(name) || !tags[name]) return;
144
+
145
+ const tag = tags[name].find(el => el.shortId === shortId)
146
+ if (!tag) return;
147
+
148
+ const value = defaultValue.length == 0 ? null : defaultValue.join(",")
149
+
150
+ filters.push({
151
+ key: "filter",
152
+ name: t(`filter.tags.${name}`),
153
+ value: tag.label,
154
+ removable: true,
155
+ default: value
156
+ })
157
+ })
158
+ }
159
+
160
+ if (params.packages !== null) {
161
+ let packageTag = {
162
+ label: params.packages,
163
+ }
164
+
165
+ if (tags["packages"]) {
166
+ packageTag = tags["packages"]?.find(el => el.shortId === params.packages)
167
+ }
168
+
169
+ filters.push({
170
+ key: "packages",
171
+ name: t("filter.tags.packages"),
172
+ value: packageTag.label,
173
+ removable: true,
174
+ default: null
175
+ })
176
+ }
177
+
178
+ Object.keys(params)
179
+ .filter(item => !["page", "search", "language", "operator", "like", "wildcard", "filter", "packages"].includes(item))
180
+ .forEach(item => {
181
+ if (params[item as keyof typeof params] === null || params[item as keyof typeof params] === undefined) return;
182
+ const value = params[item as keyof typeof params] as string
183
+ filters.push({ key: item, value: value, removable: true, default: null })
184
+ })
185
+
186
+ return filters
187
+ }, [tags, params]);
188
+
189
+ useEffect(() => setLoading(false), [data])
190
+
191
+ const updateFilterParam = (key: string, item: any) => {
192
+ setLoading(true)
193
+
194
+ if (item.active) {
195
+ removeFilterItem(key, item)
196
+ setOpen(false);
197
+ return;
198
+ }
199
+
200
+ if (key === "packages") {
201
+ setParams({ packages: item.shortId })
202
+ return;
203
+ }
204
+
205
+ const value = `${key}.shortId=${item.shortId}`
206
+ let aux = value
207
+
208
+ if (params.filter != null) {
209
+ const splittedParam = params.filter.split(",")
210
+ const finalValue = [...splittedParam]
211
+
212
+ const hasParams = params.filter.includes(key)
213
+
214
+ if (hasParams) {
215
+ let mainIndex = -1
216
+
217
+ splittedParam.forEach((el, index) => {
218
+ if (el.includes(key)) {
219
+ mainIndex = index
220
+ }
221
+ })
222
+ finalValue[mainIndex] = value
223
+ } else {
224
+ finalValue.push(value)
225
+ }
226
+
227
+ aux = finalValue.join(",")
228
+ }
229
+
230
+ setParams({ filter: aux })
231
+ setOpen(false);
232
+ };
233
+
234
+ const removeFilterItem = (key: string, item: any) => {
235
+ if (key === "packages") {
236
+ setParams({ packages: null })
237
+ return;
238
+ }
239
+
240
+ if (params.filter !== null) {
241
+ const value = `${key}.shortId=${item.shortId}`
242
+ const newValue = params.filter.split(",").filter(item => item !== value).join(",")
243
+ setParams({ filter: newValue.length === 0 ? null : newValue })
244
+ }
245
+ }
246
+
247
+ return (
248
+ <div className="container">
249
+ <div className="flex gap-4 py-6">
250
+ <div className="flex-1">
251
+ <AutoComplete
252
+ embedded={false}
253
+ initialValue={params.search}
254
+ searchByPackage={false}
255
+ />
256
+ </div>
257
+ <DialogFilter
258
+ trigger={(
259
+ <Button variant="default">
260
+ <span className="hidden sm:inline">
261
+ {t("searchSettings")}
262
+ </span>
263
+ <Settings2 className="h-4 w-4" />
264
+ </Button>
265
+ )}
266
+ />
267
+ </div>
268
+
269
+ {filters != null && filters.length > 0 && (
270
+ <div className="pb-4 flex justify-between">
271
+
272
+ <div className="flex flex-wrap gap-2">
273
+ <Button
274
+ size="sm"
275
+ variant="secondary"
276
+ onClick={() => setOpen(true)}
277
+ className="md:hidden"
278
+ >
279
+ <Funnel className="h-2" />
280
+ </Button>
281
+
282
+ {filters.length > 0 && (
283
+ <>
284
+ {filters.slice(0, 1).map((item) => (
285
+ <Badge
286
+ key={`${item.key}-${item?.value}`}
287
+ variant="outline"
288
+ className="h-8"
289
+ >
290
+ {item?.name ? item.name : item.key}: {item.value}
291
+
292
+ {item.removable && (
293
+ <Button size="xs" variant="ghost" onClick={() => {
294
+ setLoading(true)
295
+ setParams({ [item.key]: item?.default })
296
+ }}>
297
+ <X className="h-2" />
298
+ </Button>
299
+ )}
300
+ </Badge>
301
+ ))}
302
+
303
+ {isMobile ? (
304
+ <TooltipProvider>
305
+ <Tooltip delayDuration={100}>
306
+ <TooltipTrigger>
307
+ <Badge
308
+ key={`grouped-filters`}
309
+ variant="outline"
310
+ className="h-8"
311
+ >
312
+ +{filters.length - 1} {t("filter.filters")}
313
+ </Badge>
314
+
315
+ </TooltipTrigger>
316
+
317
+ <TooltipContent>
318
+ {filters.slice(1).map((item) => {
319
+ const label = item?.name ? item.name : item.key
320
+ const returnString = `${label}: ${item.value}`;
321
+ return <div className="capitalize" key={returnString}>{returnString}</div>;
322
+ })}
323
+ </TooltipContent>
324
+ </Tooltip>
325
+ </TooltipProvider>
326
+ ) : (
327
+ <>
328
+ {filters.slice(1).map((item) => (
329
+ <Badge
330
+ key={`${item.key}-${item?.value}`}
331
+ variant="outline"
332
+ className="h-8"
333
+ >
334
+ {item?.name ? item.name : item.key}: {item.value}
335
+
336
+ {item.removable && (
337
+ <Button size="xs" variant="ghost" onClick={() => {
338
+ setLoading(true)
339
+ setParams({ [item.key]: item?.default })
340
+ }}>
341
+ <X className="h-2" />
342
+ </Button>
343
+ )}
344
+ </Badge>
345
+ ))}
346
+ </>
347
+ )}
348
+ </>
349
+ )}
350
+ </div>
351
+
352
+ <Button
353
+ size="sm"
354
+ variant="secondary"
355
+ onClick={() => {
356
+ setLoading(true)
357
+ setParams({ filter: null, packages: null })
358
+ }}
359
+ >
360
+ {t("reset")}
361
+ </Button>
362
+
363
+ </div>
364
+ )}
365
+
366
+ <div className="flex flex-row gap-6 pb-6">
367
+ <Sheet open={open} onOpenChange={setOpen}>
368
+ <FilterSidebar
369
+ tags={tags}
370
+ totalItemCount={data.pageInfo.totalItemCount}
371
+ updateFilterParam={updateFilterParam}
372
+ />
373
+ </Sheet>
374
+
375
+ <div className="flex-1">
376
+ <ResultContainer
377
+ items={data.items}
378
+ pagination={data.pageInfo}
379
+ />
380
+ </div>
381
+ </div>
382
+ </div>
383
+ );
384
384
  };