Skip to content

Commit

Permalink
CDP #33 - Adding model side panels; Adding handling for user-defined …
Browse files Browse the repository at this point in the history
…fields
  • Loading branch information
dleadbetter committed Jan 28, 2025
1 parent 1beeabb commit 4d72271
Show file tree
Hide file tree
Showing 21 changed files with 421 additions and 91 deletions.
50 changes: 0 additions & 50 deletions src/apps/search/Organization.tsx

This file was deleted.

31 changes: 28 additions & 3 deletions src/apps/search/SearchRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import Place from '@apps/search/Place';
import Organization from '@apps/search/Organization';
import Event from '@apps/search/panels/Event';
import Instance from '@apps/search/panels/Instance';
import Item from '@apps/search/panels/Item';
import Person from '@apps/search/panels/Person';
import Place from '@apps/search/panels/Place';
import Organization from '@apps/search/panels/Organization';
import Work from '@apps/search/panels/Work';
import SearchPanel from '@apps/search/SearchPanel';
import SearchTimeline from '@apps/search/SearchTimeline';
import SearchTimeline from '@apps/search/timeline/SearchTimeline';
import { CoreDataContextProvider } from '@performant-software/core-data';
import { Route, Routes, useRuntimeConfig } from '@peripleo/peripleo';

