diff --git a/apps/dataset-browser/src/app/[locale]/layout.tsx b/apps/dataset-browser/src/app/[locale]/layout.tsx index 40718f681..cd7c3738f 100644 --- a/apps/dataset-browser/src/app/[locale]/layout.tsx +++ b/apps/dataset-browser/src/app/[locale]/layout.tsx @@ -16,7 +16,7 @@ export default async function RootLayout({children, params: {locale}}: Props) { let messages; try { messages = (await import(`../../messages/${locale}/messages.json`)).default; - } catch (error) { + } catch (err) { notFound(); } diff --git a/apps/dataset-browser/src/app/[locale]/page.tsx b/apps/dataset-browser/src/app/[locale]/page.tsx index 2a85394f3..89850a553 100644 --- a/apps/dataset-browser/src/app/[locale]/page.tsx +++ b/apps/dataset-browser/src/app/[locale]/page.tsx @@ -97,9 +97,9 @@ export default async function Home({searchParams = {}}: Props) { let searchResult: SearchResult | undefined; try { searchResult = await datasetFetcher.search(searchOptions); - } catch (error) { + } catch (err) { hasError = true; - console.error(error); + console.error(err); } const locale = useLocale(); diff --git a/apps/researcher/package.json b/apps/researcher/package.json index 27503e0cb..ec6ba3190 100644 --- a/apps/researcher/package.json +++ b/apps/researcher/package.json @@ -31,6 +31,7 @@ "@hapi/hoek": "11.0.2", "@headlessui/react": "1.7.17", "@heroicons/react": "2.0.18", + "@hookform/resolvers": "3.3.1", "@next/mdx": "13.5.3", "classnames": "2.3.2", "fetch-sparql-endpoint": "3.3.3", @@ -40,6 +41,8 @@ "rdf-object": "1.14.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-hook-form": "7.46.1", + "tiny-case": "1.0.3", "zod": "3.22.2", "zustand": "4.3.9" }, diff --git a/apps/researcher/src/app/[locale]/communities/[slug]/[listId]/page.tsx b/apps/researcher/src/app/[locale]/communities/[slug]/[listId]/page.tsx new file mode 100644 index 000000000..52f9f19ff --- /dev/null +++ b/apps/researcher/src/app/[locale]/communities/[slug]/[listId]/page.tsx @@ -0,0 +1,7 @@ +export default function Page() { + return ( +
+

This page is under construction

+
+ ); +} diff --git a/apps/researcher/src/app/[locale]/communities/[slug]/buttons.tsx b/apps/researcher/src/app/[locale]/communities/[slug]/buttons.tsx index 20d4dfddc..ceca4b7a4 100644 --- a/apps/researcher/src/app/[locale]/communities/[slug]/buttons.tsx +++ b/apps/researcher/src/app/[locale]/communities/[slug]/buttons.tsx @@ -38,10 +38,10 @@ export function JoinCommunityButton({communityId}: Props) { organizationId: communityId, userId: user!.id, }); - } catch (error) { + } catch (err) { setIsClicked(false); setHasError(true); - console.error(error); + console.error(err); } }); }; diff --git a/apps/researcher/src/app/[locale]/communities/[slug]/object.tsx b/apps/researcher/src/app/[locale]/communities/[slug]/object.tsx new file mode 100644 index 000000000..1bd6c6c28 --- /dev/null +++ b/apps/researcher/src/app/[locale]/communities/[slug]/object.tsx @@ -0,0 +1,19 @@ +import heritageObjects from '@/lib/heritage-objects-instance'; + +interface Props { + objectIri: string; +} + +export default async function ObjectCard({objectIri}: Props) { + const object = await heritageObjects.getById(objectIri); + + if (!object) { + return null; + } + + return ( +
+ {object.name} +
+ ); +} diff --git a/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx b/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx index a9c50e9ef..17008e28f 100644 --- a/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx +++ b/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx @@ -7,6 +7,10 @@ import {getMemberships, getCommunityBySlug, isAdmin} from '@/lib/community'; import ErrorMessage from '@/components/error-message'; import {ClerkAPIResponseError} from '@clerk/shared'; import {revalidatePath} from 'next/cache'; +import {objectList} from '@colonial-collections/database'; +import ObjectCard from './object'; +import AddObjectListForm from '@/components/add-object-list-form'; +import {SlideOutButton, SlideOut, Notifications} from 'ui'; interface Props { params: { @@ -15,6 +19,8 @@ interface Props { }; } +const slideOutFormId = 'add-object-list'; + export default async function CommunityPage({params}: Props) { const t = await getTranslator(params.locale, 'Community'); @@ -39,9 +45,19 @@ export default async function CommunityPage({params}: Props) { return ; } + let objectLists; + try { + objectLists = await objectList.getByCommunityId(community.id, { + withObjects: true, + limitObjects: 4, + }); + } catch (err) { + return ; + } + return ( <> -
+
@@ -52,27 +68,93 @@ export default async function CommunityPage({params}: Props) { {isAdmin(memberships) && }
-
-

- {t('title')} - - {community.name} - -

-
-
-
-
-
- {/*Place the description here*/} +
+
+
+
+
-
- +
+
+

+ {t('title')} + + {community.name} + +

+ +
+
+ {/*Place the description here*/} +
+
+ +
-

{t('objectListsTitle')}

-
- {/*Place the lists here */} + +
+
+
+

{t('objectListsTitle')}

+

{t('objectListsSubTitle', {count: objectLists.length})}

+
+
+ {isAdmin(memberships) && ( + + {t('addObjectListButton')} + + )} +
+
+ + + + + + +
+ {objectLists.map(objectList => ( + +

+ {objectList.name} +

+

{objectList.description}

+ +
+
    + {objectList.objects?.map(object => ( + + ))} +
+ +
+ +
+
+ + ))} +
-
{/* TODO add number of lists here */}
+
+ + + +
); diff --git a/apps/researcher/src/app/[locale]/layout.tsx b/apps/researcher/src/app/[locale]/layout.tsx index ab228a59e..cd97f60d2 100644 --- a/apps/researcher/src/app/[locale]/layout.tsx +++ b/apps/researcher/src/app/[locale]/layout.tsx @@ -17,7 +17,7 @@ export default async function RootLayout({children, params: {locale}}: Props) { let messages; try { messages = (await import(`../../messages/${locale}/messages.json`)).default; - } catch (error) { + } catch (err) { notFound(); } diff --git a/apps/researcher/src/app/[locale]/page.tsx b/apps/researcher/src/app/[locale]/page.tsx index dc8852154..4931fc85b 100644 --- a/apps/researcher/src/app/[locale]/page.tsx +++ b/apps/researcher/src/app/[locale]/page.tsx @@ -92,9 +92,9 @@ export default async function Home({searchParams = {}}: Props) { let searchResult: SearchResult | undefined; try { searchResult = await heritageObjects.search(searchOptions); - } catch (error) { + } catch (err) { hasError = true; - console.error(error); + console.error(err); } const locale = useLocale(); diff --git a/apps/researcher/src/app/[locale]/persons/page.tsx b/apps/researcher/src/app/[locale]/persons/page.tsx index 82f2fc269..437d07398 100644 --- a/apps/researcher/src/app/[locale]/persons/page.tsx +++ b/apps/researcher/src/app/[locale]/persons/page.tsx @@ -98,9 +98,9 @@ export default async function Home({searchParams = {}}: Props) { let searchResult: SearchResult | undefined; try { searchResult = await personFetcher.search(searchOptions); - } catch (error) { + } catch (err) { hasError = true; - console.error(error); + console.error(err); } const locale = useLocale(); diff --git a/apps/researcher/src/components/add-object-list-form/actions.ts b/apps/researcher/src/components/add-object-list-form/actions.ts new file mode 100644 index 000000000..b386b0801 --- /dev/null +++ b/apps/researcher/src/components/add-object-list-form/actions.ts @@ -0,0 +1,18 @@ +'use server'; + +import {getCommunityById} from '@/lib/community'; +import {objectList} from '@colonial-collections/database'; +import {revalidatePath} from 'next/cache'; + +interface List { + communityId: string; + createdBy: string; + name: string; + description?: string; +} + +export async function addList(list: List) { + await objectList.create(list); + const community = await getCommunityById(list.communityId); + revalidatePath(`/[locale]/communities/${community.slug}`, 'page'); +} diff --git a/apps/researcher/src/components/add-object-list-form/index.tsx b/apps/researcher/src/components/add-object-list-form/index.tsx new file mode 100644 index 000000000..311ae6dd4 --- /dev/null +++ b/apps/researcher/src/components/add-object-list-form/index.tsx @@ -0,0 +1,151 @@ +'use client'; + +import {useAuth} from '@clerk/nextjs'; +import {addList} from './actions'; +import {useForm, SubmitHandler} from 'react-hook-form'; +import {zodResolver} from '@hookform/resolvers/zod'; +import {insertObjectListSchema} from '@colonial-collections/database/client'; +import {useTranslations} from 'next-intl'; +import {useSlideOut, useNotifications} from 'ui'; +import {camelCase} from 'tiny-case'; + +interface FormProps { + communityId: string; + userId: string; + slideOutId: string; +} + +interface FormValues { + name: string; + description: string; + createdBy: string; + communityId: string; +} + +function Form({communityId, userId, slideOutId}: FormProps) { + const { + register, + handleSubmit, + setError, + formState: {errors, isSubmitting}, + } = useForm({ + resolver: zodResolver(insertObjectListSchema), + defaultValues: { + name: '', + description: '', + createdBy: userId, + communityId, + }, + }); + + const t = useTranslations('AddObjectListForm'); + const {setIsVisible} = useSlideOut(); + const {addNotification} = useNotifications(); + + const onSubmit: SubmitHandler = async list => { + try { + await addList(list); + addNotification({ + id: 'add-object-list-success', + message: t.rich('listSuccessfullyAdded', { + name: () => {list.name}, + }), + type: 'success', + }); + setIsVisible(slideOutId, false); + } catch (err) { + setError('root.serverError', { + message: t('serverError'), + }); + } + }; + + return ( +
+ {errors.root?.serverError.message && ( +
+
+

+ {errors.root.serverError.message} +

+
+
+ )} +
+
+ + +

{errors.name && t(camelCase(`name_${errors.name.type}`))}

+
+
+
+ +
+
+ + {t('labelDescriptionSubTitle')} +