@@ -10,16 +10,18 @@ import { BookText, ChevronDown, GitFork, ImageOff, LinkIcon, Microscope } from "
10
10
import { Badge } from "@/components/ui/badge"
11
11
import { Button } from "@/components/ui/button"
12
12
import { DropdownMenu , DropdownMenuContent , DropdownMenuItem , DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
13
- import { SearchFieldConfiguration , SearchResult } from "@elastic/search-ui"
13
+ import { SearchFieldConfiguration } from "@elastic/search-ui"
14
14
import { GenericResultViewTag , GenericResultViewTagProps } from "@/components/result/GenericResultViewTag"
15
+ import { GenericResultViewImageCarousel } from "@/components/result/GenericResultViewImageCarousel"
16
+ import { z } from "zod"
15
17
16
18
const HTTP_REGEX = / h t t p s ? : \/ \/ [ a - z ] + \. [ a - z ] + .* / gm
17
19
18
20
export interface GenericResultViewProps {
19
21
/**
20
22
* Search result that will be rendered in this view. Will be provided by FairDOElasticSearch
21
23
*/
22
- result : SearchResult
24
+ result : Record < string , unknown >
23
25
24
26
/**
25
27
* The elastic field where the title of the card will be read from
@@ -114,18 +116,53 @@ export function GenericResultView({
114
116
115
117
const getField = useCallback (
116
118
( field : string ) => {
117
- return autoUnwrap ( result [ field ] )
119
+ try {
120
+ return autoUnwrap (
121
+ z
122
+ . string ( )
123
+ . or ( z . object ( { raw : z . string ( ) } ) )
124
+ . optional ( )
125
+ . parse ( result [ field ] )
126
+ )
127
+ } catch ( e ) {
128
+ console . error ( `Parsing field ${ field } failed` , e )
129
+ return undefined
130
+ }
118
131
} ,
119
132
[ result ]
120
133
)
121
134
122
135
const getArrayField = useCallback (
123
136
( field : string ) => {
124
- return autoUnwrapArray ( result [ field ] )
137
+ try {
138
+ return autoUnwrapArray (
139
+ z
140
+ . string ( )
141
+ . array ( )
142
+ . or ( z . object ( { raw : z . string ( ) . array ( ) } ) )
143
+ . optional ( )
144
+ . parse ( result [ field ] )
145
+ )
146
+ } catch ( e ) {
147
+ console . error ( `Parsing array field ${ field } failed` , e )
148
+ return [ ]
149
+ }
125
150
} ,
126
151
[ result ]
127
152
)
128
153
154
+ const getArrayOrSingleField = useCallback (
155
+ ( field : string ) => {
156
+ const _field : unknown = result [ field ]
157
+ if ( Array . isArray ( _field ) || ( typeof _field === "object" && _field && "raw" in _field && Array . isArray ( _field . raw ) ) ) {
158
+ return getArrayField ( field )
159
+ } else {
160
+ return getField ( field )
161
+ }
162
+ } ,
163
+ [ getArrayField , getField , result ]
164
+ )
165
+
129
166
const pid = useMemo ( ( ) => {
130
167
const _pid = getField ( pidField ?? "pid" )
131
168
if ( _pid && _pid . startsWith ( "https://" ) ) {
@@ -151,8 +188,9 @@ export function GenericResultView({
151
188
} , [ digitalObjectLocationField , getField ] )
152
189
153
190
const previewImage = useMemo ( ( ) => {
154
- return getField ( imageField ?? "imageURL" )
155
- } , [ getField , imageField ] )
191
+ const images = getArrayOrSingleField ( imageField ?? "imageURL" )
192
+ return Array . isArray ( images ) ? ( images . length === 1 ? images [ 0 ] : images ) : images
193
+ } , [ getArrayOrSingleField , imageField ] )
156
194
157
195
const identifier = useMemo ( ( ) => {
158
196
return getField ( additionalIdentifierField ?? "identifier" )
@@ -164,6 +202,7 @@ export function GenericResultView({
164
202
165
203
const creationDate = useMemo ( ( ) => {
166
204
const value = getField ( creationDateField ?? "dateCreated" )
205
+ if ( ! value ) return undefined
167
206
const dateTime = DateTime . fromISO ( value )
168
207
return dateTime . isValid ? dateTime . toLocaleString ( ) : value
169
208
} , [ creationDateField , getField ] )
@@ -185,9 +224,11 @@ export function GenericResultView({
185
224
186
225
if ( search ) {
187
226
for ( const entry of search . results ) {
188
- addToResultCache ( autoUnwrap ( entry [ pidField ?? "pid" ] ) , {
189
- pid : autoUnwrap ( entry [ pidField ?? "pid" ] ) ,
190
- name : autoUnwrap ( entry [ titleField ?? "name" ] )
227
+ const pid = autoUnwrap ( entry [ pidField ?? "pid" ] )
228
+ if ( ! pid ) continue
229
+ addToResultCache ( pid , {
230
+ pid,
231
+ name : autoUnwrap ( entry [ titleField ?? "name" ] ) ?? ""
191
232
} )
192
233
}
193
234
}
@@ -198,8 +239,8 @@ export function GenericResultView({
198
239
199
240
openRelationGraph (
200
241
{
201
- id : pid ,
202
- label : title ,
242
+ id : pid ?? "source" ,
243
+ label : title ?? "Source" ,
203
244
tag : "Current" ,
204
245
remoteURL : doLocation ,
205
246
searchQuery : pid
@@ -212,6 +253,7 @@ export function GenericResultView({
212
253
} , [ doLocation , fetchRelatedItems , getResultFromCache , isMetadataFor , openRelationGraph , pid , title ] )
213
254
214
255
const goToMetadata = useCallback ( ( ) => {
256
+ if ( ! hasMetadata ) return
215
257
searchFor ( hasMetadata )
216
258
} , [ hasMetadata , searchFor ] )
217
259
@@ -229,7 +271,9 @@ export function GenericResultView({
229
271
} , [ addToResultCache , pid , title ] )
230
272
231
273
return (
232
- < div className = { `rfs-m-2 rfs-rounded-lg rfs-border rfs-border-border rfs-p-4 ${ exactPidMatch ? "rfs-animate-outline-ping" : "" } ` } >
274
+ < div
275
+ className = { `rfs-m-2 rfs-rounded-lg rfs-border rfs-border-border rfs-p-4 rfs-group/resultView ${ exactPidMatch ? "rfs-animate-outline-ping" : "" } ` }
276
+ >
233
277
< div
234
278
className = { `rfs-grid ${ imageField ? "rfs-grid-rows-[100px_1fr] md:rfs-grid-cols-[200px_1fr] md:rfs-grid-rows-1" : "" } rfs-gap-4 rfs-overflow-x-auto md:rfs-max-w-full` }
235
279
>
@@ -238,7 +282,11 @@ export function GenericResultView({
238
282
className = { `rfs-flex rfs-justify-center rfs-rounded md:rfs-items-center rfs-p-2 d ${ invertImageInDarkMode ? "dark:rfs-invert" : "" } ` }
239
283
>
240
284
{ previewImage ? (
241
- < img className = "md:rfs-size-[200px]" src = { previewImage } alt = { `Preview for ${ title } ` } />
285
+ Array . isArray ( previewImage ) ? (
286
+ < GenericResultViewImageCarousel images = { previewImage } title = { title } />
287
+ ) : (
288
+ < img className = "md:rfs-size-[200px]" src = { previewImage } alt = { `Preview for ${ title } ` } />
289
+ )
242
290
) : (
243
291
< div className = "rfs-flex rfs-flex-col rfs-justify-center dark:rfs-text-background" >
244
292
< ImageOff className = "rfs-size-6 rfs-text-muted-foreground/50" />
0 commit comments