@c-rex/components 0.3.0-build.28 → 0.3.0-build.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.
@@ -0,0 +1,258 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useTranslations } from "next-intl";
5
+ import { Settings2 } from "lucide-react";
6
+ import { Button } from "@c-rex/ui/button";
7
+ import type { AutoCompleteProps } from "../../autocomplete";
8
+ import { Empty } from "../empty";
9
+ import { FilterNavbar } from "../filter-navbar";
10
+ import { DialogFilter } from "../dialog-filter";
11
+ import { FilterSidebar } from "../filter-sidebar";
12
+ import type { FilterSidebarProps } from "../filter-sidebar";
13
+ import { GenericTableResultList } from "./table-result-list";
14
+ import { Pagination } from "../pagination";
15
+ import { Skeleton } from "@c-rex/ui/skeleton";
16
+ import type { CommonItemsModel, ResultContainerModel, Tags } from "@c-rex/interfaces";
17
+ import type { FacetLabelOverrides } from "../filter-sidebar/utils";
18
+
19
+ type SerializedError = { message: string; name: string } | null;
20
+
21
+ type GetAllClientProps<TData, TQueryParams> = {
22
+ queryParams: TQueryParams;
23
+ children: (result: {
24
+ data: TData | null;
25
+ error: SerializedError;
26
+ isLoading: boolean;
27
+ }) => React.ReactNode;
28
+ };
29
+
30
+ type SuggestionsProps = Pick<AutoCompleteProps, "embedded" | "initialValue" | "onSelectPath" | "keepParams">;
31
+
32
+ export type GenericSearchResultsClientProps<
33
+ TItem extends CommonItemsModel,
34
+ TQueryParams
35
+ > = {
36
+ GetAllClient: React.ComponentType<GetAllClientProps<ResultContainerModel<TItem>, TQueryParams>>;
37
+ SuggestionsComponent: React.ComponentType<SuggestionsProps>;
38
+ queryParams: TQueryParams;
39
+ search: string;
40
+ suggestionsPath: string;
41
+ linkPattern: string;
42
+ addPackageId: boolean;
43
+ showFilesColumn?: boolean;
44
+ facetLabelOverrides?: FacetLabelOverrides;
45
+ informationSubjectsTaxonomy?: NonNullable<FilterSidebarProps["facetTaxonomies"]>["informationSubjects"];
46
+ facetExcludeProperties?: string[];
47
+ };
48
+
49
+ const hasFacetTags = (tags?: Tags): tags is Tags => Boolean(tags && Object.keys(tags).length > 0);
50
+
51
+ const ResultsSkeleton = ({ showFilesColumn = false }: { showFilesColumn?: boolean }) => (
52
+ <div className="rounded-md border mb-6">
53
+ <div className="font-bold text-sm p-2 border-b items-center flex-wrap hidden md:flex">
54
+ <div className="flex-1 p-2"><Skeleton className="h-4 w-20" /></div>
55
+ <div className="w-1/2 md:w-1/5 p-2 flex justify-center"><Skeleton className="h-4 w-20" /></div>
56
+ <div className="w-1/2 md:w-1/5 p-2 flex justify-center"><Skeleton className="h-4 w-16" /></div>
57
+ {showFilesColumn && (
58
+ <div className="w-1/2 md:w-1/5 p-2 flex justify-center"><Skeleton className="h-4 w-16" /></div>
59
+ )}
60
+ </div>
61
+ {Array.from({ length: 6 }).map((_, index) => (
62
+ <div key={index} className="min-h-12 flex flex-wrap items-center border-b last:border-b-0">
63
+ <div className="flex-1 p-2">
64
+ <Skeleton className="h-5 w-72 max-w-full" />
65
+ </div>
66
+ <div className="w-1/5 md:w-1/5 flex justify-center p-2">
67
+ <Skeleton className="h-5 w-8" />
68
+ </div>
69
+ <div className="w-4/5 md:w-1/5 p-2 flex justify-center">
70
+ <Skeleton className="h-6 w-24" />
71
+ </div>
72
+ {showFilesColumn && (
73
+ <div className="w-1/5 p-2 flex justify-center">
74
+ <Skeleton className="h-6 w-10" />
75
+ </div>
76
+ )}
77
+ </div>
78
+ ))}
79
+ </div>
80
+ );
81
+
82
+ type SearchResultsBodyProps<TItem extends CommonItemsModel> = {
83
+ data: ResultContainerModel<TItem> | null;
84
+ error: SerializedError;
85
+ isLoading: boolean;
86
+ facetLabelOverrides?: FacetLabelOverrides;
87
+ informationSubjectsTaxonomy?: NonNullable<FilterSidebarProps["facetTaxonomies"]>["informationSubjects"];
88
+ facetExcludeProperties: string[];
89
+ search: string;
90
+ linkPattern: string;
91
+ addPackageId: boolean;
92
+ showFilesColumn: boolean;
93
+ };
94
+
95
+ const SearchResultsBody = <TItem extends CommonItemsModel>({
96
+ data,
97
+ error,
98
+ isLoading,
99
+ facetLabelOverrides,
100
+ informationSubjectsTaxonomy,
101
+ facetExcludeProperties,
102
+ search,
103
+ linkPattern,
104
+ addPackageId,
105
+ showFilesColumn,
106
+ }: SearchResultsBodyProps<TItem>) => {
107
+ const [lastFacetTags, setLastFacetTags] = useState<Tags | undefined>(undefined);
108
+
109
+ useEffect(() => {
110
+ if (hasFacetTags(data?.tags)) {
111
+ setLastFacetTags(data.tags);
112
+ }
113
+ }, [data?.tags]);
114
+
115
+ const effectiveTags = hasFacetTags(data?.tags) ? data.tags : lastFacetTags;
116
+
117
+ if (error) return <div className="text-red-600">{JSON.stringify(error)}</div>;
118
+
119
+ if (isLoading && !data) {
120
+ return (
121
+ <div className="flex flex-col gap-4 pb-4">
122
+ <ResultsSkeleton showFilesColumn={showFilesColumn} />
123
+ </div>
124
+ );
125
+ }
126
+
127
+ if (!data) {
128
+ return (
129
+ <div className="flex flex-col gap-4 pb-4">
130
+ <FilterNavbar
131
+ tags={effectiveTags}
132
+ facetLabelOverrides={facetLabelOverrides}
133
+ excludeProperties={facetExcludeProperties}
134
+ />
135
+ <Empty />
136
+ </div>
137
+ );
138
+ }
139
+
140
+ if (data.items.length === 0) {
141
+ return (
142
+ <div className="flex flex-col gap-4 pb-4">
143
+ <FilterNavbar
144
+ tags={effectiveTags}
145
+ facetLabelOverrides={facetLabelOverrides}
146
+ excludeProperties={facetExcludeProperties}
147
+ />
148
+
149
+ <div className="flex flex-row gap-4">
150
+ <FilterSidebar
151
+ tags={effectiveTags}
152
+ totalItemCount={data.pageInfo.totalItemCount}
153
+ facetLabelOverrides={facetLabelOverrides}
154
+ facetTaxonomies={informationSubjectsTaxonomy ? { informationSubjects: informationSubjectsTaxonomy } : undefined}
155
+ excludeProperties={facetExcludeProperties}
156
+ />
157
+
158
+ <div className="flex-1 flex flex-col gap-4">
159
+ <Pagination pageInfo={data.pageInfo} className="pt-0" />
160
+ <Empty />
161
+ <Pagination pageInfo={data.pageInfo} />
162
+ </div>
163
+ </div>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ return (
169
+ <div className="flex flex-col gap-4 pb-4">
170
+ <FilterNavbar
171
+ tags={effectiveTags}
172
+ facetLabelOverrides={facetLabelOverrides}
173
+ excludeProperties={facetExcludeProperties}
174
+ />
175
+
176
+ <div className="flex flex-row gap-4">
177
+ <FilterSidebar
178
+ tags={effectiveTags}
179
+ totalItemCount={data.pageInfo.totalItemCount}
180
+ facetLabelOverrides={facetLabelOverrides}
181
+ facetTaxonomies={informationSubjectsTaxonomy ? { informationSubjects: informationSubjectsTaxonomy } : undefined}
182
+ excludeProperties={facetExcludeProperties}
183
+ />
184
+
185
+ <div className="flex-1 flex flex-col gap-4">
186
+ <Pagination pageInfo={data.pageInfo} className="pt-0" />
187
+ <GenericTableResultList
188
+ items={data.items}
189
+ query={search}
190
+ addPackageId={addPackageId}
191
+ linkPattern={linkPattern}
192
+ showFilesColumn={showFilesColumn}
193
+ />
194
+ <Pagination pageInfo={data.pageInfo} />
195
+ </div>
196
+ </div>
197
+ </div>
198
+ );
199
+ };
200
+
201
+ export const GenericSearchResultsClient = <
202
+ TItem extends CommonItemsModel,
203
+ TQueryParams
204
+ >({
205
+ GetAllClient,
206
+ SuggestionsComponent,
207
+ queryParams,
208
+ search,
209
+ suggestionsPath,
210
+ linkPattern,
211
+ addPackageId,
212
+ showFilesColumn = false,
213
+ facetLabelOverrides,
214
+ informationSubjectsTaxonomy,
215
+ facetExcludeProperties = [],
216
+ }: GenericSearchResultsClientProps<TItem, TQueryParams>) => {
217
+ const t = useTranslations();
218
+
219
+ return (
220
+ <div className="container">
221
+ <div className="flex gap-6 py-6">
222
+ <div className="flex-1">
223
+ <SuggestionsComponent
224
+ embedded={false}
225
+ initialValue={search}
226
+ onSelectPath={suggestionsPath}
227
+ keepParams
228
+ />
229
+ </div>
230
+ <DialogFilter
231
+ trigger={(
232
+ <Button variant="default">
233
+ <span className="hidden sm:inline">{t("searchSettings")}</span>
234
+ <Settings2 />
235
+ </Button>
236
+ )}
237
+ />
238
+ </div>
239
+
240
+ <GetAllClient queryParams={queryParams}>
241
+ {({ data, error, isLoading }) => (
242
+ <SearchResultsBody
243
+ data={data}
244
+ error={error}
245
+ isLoading={isLoading}
246
+ facetLabelOverrides={facetLabelOverrides}
247
+ informationSubjectsTaxonomy={informationSubjectsTaxonomy}
248
+ facetExcludeProperties={facetExcludeProperties}
249
+ search={search}
250
+ linkPattern={linkPattern}
251
+ addPackageId={addPackageId}
252
+ showFilesColumn={showFilesColumn}
253
+ />
254
+ )}
255
+ </GetAllClient>
256
+ </div>
257
+ );
258
+ };
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { FC } from "react";
2
4
  import { CommonItemsModel } from "@c-rex/interfaces";
3
5
  import { cn, generateQueryParams } from "@c-rex/utils";
@@ -5,7 +7,7 @@ import { Flag } from "@c-rex/components/flag";
5
7
  import { Badge } from "@c-rex/ui/badge";
6
8
  import Link from "next/link";
7
9
  import { FileDownloadDropdown } from "@c-rex/components/file-download";
8
- import { getTranslations } from "next-intl/server";
10
+ import { useTranslations } from "next-intl";
9
11
  import { QueryParams } from "@c-rex/types";
10
12
  import { getResultItemSummary } from "../summary";
11
13
 
@@ -17,14 +19,14 @@ interface Props {
17
19
  query?: string
18
20
  }
19
21
 
20
- export const GenericTableResultList: FC<Props> = async ({
22
+ export const GenericTableResultList: FC<Props> = ({
21
23
  items,
22
24
  linkPattern,
23
25
  addPackageId,
24
26
  query,
25
27
  showFilesColumn = true,
26
28
  }) => {
27
- const t = await getTranslations("results");
29
+ const t = useTranslations("results");
28
30
 
29
31
  return (
30
32
  <div className="rounded-md border mb-6 last:border-b-0">