diff --git a/apps/researcher/package.json b/apps/researcher/package.json
index a4f7aa7a6..d2290714c 100644
--- a/apps/researcher/package.json
+++ b/apps/researcher/package.json
@@ -24,22 +24,24 @@
"@clerk/nextjs": "4.23.2",
"@clerk/types": "3.51.0",
"@colonial-collections/content": "*",
+ "@colonial-collections/database": "*",
"@colonial-collections/iris": "*",
"@colonial-collections/label-fetcher": "*",
"@colonial-collections/list-store": "*",
"@hapi/hoek": "11.0.2",
"@headlessui/react": "1.7.16",
"@heroicons/react": "2.0.18",
+ "@hookform/resolvers": "3.3.1",
"@next/mdx": "13.4.19",
"classnames": "2.3.2",
"fetch-sparql-endpoint": "3.3.3",
"next": "13.5.2",
"next-intl": "3.0.0-beta.7",
- "@colonial-collections/database": "*",
"openseadragon": "4.1.0",
"rdf-object": "1.14.0",
"react": "18.2.0",
"react-dom": "18.2.0",
+ "react-hook-form": "7.46.1",
"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]/actions.ts b/apps/researcher/src/app/[locale]/communities/[slug]/actions.ts
index 0c1695542..774cdee73 100644
--- a/apps/researcher/src/app/[locale]/communities/[slug]/actions.ts
+++ b/apps/researcher/src/app/[locale]/communities/[slug]/actions.ts
@@ -1,6 +1,29 @@
'use server';
-import {joinCommunity} from '@/lib/community';
+import {joinCommunity, editDescription} from '@/lib/community';
+import {revalidatePath} from 'next/cache';
+
+interface editDescriptionActionProps {
+ communityId: string;
+ communitySlug: string;
+ description: string;
+}
+
+async function editDescriptionAction({
+ communityId,
+ communitySlug,
+ description,
+}: editDescriptionActionProps) {
+ try {
+ await editDescription({communityId, description});
+ revalidatePath(`/[locale]/communities/${communitySlug}`, 'page');
+ revalidatePath('/[locale]/communities', 'page');
+
+ return {statusCode: 200};
+ } catch (err) {
+ return {statusCode: 500};
+ }
+}
// Export as server actions.
-export {joinCommunity};
+export {joinCommunity, editDescriptionAction};
diff --git a/apps/researcher/src/app/[locale]/communities/[slug]/edit-description-form.tsx b/apps/researcher/src/app/[locale]/communities/[slug]/edit-description-form.tsx
new file mode 100644
index 000000000..8a871dfb7
--- /dev/null
+++ b/apps/researcher/src/app/[locale]/communities/[slug]/edit-description-form.tsx
@@ -0,0 +1,113 @@
+'use client';
+
+import {useForm, SubmitHandler} from 'react-hook-form';
+import {useTranslations} from 'next-intl';
+import {useSlideOut, useNotifications} from 'ui';
+import {editDescriptionAction} from './actions';
+
+interface Props {
+ communityId: string;
+ communitySlug: string;
+ slideOutId: string;
+ description?: string;
+}
+
+interface FormValues {
+ description: string;
+ communityId: string;
+ communitySlug: string;
+}
+
+export default function EditDescriptionForm({
+ slideOutId,
+ communityId,
+ communitySlug,
+ description,
+}: Props) {
+ const {
+ register,
+ handleSubmit,
+ setError,
+ formState: {errors, isSubmitting},
+ } = useForm({
+ defaultValues: {
+ description: description ?? '',
+ communityId,
+ communitySlug,
+ },
+ });
+
+ const t = useTranslations('Community');
+ const {setIsVisible} = useSlideOut();
+ const {addNotification} = useNotifications();
+
+ const onSubmit: SubmitHandler = async data => {
+ const response = await editDescriptionAction(data);
+
+ if (response.statusCode > 200) {
+ setError('root.serverError', {
+ message: t('serverError'),
+ });
+ } else {
+ addNotification({
+ id: 'add-object-list-success',
+ message: <>{t('descriptionSuccessfullyEdited')}>,
+ type: 'success',
+ });
+ setIsVisible(slideOutId, false);
+ }
+ };
+
+ return (
+
+ );
+}
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..9a21a0bcc 100644
--- a/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx
+++ b/apps/researcher/src/app/[locale]/communities/[slug]/page.tsx
@@ -7,6 +7,11 @@ 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, SlideOutClosed, Notifications} from 'ui';
+import EditDescriptionForm from './edit-description-form';
interface Props {
params: {
@@ -15,6 +20,13 @@ interface Props {
};
}
+const slideOutFormId = 'add-object-list';
+const slideOutDescriptionId = 'edit-community-description';
+
+// Don't cache this page, so we always get the latest community data from the third-party Clerk.
+// With 'force-no-store', the description will change after editing.
+export const fetchCache = 'force-no-store';
+
export default async function CommunityPage({params}: Props) {
const t = await getTranslator(params.locale, 'Community');
@@ -39,9 +51,19 @@ export default async function CommunityPage({params}: Props) {
return ;
}
+ let objectLists;
+ try {
+ objectLists = await objectList.getCommunityListsWithObjects({
+ communityId: community.id,
+ limitObjects: 4,
+ });
+ } catch (err) {
+ return ;
+ }
+
return (
<>
-
+
@@ -52,27 +74,109 @@ export default async function CommunityPage({params}: Props) {
{isAdmin(memberships) && }
-
-
- {t('title')}
-
- {community.name}
-
-
-
-
-
-
-
- {/*Place the description here*/}
+
+
+
+
+
+ {t('title')}
+
+ {community.name}
+
+
+
+
+
+ {community.publicMetadata?.description}
+
+
+
+
+
+
+ {t('editDescriptionButton')}
+
- {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 => (
+
+ ))}
+
+
+
+
+
+
+
+ ))}
+