Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add object to list #272

Merged
merged 12 commits into from
Oct 20, 2023
2 changes: 1 addition & 1 deletion apps/researcher/src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default async function RootLayout({children, params: {locale}}: Props) {
</li>
</ul>
</div>
<div className="max-w-[1800px] mx-auto h-full min-h-screen flex flex-col justify-stretch items-stretch gap-8 pb-40">
<div className="max-w-[1800px] mx-auto min-h-screen flex flex-col justify-stretch items-stretch gap-8 pb-40">
<header className="w-full px-10 py-4 bg-neutral-50">
<Navigation locales={locales} />
</header>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use server';

import {getCommunityBySlug} from '@/lib/community';
import {objectList} from '@colonial-collections/database';
import {InsertObjectItem} from '@colonial-collections/database';
import {revalidatePath} from 'next/cache';

export async function getCommunityLists(communityId: string, objectId: string) {
return objectList.getByCommunityId(communityId, {objectIri: objectId});
}

interface AddObjectToListProps {
objectItem: InsertObjectItem;
barbarah marked this conversation as resolved.
Show resolved Hide resolved
communityId: string;
}

export async function addObjectToList({
objectItem,
communityId,
}: AddObjectToListProps) {
const addObjectPromise = objectList.addObject(objectItem);
const getCommunityPromise = getCommunityBySlug(communityId);
const [community] = await Promise.all([
getCommunityPromise,
addObjectPromise,
]);

revalidatePath(`/[locale]/communities/${community.slug}`, 'page');
}

export async function removeObjectFromList(id: number, communityId: string) {
await objectList.removeObject(id);
const community = await getCommunityBySlug(communityId);
revalidatePath(`/[locale]/communities/${community.slug}`, 'page');
}
170 changes: 170 additions & 0 deletions apps/researcher/src/app/[locale]/objects/[id]/object-lists-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
'use client';

import {useTranslations} from 'next-intl';
import {Fragment, useState, useEffect} from 'react';
import {Menu, Transition} from '@headlessui/react';
import {ChevronDownIcon} from '@heroicons/react/20/solid';
import {CheckIcon} from '@heroicons/react/24/outline';
import {useUser} from '@clerk/nextjs';
import {
getCommunityLists,
addObjectToList,
removeObjectFromList,
} from './object-lists-actions';
import {SelectObjectList} from '@colonial-collections/database';
import {useNotifications} from 'ui';

interface CommunityMenuItemsProps {
communityId: string;
objectId: string;
userId: string;
}

function CommunityMenuItems({
communityId,
objectId,
userId,
}: CommunityMenuItemsProps) {
const [objectLists, setObjectLists] = useState<SelectObjectList[]>([]);
const t = useTranslations('ObjectDetails');
const {addNotification} = useNotifications();

async function listClick(objectList: SelectObjectList) {
const isEmptyList = !objectList.objects!.length;

if (isEmptyList) {
try {
await addObjectToList({
objectItem: {
objectIri: objectId,
objectListId: objectList.id,
createdBy: userId,
},
communityId,
});

addNotification({
id: 'objectAddedToList',
message: t.rich('objectAddedToList', {
name: () => <em>{objectList.name}</em>,
}),
type: 'success',
});
} catch (err) {
addNotification({
id: 'errorObjectAddedToList',
message: t('errorObjectAddedToList'),
type: 'error',
});
}
} else {
try {
await removeObjectFromList(objectList.objects![0].id, communityId);

addNotification({
id: 'objectRemovedFromList',
message: t.rich('objectRemovedFromList', {
name: () => <em>{objectList.name}</em>,
}),
type: 'success',
});
} catch (err) {
addNotification({
id: 'errorObjectRemovedFromList',
message: t('errorObjectRemovedFromList'),
type: 'error',
});
}
}
}

useEffect(() => {
async function getLists() {
const lists = await getCommunityLists(communityId, objectId);
setObjectLists(lists);
}
getLists();
}, [communityId, objectId]);

if (!objectLists.length) {
return (
<div className="px-4 py-2 text-sm text-gray-400 italic">
{t('noListsInCommunity')}
</div>
);
}

return (
<div>
{objectLists.map(objectList => (
<Menu.Item key={objectList.id}>
<button
onClick={() => listClick(objectList)}
className="group flex items-center px-4 py-2 text-sm text-gray-700"
>
<span className="mr-3 h-5 w-5 blueGrey-500 group-hover:blueGrey-700">
{objectList.objects!.length ? (
<CheckIcon className="h-5 w-5" aria-hidden="true" />
) : null}
</span>
{objectList.name}
</button>
</Menu.Item>
))}
</div>
);
}

interface ObjectListsMenuProps {
objectId: string;
}

export default function ObjectListsMenu({objectId}: ObjectListsMenuProps) {
const t = useTranslations('ObjectDetails');
const {user} = useUser();

if (!user || !user.organizationMemberships.length) {
return null;
}

const communities = user.organizationMemberships.map(
membership => membership.organization
);

return (
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="p-1 sm:py-2 sm:px-3 rounded-xl text-xs bg-greenGrey-100 text-greenGrey-800 flex items-center gap-1">
{t('addToListButton')}
<ChevronDownIcon
className="-mr-1 h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Menu.Button>
</div>

<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{communities.map(community => (
<div key={community.id}>
<div className="font-semibold px-2 pt-2">{community.name}</div>
barbarah marked this conversation as resolved.
Show resolved Hide resolved
<CommunityMenuItems
userId={user.id}
communityId={community.id}
objectId={objectId}
/>
</div>
))}
</Menu.Items>
</Transition>
</Menu>
);
}
18 changes: 8 additions & 10 deletions apps/researcher/src/app/[locale]/objects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
import useCurrentPublisher from './useCurrentPublisher';
import {env} from 'node:process';
import {formatDateCreated} from './format-date-created';
import ObjectListsMenu from './object-lists-menu';
import {SignedIn} from '@clerk/nextjs';
import {Notifications} from 'ui';

// Revalidate the page
export const revalidate = 0;
Expand Down Expand Up @@ -106,16 +109,9 @@ export default async function Details({params}: Props) {
</ToFilteredListButton>
</div>
<div className="sm:flex justify-end gap-4 hidden">
<button className="p-1 sm:py-2 sm:px-3 rounded-full text-xs bg-sand-100 hover:bg-sand-200 transition text-sand-800 flex items-center gap-1">
{t('bookmarkButton')}
</button>
<button className="p-1 sm:py-2 sm:px-3 rounded-full text-xs bg-sand-100 hover:bg-sand-200 transition text-sand-800 flex items-center gap-1">
{t('shareButton')}
</button>
<select className="p-1 sm:py-2 sm:px-3 rounded-xl text-xs bg-sand-100 text-sand-800 flex items-center gap-1">
<option className="p-2">{t('addToListButton')}</option>
<option className="p-2">{t('createNewListButton')}</option>
</select>
<SignedIn>
<ObjectListsMenu objectId={id} />
</SignedIn>
</div>
</div>

Expand All @@ -127,6 +123,8 @@ export default async function Details({params}: Props) {
{object.name}
</h1>

<Notifications />

<div className="text-neutral-500 text-sm flex flex-col sm:flex-row justify-between items-start sm:items-center">
<div className="flex flex-row justify-start gap-1 ">
<span className="-ml-4 sm:ml-0">
Expand Down
7 changes: 6 additions & 1 deletion apps/researcher/src/messages/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@
"currentPublisher": "Current location of object",
"noOrganizationFound": "No organization found.",
"dateCreated": "Date Made",
"dateCreatedSubTitle": "When was the object made? Could be an exact date or a range."
"dateCreatedSubTitle": "When was the object made? Could be an exact date or a range.",
"noListsInCommunity": "No lists",
"objectAddedToList": "Object added to list <name></name>.",
"errorObjectAddedToList": "There was a problem adding the object to the list. Please try again later.",
"objectRemovedFromList": "Object removed from list <name></name>.",
"errorObjectRemovedFromList": "There was a problem removing the object from the list. Please try again later."
},
"PersonDetails": {
"backButton": "Back to results",
Expand Down
7 changes: 6 additions & 1 deletion apps/researcher/src/messages/nl/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@
"currentPublisher": "Huidige locatie van object",
"noOrganizationFound": "Geen organisatie gevonden.",
"dateCreated": "Datum van creatie",
"dateCreatedSubTitle": "Wanneer is het voorwerp gemaakt? Dit kan een exacte datum of een datumbereik zijn."
"dateCreatedSubTitle": "Wanneer is het voorwerp gemaakt? Dit kan een exacte datum of een datumbereik zijn.",
"noListsInCommunity": "Geen lijsten",
"objectAddedToList": "Object toegevoegd aan lijst <name></name>.",
"errorObjectAddedToList": "Er is een probleem opgetreden bij het toevoegen van het object aan de lijst. Probeer het later opnieuw.",
"objectRemovedFromList": "Object verwijderd van lijst <name></name>.",
"errorObjectRemovedFromList": "Er is een probleem opgetreden bij het verwijderen van het object van de lijst. Probeer het later opnieuw."
},
"PersonDetails": {
"backButton": "Terug naar resultaten",
Expand Down
1 change: 1 addition & 0 deletions packages/database/index.ts
sdevalk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * as objectList from './src/object-list';
export * from './src/db/types';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE `object_item` MODIFY COLUMN `object_list_id` int NOT NULL;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE `object_item` ADD `id` serial AUTO_INCREMENT NOT NULL;--> statement-breakpoint
ALTER TABLE `object_item` DROP PRIMARY KEY;--> statement-breakpoint
ALTER TABLE `object_item` ADD PRIMARY KEY(`id`);
Loading