Expand All @@ -14,14 +19,34 @@ const SearchRoutes = () => {
projectIds={config.core_data.project_ids}
>
<Routes>
<Route
match='/events/'
element={<Event />}
/>
<Route
match='/instances/'
element={<Instance />}
/>
<Route
match='/item/'
element={<Item />}
/>
<Route
match='/organizations/'
element={<Organization />}
/>
<Route
match='/people/'
element={<Person />}
/>
<Route
match='/places/'
element={<Place />}
/>
<Route
match='/work/'
element={<Work />}
/>
<Route
element={(
<>
Expand Down
77 changes: 77 additions & 0 deletions src/apps/search/panels/BasePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import RecordPanel from '@apps/search/panels/RecordPanel';
import { BaseService, CoreData as CoreDataUtils, useLoader } from '@performant-software/core-data';
import { useCurrentRoute, useNavigate } from '@peripleo/peripleo';
import { getPreviewImage } from '@utils/Media';
import { getCurrentId } from '@utils/router';
import React, { useCallback, useMemo } from 'react';
import _ from 'underscore';

interface Props {
name: string;
renderItem?: (item: any) => JSX.Element;
renderName?: (item: any) => string;
useServiceHook: () => typeof BaseService;
}

const BasePanel = (props: Props) => {
const Service = props.useServiceHook();
const navigate = useNavigate();

const route = useCurrentRoute();
const id = getCurrentId(route);

/**
* Loads the instance record from the Core Data API.
*/
const onLoad = useCallback(() => Service.fetchOne(id), [id]);
const { data } = useLoader(onLoad);

const item = useMemo(() => data && data[props.name], [data, props.name]);

/**
* Loads the first related media record from the Core Data API.
*/
const onLoadMedia = useCallback(() => Service.fetchRelatedMedia(id, { limit: 1 }), [id]);
const { data: { media_contents: mediaContents = [] } = {} } = useLoader(onLoadMedia);

/**
* Loads the related place records from the Core Data API.
*/
const onLoadPlaces = useCallback(() => Service.fetchRelatedPlaces(id), [id]);
const { data: { places = [] } = {} } = useLoader(onLoadPlaces);

/**
* Memo-izes the geometry.
*/
const geometry = useMemo(() => !_.isEmpty(places) && CoreDataUtils.toFeatureCollection(places), [places]);

/**
* Memo-izes the image.
*/
const image = useMemo(() => getPreviewImage(mediaContents), [mediaContents]);

/**
* Memo-izes the name.
*/
const name = useMemo(() => (item && props.renderName && props.renderName(item)) || item?.name, [item]);

return (
<RecordPanel
geometry={geometry}
id={id}
image={image}
name={name}
onClose={() => navigate('/')}
onLoadMedia={() => Service.fetchRelatedManifests(id)}
onLoadOrganizations={() => Service.fetchRelatedOrganizations(id)}
onLoadPeople={() => Service.fetchRelatedPeople(id)}
onLoadPlaces={() => Service.fetchRelatedPlaces(id)}
onLoadTaxonomies={() => Service.fetchRelatedTaxonomies(id)}
userDefined={item?.user_defined}
>
{ item && props.renderItem && props.renderItem(item) }
</RecordPanel>
)
};

export default BasePanel;
54 changes: 54 additions & 0 deletions src/apps/search/panels/Event.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import BasePanel from '@apps/search/panels/BasePanel';
import { useTranslations } from '@i18n/client';
import { useEventsService } from '@performant-software/core-data';
import { FuzzyDate as FuzzyDateUtils } from '@performant-software/shared-components';
import React, { useCallback } from 'react';

const Event = () => {
const { t } = useTranslations();

/**
* Returns the start date label for the passed event.
*/
const getStartDateLabel = useCallback((event) => event.end_date ? t('startDate') : t('date'), [t]);

/**
* Renders the label and value for the passed date.
*/
const renderDate = useCallback((date, label) => (
<div>
<div
className='text-muted'
>
{ label }
</div>
<div
className='font-medium overflow-hidden text-ellipsis'
>
{ FuzzyDateUtils.getDateView(date) }
</div>
</div>
), []);

return (
<BasePanel
name='event'
renderItem={(event) => (
<>
{ event.start_date && renderDate(event.start_date, getStartDateLabel(event)) }
{ event.end_date && renderDate(event.end_date, t('endDate')) }
{ event.description && (
<p
className='py-2'
>
{ event.description }
</p>
)}
</>
)}
useServiceHook={useEventsService}
/>
);
};

export default Event;
12 changes: 12 additions & 0 deletions src/apps/search/panels/Instance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import BasePanel from '@apps/search/panels/BasePanel';
import { useInstancesService } from '@performant-software/core-data';
import React from 'react';

const Instance = () => (
<BasePanel
name='instance'
useServiceHook={useInstancesService}
/>
);

export default Instance;
12 changes: 12 additions & 0 deletions src/apps/search/panels/Item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import BasePanel from '@apps/search/panels/BasePanel';
import { useItemsService } from '@performant-software/core-data';
import React from 'react';

const Item = () => (
<BasePanel
name='item'
useServiceHook={useItemsService}
/>
);

export default Item;
12 changes: 12 additions & 0 deletions src/apps/search/panels/Organization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import BasePanel from '@apps/search/panels/BasePanel';
import { useOrganizationsService } from '@performant-software/core-data';
import React from 'react';

const Organization = () => (
<BasePanel
name='organization'
useServiceHook={useOrganizationsService}
/>
);

export default Organization;
23 changes: 23 additions & 0 deletions src/apps/search/panels/Person.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import BasePanel from '@apps/search/panels/BasePanel';
import { usePeopleService } from '@performant-software/core-data';
import { getNameView } from '@utils/Person';
import React, { useCallback } from 'react';

const Person = () => {
/**
* Returns a concatenation of the person's name attributes.
*
* @type {function(*): *}
*/
const renderName = useCallback((person) => getNameView(person), []);

return (
<BasePanel
name='person'
renderName={renderName}
useServiceHook={usePeopleService}
/>
);
};

export default Person;
File renamed without changes.
34 changes: 23 additions & 11 deletions src/apps/search/Place.tsx → src/apps/search/panels/Place.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import RelatedRecords from '@apps/search/RelatedRecords';
import RecordPanel from '@apps/search/RecordPanel';
import RecordPanel from '@apps/search/panels/RecordPanel';
import { useTranslations } from '@i18n/client';
import {
CoreData as CoreDataUtils,
Expand All @@ -8,6 +7,7 @@ import {
usePlacesService
} from '@performant-software/core-data';
import { useCurrentRoute, useNavigate } from '@peripleo/peripleo';
import { getPreviewImage } from '@utils/Media';
import { getCurrentId } from '@utils/router';
import React, { useCallback, useMemo } from 'react';
import _ from 'underscore';
Expand All @@ -31,25 +31,37 @@ const Place = () => {
const onLoad = useCallback(() => PlacesService.fetchOne(id), [id]);
const { data: { place = null } = {} } = useLoader(onLoad, null, [id]);

/**
* Loads the first related media record from the Core Data API.
*/
const onLoadMedia = useCallback(() => PlacesService.fetchRelatedMedia(id, { limit: 1 }), [id]);
const { data: { media_contents: mediaContents = [] } = {} } = useLoader(onLoadMedia);

/**
* Memo-izes the geometry.
*/
const geometry = useMemo(() => place && CoreDataUtils.toFeature(place), [place]);

/**
* Memo-izes the image.
*/
const image = useMemo(() => getPreviewImage(mediaContents), [mediaContents]);

return (
<RecordPanel
className='place'
geometry={geometry}
item={place}
id={id}
image={image}
name={place?.name}
onClose={() => navigate('/')}
onLoadMedia={() => PlacesService.fetchRelatedManifests(id)}
onLoadOrganizations={() => PlacesService.fetchRelatedOrganizations(id)}
onLoadPeople={() => PlacesService.fetchRelatedPeople(id)}
onLoadPlaces={() => PlacesService.fetchRelatedPlaces(id)}
onLoadTaxonomies={() => PlacesService.fetchRelatedTaxonomies(id)}
userDefined={place?.user_defined}
>
<RelatedRecords
key={`related-${id}`}
onLoadMedia={() => PlacesService.fetchRelatedManifests(id)}
onLoadOrganizations={() => PlacesService.fetchRelatedOrganizations(id)}
onLoadPeople={() => PlacesService.fetchRelatedPeople(id)}
onLoadPlaces={() => PlacesService.fetchRelatedPlaces(id)}
onLoadTaxonomies={() => PlacesService.fetchRelatedTaxonomies(id)}
/>
{!_.isEmpty(place?.place_layers) && (
<PlaceLayersSelector
className='place-layers-selector'
Expand Down
Loading

0 comments on commit 4d72271

Please sign in to comment.