diff --git a/src/App.tsx b/src/App.tsx index 16e0ca33..8830028a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,7 @@ import { ROUTES } from '@common/constants/routes.constants'; import { OKAPI_CONFIG } from '@common/constants/api.constants'; import { BASE_LOCALE, i18nMessages } from '@common/i18n/messages'; import { localStorageService } from '@common/services/storage'; -import { Root, Search, Load, EditWrapper, ExternalResourceEdit } from '@views'; +import { Root, Search, Load, EditWrapper, ExternalResourcePreview } from '@views'; import state from '@state'; import { ServicesProvider } from './providers'; import './App.scss'; @@ -43,8 +43,8 @@ export const routes: RouteObject[] = [ element: , }, { - path: ROUTES.EXTERNAL_RESOURCE_EDIT.uri, - element: , + path: ROUTES.EXTERNAL_RESOURCE_PREVIEW.uri, + element: , }, { path: '*', diff --git a/src/common/api/records.api.ts b/src/common/api/records.api.ts index 78d2bb73..7ff63769 100644 --- a/src/common/api/records.api.ts +++ b/src/common/api/records.api.ts @@ -1,4 +1,9 @@ -import { BIBFRAME_API_ENDPOINT, MAX_LIMIT } from '@common/constants/api.constants'; +import { + BIBFRAME_API_ENDPOINT, + ExternalResourceIdType, + GET_RESOURCE_BY_TYPE_URIS, + MAX_LIMIT, +} from '@common/constants/api.constants'; import baseApi from './base.api'; import { TYPE_URIS } from '@common/constants/bibframe.constants'; @@ -6,6 +11,10 @@ type SingleRecord = { recordId: string; }; +type IGetRecord = SingleRecord & { + idType?: ExternalResourceIdType; +}; + type GetAllRecords = { pageSize?: number; pageNumber?: number; @@ -14,8 +23,9 @@ type GetAllRecords = { const singleRecordUrl = `${BIBFRAME_API_ENDPOINT}/:recordId`; -export const getRecord = async ({ recordId }: SingleRecord) => { - const url = baseApi.generateUrl(singleRecordUrl, { name: ':recordId', value: recordId }); +export const getRecord = async ({ recordId, idType }: IGetRecord) => { + const selectedUrl = (idType && GET_RESOURCE_BY_TYPE_URIS[idType]) ?? singleRecordUrl; + const url = baseApi.generateUrl(selectedUrl, { name: ':recordId', value: recordId }); return baseApi.getJson({ url, diff --git a/src/common/constants/api.constants.ts b/src/common/constants/api.constants.ts index ba0e02e8..3183c596 100644 --- a/src/common/constants/api.constants.ts +++ b/src/common/constants/api.constants.ts @@ -13,3 +13,11 @@ export const DEFAULT_PAGES_METADATA = { totalElements: 0, totalPages: 0, }; + +export enum ExternalResourceIdType { + Inventory = 'inventory', +} + +export const GET_RESOURCE_BY_TYPE_URIS = { + [ExternalResourceIdType.Inventory]: `${BIBFRAME_API_ENDPOINT}/preview/:recordId`, +}; diff --git a/src/common/constants/routes.constants.ts b/src/common/constants/routes.constants.ts index 41174b46..376d13d0 100644 --- a/src/common/constants/routes.constants.ts +++ b/src/common/constants/routes.constants.ts @@ -19,13 +19,14 @@ export const ROUTES = { uri: '/resources/:resourceId/edit', name: 'ld.editResource', }, - EXTERNAL_RESOURCE_EDIT: { - uri: '/resources/external/:resourceId/edit', - name: 'ld.externalResource', + EXTERNAL_RESOURCE_PREVIEW: { + uri: '/resources/external/:externalId/preview', + name: 'ld.externalResourcePreview', }, }; export const RESOURCE_URLS = [ROUTES.RESOURCE_EDIT.uri]; +export const EXTERNAL_RESOURCE_URLS = [ROUTES.EXTERNAL_RESOURCE_PREVIEW.uri]; export const RESOURCE_EDIT_CREATE_URLS = [ROUTES.RESOURCE_EDIT.uri, ROUTES.RESOURCE_CREATE.uri]; diff --git a/src/common/helpers/record.helper.ts b/src/common/helpers/record.helper.ts index c96fdbf0..5480fd39 100644 --- a/src/common/helpers/record.helper.ts +++ b/src/common/helpers/record.helper.ts @@ -148,13 +148,18 @@ export const getSelectedRecordBlocks = (searchParams: URLSearchParams) => { }; export const getRecordTitle = (record: RecordEntry) => { - const { block } = getEditingRecordBlocks(record); + // TODO: unify interactions with record and its format + // Some functions expect { resource: { %RECORD_CONTENTS% }} + // Others, like this one, expect { %RECORD_CONTENTS% } + const recordContents = unwrapRecordValuesFromCommonContainer(record); + + const { block } = getEditingRecordBlocks(recordContents); let selectedTitle; TITLE_CONTAINER_URIS.every(uri => { const selectedTitleContainer = ( - record[block!]?.['http://bibfra.me/vocab/marc/title'] as unknown as Record[] + recordContents[block!]?.['http://bibfra.me/vocab/marc/title'] as unknown as Record[] )?.find(obj => Object.hasOwn(obj, uri)); if (selectedTitleContainer) { @@ -180,6 +185,9 @@ export const getAdjustedRecordContents = ({ record, block, reference, asClone }: }; }; +export const unwrapRecordValuesFromCommonContainer = (record: RecordEntry) => + (record.resource ?? record) as RecordEntry; + export const wrapRecordValuesWithCommonContainer = (record: RecordEntry) => ({ resource: record }); export const checkIfRecordHasDependencies = (record: RecordEntry) => { diff --git a/src/common/hooks/useConfig.hook.ts b/src/common/hooks/useConfig.hook.ts index cea1573b..710a2927 100644 --- a/src/common/hooks/useConfig.hook.ts +++ b/src/common/hooks/useConfig.hook.ts @@ -1,4 +1,4 @@ -import { useContext } from 'react'; +import { useContext, useRef } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { v4 as uuidv4 } from 'uuid'; import state from '@state'; @@ -33,6 +33,7 @@ export const useConfig = () => { const setPreviewContent = useSetRecoilState(state.inputs.previewContent); const setSelectedRecordBlocks = useSetRecoilState(state.inputs.selectedRecordBlocks); const { getProcessedRecordAndSchema } = useProcessedRecordAndSchema(); + const isProcessingProfiles = useRef(false); const prepareFields = (profiles: ProfileEntry[]): ResourceTemplates => { const preparedFields = profiles.reduce((fields, profile) => { @@ -87,43 +88,51 @@ export const useConfig = () => { }; const getProfiles = async ({ record, recordId, previewParams, asClone }: GetProfiles): Promise => { - const hasStoredProfiles = profiles?.length; - const response = hasStoredProfiles ? profiles : await fetchProfiles(); - // TODO: check a list of supported profiles and implement the profile selection - const selectedProfile = response.find(({ name }: ProfileEntry) => name === PROFILE_NAMES.MONOGRAPH); - const templates = preparedFields || prepareFields(response); - - if (!hasStoredProfiles) { - setProfiles(response); - } + if (isProcessingProfiles.current && (record || recordId)) return; + + try { + isProcessingProfiles.current = true; - setUserValues({}); - - const recordData = record?.resource || {}; - const recordTitle = getRecordTitle(recordData as RecordEntry); - const entities = getPrimaryEntitiesFromRecord(record as RecordEntry); - - if (selectedProfile) { - setSelectedProfile(selectedProfile); - - const { updatedSchema, initKey } = await buildSchema(selectedProfile, templates, recordData, asClone); - - if (previewParams && recordId) { - setPreviewContent(prev => [ - ...(previewParams.singular ? [] : prev.filter(({ id }) => id !== recordId)), - { - id: recordId, - base: updatedSchema, - userValues: userValuesService.getAllValues(), - initKey, - title: recordTitle, - entities, - }, - ]); + const hasStoredProfiles = profiles?.length; + const response = hasStoredProfiles ? profiles : await fetchProfiles(); + // TODO: check a list of supported profiles and implement the profile selection + const selectedProfile = response.find(({ name }: ProfileEntry) => name === PROFILE_NAMES.MONOGRAPH); + const templates = preparedFields || prepareFields(response); + + if (!hasStoredProfiles) { + setProfiles(response); } - } - return response; + setUserValues({}); + + const recordData = record?.resource || {}; + const recordTitle = getRecordTitle(recordData as RecordEntry); + const entities = getPrimaryEntitiesFromRecord(record as RecordEntry); + + if (selectedProfile) { + setSelectedProfile(selectedProfile); + + const { updatedSchema, initKey } = await buildSchema(selectedProfile, templates, recordData, asClone); + + if (previewParams && recordId) { + setPreviewContent(prev => [ + ...(previewParams.singular ? [] : prev.filter(({ id }) => id !== recordId)), + { + id: recordId, + base: updatedSchema, + userValues: userValuesService.getAllValues(), + initKey, + title: recordTitle, + entities, + }, + ]); + } + } + + return response; + } finally { + isProcessingProfiles.current = false; + } }; return { getProfiles, prepareFields }; diff --git a/src/common/hooks/useRecordControls.ts b/src/common/hooks/useRecordControls.ts index e4d7ec44..7c602df1 100644 --- a/src/common/hooks/useRecordControls.ts +++ b/src/common/hooks/useRecordControls.ts @@ -25,6 +25,7 @@ import { generateEditResourceUrl } from '@common/helpers/navigation.helper'; import { useBackToSearchUri } from './useBackToSearchUri'; import state from '@state'; import { useContainerEvents } from './useContainerEvents'; +import { ExternalResourceIdType } from '@common/constants/api.constants'; type SaveRecordProps = { asRefToNewRecord?: boolean; @@ -32,6 +33,13 @@ type SaveRecordProps = { isNavigatingBack?: boolean; }; +type IBaseFetchRecord = { + recordId?: string; + cachedRecord?: RecordEntry; + idType?: ExternalResourceIdType; + errorMessage?: string; +}; + export const useRecordControls = () => { const [searchParams, setSearchParams] = useSearchParams(); const setIsLoading = useSetRecoilState(state.loadingState.isLoading); @@ -59,30 +67,30 @@ export const useRecordControls = () => { const isClone = queryParams.get(QueryParams.CloneOf); const fetchRecord = async (recordId: string, previewParams?: PreviewParams) => { - try { - const profile = PROFILE_BFIDS.MONOGRAPH; - const locallySavedData = getSavedRecord(profile, recordId); - const recordData: RecordEntry = - locallySavedData && !previewParams ? locallySavedData.data : await getRecord({ recordId }); - - if (!previewParams) { - setCurrentlyEditedEntityBfid(new Set(getPrimaryEntitiesFromRecord(recordData))); - setRecord(recordData); - } + const profile = PROFILE_BFIDS.MONOGRAPH; + const locallySavedData = getSavedRecord(profile, recordId); + const cachedRecord: RecordEntry | undefined = + locallySavedData && !previewParams ? (locallySavedData.data as RecordEntry) : undefined; - setCurrentlyPreviewedEntityBfid(new Set(getPrimaryEntitiesFromRecord(recordData, !!previewParams))); + const recordData = await getRecordAndInitializeParsing({ recordId, cachedRecord }); - await getProfiles({ record: recordData, recordId, previewParams, asClone: Boolean(isClone) }); + if (!recordData) return; - setIsEdited(false); - } catch (_err) { - console.error('Error fetching record.'); - - setStatusMessages(currentStatus => [ - ...currentStatus, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); + if (!previewParams) { + setCurrentlyEditedEntityBfid(new Set(getPrimaryEntitiesFromRecord(recordData))); + setRecord(recordData); } + + setCurrentlyPreviewedEntityBfid(new Set(getPrimaryEntitiesFromRecord(recordData, !!previewParams))); + + await getProfiles({ + record: recordData, + recordId, + previewParams, + asClone: Boolean(isClone), + }); + + setIsEdited(false); }; const saveRecord = async ({ @@ -120,10 +128,7 @@ export const useRecordControls = () => { setStatusMessages(currentStatus => [ ...currentStatus, - UserNotificationFactory.createMessage( - StatusType.success, - recordId ? 'ld.rdUpdateSuccess' : 'ld.rdSaveSuccess', - ), + UserNotificationFactory.createMessage(StatusType.success, recordId ? 'ld.rdUpdateSuccess' : 'ld.rdSaveSuccess'), ]); // TODO: isEdited state update is not immediately reflected in the @@ -198,7 +203,15 @@ export const useRecordControls = () => { const discardRecord = (clearState = true) => { if (clearState) clearRecordState(); - navigate(searchResultsUri); + // TODO: if record is external, navigate BACK (to inventory) + // same on SAVE & CLOSE + // also, CLEAN OUT /EXTERNAL/ URI from history on SAVE & CLOSE + + // sth like "lastNonEditView" => and always redirect to that + + // navigate(searchResultsUri); + + navigate(-1); }; const deleteRecord = async () => { @@ -261,6 +274,36 @@ export const useRecordControls = () => { } }; + const getRecordAndInitializeParsing = async ({ recordId, cachedRecord, idType, errorMessage }: IBaseFetchRecord) => { + if (!recordId && !cachedRecord) return; + + try { + const recordData: RecordEntry = cachedRecord ?? (recordId && (await getRecord({ recordId, idType }))); + + await getProfiles({ + record: recordData, + recordId, + }); + + return recordData; + } catch (_err) { + setStatusMessages(currentStatus => [ + ...currentStatus, + UserNotificationFactory.createMessage(StatusType.error, errorMessage ?? 'ld.errorFetching'), + ]); + } + }; + + const fetchExternalRecordForPreview = async (recordId?: string, idType = ExternalResourceIdType.Inventory) => { + if (!recordId) return; + + await getRecordAndInitializeParsing({ + recordId, + idType, + errorMessage: 'ld.errorFetchingExternalResourceForPreview', + }); + }; + return { fetchRecord, saveRecord, @@ -269,5 +312,6 @@ export const useRecordControls = () => { discardRecord, clearRecordState, fetchRecordAndSelectEntityValues, + fetchExternalRecordForPreview, }; }; diff --git a/src/common/i18n/messages.ts b/src/common/i18n/messages.ts index c58e95ea..d9e1604c 100644 --- a/src/common/i18n/messages.ts +++ b/src/common/i18n/messages.ts @@ -179,12 +179,14 @@ export const BASE_LOCALE = { 'ld.to': 'To', 'ld.apply': 'Apply', 'ld.notSpecified': 'Not specified', - 'ld.externalResource': 'External resource', + 'ld.externalResourcePreview': 'External resource preview', + 'ld.errorFetchingExternalResourceForPreview': 'Error fetching external resource for preview', 'ld.fetchingExternalResourceById': 'Fetching external resource id {resourceId}...', 'ld.lastUpdated': 'Last updated', 'ld.marcAuthorityRecord': 'MARC authority record', 'ld.selectBrowseOption': 'Select a browse option', - 'ld.searchQueryWouldBeHere': '{query} would be here' + 'ld.searchQueryWouldBeHere': '{query} would be here', + 'ld.continue': 'Continue', }; export const i18nMessages = { diff --git a/src/components/ExternalResourceLoader/ExternalResourceLoader.tsx b/src/components/ExternalResourceLoader/ExternalResourceLoader.tsx index 058bc0f5..c07846b0 100644 --- a/src/components/ExternalResourceLoader/ExternalResourceLoader.tsx +++ b/src/components/ExternalResourceLoader/ExternalResourceLoader.tsx @@ -3,12 +3,12 @@ import { useParams } from 'react-router-dom'; import './ExternalResourceLoader.scss'; export const ExternalResourceLoader = () => { - const { resourceId } = useParams(); + const { externalId } = useParams(); return ( - + ); diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 97397c91..010bb45c 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,17 +1,24 @@ import { useRoutePathPattern } from '@common/hooks/useRoutePathPattern'; -import { RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; +import { EXTERNAL_RESOURCE_URLS, RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; import { RecordControls } from '@components/RecordControls'; -import './Footer.scss'; import state from '@state'; import { useRecoilValue } from 'recoil'; +import { PreviewExternalResourceControls } from '@components/PreviewExternalResourceControls'; +import './Footer.scss'; export const Footer = () => { const showRecordControls = useRoutePathPattern(RESOURCE_EDIT_CREATE_URLS); + const showExternalResourceControls = useRoutePathPattern(EXTERNAL_RESOURCE_URLS); const marcPreviewData = useRecoilValue(state.data.marcPreview); + const record = useRecoilValue(state.inputs.record); + const isVisible = (showRecordControls || (showExternalResourceControls && record)) && !marcPreviewData; - return showRecordControls && !marcPreviewData ? ( - - - - ) : null; + return ( + isVisible && ( + + {showRecordControls && } + {showExternalResourceControls && } + + ) + ); }; diff --git a/src/components/Nav/Nav.tsx b/src/components/Nav/Nav.tsx index fe840a3c..baf5dcd2 100644 --- a/src/components/Nav/Nav.tsx +++ b/src/components/Nav/Nav.tsx @@ -1,20 +1,27 @@ -import { RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; +import { EXTERNAL_RESOURCE_URLS, RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; import { DOM_ELEMENTS } from '@common/constants/domElementsIdentifiers.constants'; import { useRoutePathPattern } from '@common/hooks/useRoutePathPattern'; import { EditControlPane } from '@components/EditControlPane'; import state from '@state'; import { useRecoilValue } from 'recoil'; -import './Nav.scss'; import { ViewMarcControlPane } from '@components/ViewMarcControlPane'; +import { PreviewExternalResourcePane } from '@components/PreviewExternalResourcePane'; +import './Nav.scss'; export const Nav = () => { const isEditSectionOpen = useRoutePathPattern(RESOURCE_EDIT_CREATE_URLS); + const isExternalResourceSectionOpen = useRoutePathPattern(EXTERNAL_RESOURCE_URLS); const marcPreviewData = useRecoilValue(state.data.marcPreview); + const record = useRecoilValue(state.inputs.record); + const isVisible = isEditSectionOpen || (isExternalResourceSectionOpen && record); return ( - - {isEditSectionOpen && !marcPreviewData && } - {marcPreviewData && } - + isVisible && ( + + {isEditSectionOpen && !marcPreviewData && } + {marcPreviewData && } + {isExternalResourceSectionOpen && } + + ) ); }; diff --git a/src/components/Preview/Preview.scss b/src/components/Preview/Preview.scss index 0dfc5b37..24c59026 100644 --- a/src/components/Preview/Preview.scss +++ b/src/components/Preview/Preview.scss @@ -4,6 +4,17 @@ min-height: 0.5rem; } + .preview-entity { + max-width: 560px; + width: 100%; + + &:not(:last-child) { + border-right: 1px solid rgba(0, 0, 0, 0.1); + padding-right: 0.938rem; + margin-right: 0.938rem; + } + } + .preview-block { border-bottom: 1px solid #00000019; margin-top: 0.75rem; @@ -63,3 +74,10 @@ .positioned-second { order: 2; } + +.preview-panel-row { + margin: 0.938rem 0; + display: flex; + flex-direction: row; + justify-content: center; +} diff --git a/src/components/Preview/Preview.tsx b/src/components/Preview/Preview.tsx index 248ff3f5..ab64aa67 100644 --- a/src/components/Preview/Preview.tsx +++ b/src/components/Preview/Preview.tsx @@ -28,8 +28,11 @@ type IPreview = { altSchema?: Schema; altUserValues?: UserValues; altInitKey?: string; + altDisplayNames?: Record; headless?: boolean; hideActions?: boolean; + forceRenderAllTopLevelEntities?: boolean; + entityRowDisplay?: boolean; }; type Fields = { @@ -52,7 +55,16 @@ const checkShouldGroupWrap = (entry = {} as SchemaEntry, level: number) => { return (!children?.length || type === AdvancedFieldType.dropdown) && level !== GROUP_BY_LEVEL; }; -export const Preview: FC = ({ altSchema, altUserValues, altInitKey, headless = false, hideActions }) => { +export const Preview: FC = ({ + altSchema, + altUserValues, + altInitKey, + altDisplayNames, + headless = false, + hideActions, + forceRenderAllTopLevelEntities, + entityRowDisplay, +}) => { const userValuesFromState = useRecoilValue(state.inputs.userValues); const selectedEntries = useRecoilValue(state.config.selectedEntries); const setRecordStatus = useSetRecoilState(state.status.recordStatus); @@ -96,7 +108,7 @@ export const Preview: FC = ({ altSchema, altUserValues, altInitKey, he if (type === AdvancedFieldType.dropdownOption && !isOnBranchWithUserValue) return null; // don't render top level entities not selected for preview - if (isEntity && !currentlyPreviewedEntityBfid.has(bfid)) return null; + if (isEntity && !currentlyPreviewedEntityBfid.has(bfid) && !forceRenderAllTopLevelEntities) return null; // TODO: define and organize the rules for display when there's clarity const isPreviewable = !NOT_PREVIEWABLE_TYPES.includes(type as AdvancedFieldType); @@ -118,19 +130,22 @@ export const Preview: FC = ({ altSchema, altUserValues, altInitKey, he const shouldRenderPlaceholders = (isPreviewable && isGroupable && !isOnBranchWithUserValue) || !isOnBranchWithUserValue; const isDependentDropdown = type === AdvancedFieldType.dropdown && !!linkedEntry?.controlledBy; - const displayNameWithAltValue = PREVIEW_ALT_DISPLAY_LABELS[displayName] || displayName; + const displayNameWithAltValue = + altDisplayNames?.[displayName] || PREVIEW_ALT_DISPLAY_LABELS[displayName] || displayName; const isBlock = level === GROUP_BY_LEVEL && shouldRenderLabelOrPlaceholders; const isBlockContents = level === GROUP_CONTENTS_LEVEL; const isInstance = bfid === PROFILE_BFIDS.INSTANCE; const showEntityActions = !hideActions && isEntity; + const wrapEntities = forceRenderAllTopLevelEntities && isEntity; return ( ( = ({ altSchema, altUserValues, altInitKey, he 'value-heading': !isGroupable, })} > - {isEntity && !isInstance && } + {isEntity && !isInstance && !altDisplayNames && } {displayNameWithAltValue} {showEntityActions && !isInstance && ( @@ -215,7 +230,10 @@ export const Preview: FC = ({ altSchema, altUserValues, altInitKey, he }); return ( - + {!headless && ( diff --git a/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.scss b/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.scss new file mode 100644 index 00000000..9c6a5920 --- /dev/null +++ b/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.scss @@ -0,0 +1,9 @@ +.preview-external-resource-controls { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + + :first-child { + margin-right: auto; + } +} diff --git a/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.tsx b/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.tsx new file mode 100644 index 00000000..94aad0b9 --- /dev/null +++ b/src/components/PreviewExternalResourceControls/PreviewExternalResourceControls.tsx @@ -0,0 +1,19 @@ +import { Button, ButtonType } from '@components/Button'; +import { FormattedMessage } from 'react-intl'; +import { useNavigate } from 'react-router-dom'; +import './PreviewExternalResourceControls.scss'; + +export const PreviewExternalResourceControls = () => { + const navigate = useNavigate(); + + return ( + + navigate(-1)}> + + + + + + + ); +}; diff --git a/src/components/PreviewExternalResourceControls/index.ts b/src/components/PreviewExternalResourceControls/index.ts new file mode 100644 index 00000000..0941e93e --- /dev/null +++ b/src/components/PreviewExternalResourceControls/index.ts @@ -0,0 +1 @@ +export { PreviewExternalResourceControls } from './PreviewExternalResourceControls'; diff --git a/src/components/PreviewExternalResourcePane/PreviewExternalResourcePane.tsx b/src/components/PreviewExternalResourcePane/PreviewExternalResourcePane.tsx new file mode 100644 index 00000000..c48ab3fd --- /dev/null +++ b/src/components/PreviewExternalResourcePane/PreviewExternalResourcePane.tsx @@ -0,0 +1,30 @@ +import { useNavigate } from 'react-router-dom'; +import { Button, ButtonType } from '@components/Button'; +import Times16 from '@src/assets/times-16.svg?react'; +import { useRecoilValue } from 'recoil'; +import state from '@state'; +import { getRecordTitle } from '@common/helpers/record.helper'; + +export const PreviewExternalResourcePane = () => { + const navigate = useNavigate(); + const record = useRecoilValue(state.inputs.record); + + return ( + + + navigate(-1)} + className="nav-close" + > + + + + + {record && getRecordTitle(record)} + + + + ); +}; diff --git a/src/components/PreviewExternalResourcePane/index.ts b/src/components/PreviewExternalResourcePane/index.ts new file mode 100644 index 00000000..3b135191 --- /dev/null +++ b/src/components/PreviewExternalResourcePane/index.ts @@ -0,0 +1 @@ +export { PreviewExternalResourcePane } from './PreviewExternalResourcePane'; diff --git a/src/components/RecordControls/RecordControls.tsx b/src/components/RecordControls/RecordControls.tsx index a1a0549e..6c892e11 100644 --- a/src/components/RecordControls/RecordControls.tsx +++ b/src/components/RecordControls/RecordControls.tsx @@ -2,17 +2,11 @@ import { memo } from 'react'; import { SaveRecord } from '@components/SaveRecord'; import { CloseRecord } from '@components/CloseRecord'; import './RecordControls.scss'; -import { useRoutePathPattern } from '@common/hooks/useRoutePathPattern'; -import { RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; -export const RecordControls = memo(() => { - const isEditSectionOpen = useRoutePathPattern(RESOURCE_EDIT_CREATE_URLS); - - return isEditSectionOpen ? ( - - - - - - ) : null; -}); +export const RecordControls = memo(() => ( + + + + + +)); diff --git a/src/components/SearchResultEntry/SearchResultEntry.tsx b/src/components/SearchResultEntry/SearchResultEntry.tsx index 810a0d45..cadda47b 100644 --- a/src/components/SearchResultEntry/SearchResultEntry.tsx +++ b/src/components/SearchResultEntry/SearchResultEntry.tsx @@ -73,10 +73,7 @@ export const SearchResultEntry: FC = ({ instances, ...restOfW } catch (error) { console.error(error); - setCommonStatus(prev => [ - ...prev, - UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching'), - ]); + setCommonStatus(prev => [...prev, UserNotificationFactory.createMessage(StatusType.error, 'ld.errorFetching')]); } finally { setIsLoading(false); } diff --git a/src/views/ExternalResource/ExternalResourceEdit.tsx b/src/views/ExternalResource/ExternalResourceEdit.tsx deleted file mode 100644 index 2327c715..00000000 --- a/src/views/ExternalResource/ExternalResourceEdit.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { ExternalResourceLoader } from '@components/ExternalResourceLoader'; - -export const ExternalResourceEdit = () => { - // TODO: initiate fetching of external resource via :resourceId param here - // afterwards, conditionally return Edit view - // if the external resource has been successfully fetched - - return ; -}; diff --git a/src/views/ExternalResource/ExternalResourcePreview.scss b/src/views/ExternalResource/ExternalResourcePreview.scss new file mode 100644 index 00000000..7a903508 --- /dev/null +++ b/src/views/ExternalResource/ExternalResourcePreview.scss @@ -0,0 +1,4 @@ +.external-resource-preview { + height: 100%; + padding: 0 0.938rem; +} \ No newline at end of file diff --git a/src/views/ExternalResource/ExternalResourcePreview.tsx b/src/views/ExternalResource/ExternalResourcePreview.tsx new file mode 100644 index 00000000..7c4e6a24 --- /dev/null +++ b/src/views/ExternalResource/ExternalResourcePreview.tsx @@ -0,0 +1,37 @@ +import { ExternalResourceLoader } from '@components/ExternalResourceLoader'; +import state from '@state'; +import { useParams } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { useEffect } from 'react'; +import { useRecordControls } from '@common/hooks/useRecordControls'; +import { ExternalResourceIdType } from '@common/constants/api.constants'; +import { Preview } from '@components/Preview'; +import { EDIT_ALT_DISPLAY_LABELS } from '@common/constants/uiElements.constants'; +import './ExternalResourcePreview.scss'; + +export const ExternalResourcePreview = () => { + const record = useRecoilValue(state.inputs.record); + const { fetchExternalRecordForPreview } = useRecordControls(); + const { externalId } = useParams(); + + useEffect(() => { + // TODO: if applicable in future, pass in resource type from query params + fetchExternalRecordForPreview(externalId, ExternalResourceIdType.Inventory); + }, [externalId]); + + return ( + + {record ? ( + + ) : ( + + )} + + ); +}; diff --git a/src/views/ExternalResource/index.ts b/src/views/ExternalResource/index.ts index 191bd085..c8eb214b 100644 --- a/src/views/ExternalResource/index.ts +++ b/src/views/ExternalResource/index.ts @@ -1 +1 @@ -export { ExternalResourceEdit } from './ExternalResourceEdit'; +export { ExternalResourcePreview } from './ExternalResourcePreview'; diff --git a/src/views/Root/Root.tsx b/src/views/Root/Root.tsx index 01ea224d..71ad29e6 100644 --- a/src/views/Root/Root.tsx +++ b/src/views/Root/Root.tsx @@ -2,7 +2,7 @@ import { Outlet } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import classNames from 'classnames'; import { MODAL_CONTAINER_ID } from '@common/constants/uiElements.constants'; -import { FIXED_HEIGHT_VIEWS, RESOURCE_EDIT_CREATE_URLS } from '@common/constants/routes.constants'; +import { FIXED_HEIGHT_VIEWS } from '@common/constants/routes.constants'; import { useRoutePathPattern } from '@common/hooks/useRoutePathPattern'; import { CommonStatus } from '@components/CommonStatus'; import { Nav } from '@components/Nav'; @@ -13,11 +13,10 @@ import state from '@state'; export const Root = () => { const fixedHeightContainerView = useRoutePathPattern(FIXED_HEIGHT_VIEWS); const isLoading = useRecoilValue(state.loadingState.isLoading); - const isEditSectionOpen = useRoutePathPattern(RESOURCE_EDIT_CREATE_URLS); return ( - {isEditSectionOpen && } + diff --git a/src/views/index.tsx b/src/views/index.tsx index b91b626b..d059b423 100644 --- a/src/views/index.tsx +++ b/src/views/index.tsx @@ -2,4 +2,4 @@ export { Root } from './Root'; export { Load } from './Load'; export { Edit, EditWrapper } from './Edit'; export { SearchView as Search } from './Search'; -export { ExternalResourceEdit } from './ExternalResource'; +export { ExternalResourcePreview } from './ExternalResource';