managed how collections are viewed by members
This commit is contained in:
parent
51c5615fea
commit
3abea1d1b7
|
@ -7,6 +7,7 @@ import { useState } from "react";
|
||||||
import ProfilePhoto from "./ProfilePhoto";
|
import ProfilePhoto from "./ProfilePhoto";
|
||||||
import { faCalendarDays } from "@fortawesome/free-regular-svg-icons";
|
import { faCalendarDays } from "@fortawesome/free-regular-svg-icons";
|
||||||
import useModalStore from "@/store/modals";
|
import useModalStore from "@/store/modals";
|
||||||
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
collection: CollectionIncludingMembersAndLinkCount;
|
collection: CollectionIncludingMembersAndLinkCount;
|
||||||
|
@ -27,6 +28,8 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
|
|
||||||
const [expandDropdown, setExpandDropdown] = useState(false);
|
const [expandDropdown, setExpandDropdown] = useState(false);
|
||||||
|
|
||||||
|
const permissions = usePermissions(collection.id as number);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-gradient-to-tr from-sky-100 from-10% via-gray-100 via-20% to-white to-100% self-stretch min-h-[12rem] rounded-2xl shadow duration-100 hover:shadow-none group relative ${className}`}
|
className={`bg-gradient-to-tr from-sky-100 from-10% via-gray-100 via-20% to-white to-100% self-stretch min-h-[12rem] rounded-2xl shadow duration-100 hover:shadow-none group relative ${className}`}
|
||||||
|
@ -58,7 +61,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
<ProfilePhoto
|
<ProfilePhoto
|
||||||
key={i}
|
key={i}
|
||||||
src={`/api/avatar/${e.userId}`}
|
src={`/api/avatar/${e.userId}`}
|
||||||
className="-mr-3"
|
className="-mr-3 border-[3px]"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -72,7 +75,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
<div className="text-right w-40">
|
<div className="text-right w-40">
|
||||||
<div className="text-sky-500 font-bold text-sm flex justify-end gap-1 items-center">
|
<div className="text-sky-500 font-bold text-sm flex justify-end gap-1 items-center">
|
||||||
<FontAwesomeIcon icon={faLink} className="w-5 h-5 text-sky-600" />
|
<FontAwesomeIcon icon={faLink} className="w-5 h-5 text-sky-600" />
|
||||||
{collection._count.links}
|
{collection._count && collection._count.links}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end gap-1 text-gray-600">
|
<div className="flex items-center justify-end gap-1 text-gray-600">
|
||||||
<FontAwesomeIcon icon={faCalendarDays} className="w-4 h-4" />
|
<FontAwesomeIcon icon={faCalendarDays} className="w-4 h-4" />
|
||||||
|
@ -91,6 +94,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: collection,
|
active: collection,
|
||||||
});
|
});
|
||||||
setExpandDropdown(false);
|
setExpandDropdown(false);
|
||||||
|
@ -103,6 +107,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: collection,
|
active: collection,
|
||||||
defaultIndex: 1,
|
defaultIndex: 1,
|
||||||
});
|
});
|
||||||
|
@ -116,6 +121,7 @@ export default function CollectionCard({ collection, className }: Props) {
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: collection,
|
active: collection,
|
||||||
defaultIndex: 2,
|
defaultIndex: 2,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
CollectionIncludingMembersAndLinkCount,
|
CollectionIncludingMembersAndLinkCount,
|
||||||
LinkIncludingShortenedCollectionAndTags,
|
LinkIncludingShortenedCollectionAndTags,
|
||||||
Member,
|
|
||||||
} from "@/types/global";
|
} from "@/types/global";
|
||||||
import { faFolder, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
import { faFolder, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
@ -61,7 +60,7 @@ export default function LinkCard({ link, count, className }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-gradient-to-tr from-slate-200 from-10% to-gray-50 via-20% shadow hover:shadow-none cursor-pointer duration-100 p-5 rounded-2xl relative group ${className}`}
|
className={`bg-gradient-to-tr from-slate-200 from-10% to-gray-50 via-20% shadow hover:shadow-none cursor-pointer duration-100 rounded-2xl relative group ${className}`}
|
||||||
>
|
>
|
||||||
{permissions && (
|
{permissions && (
|
||||||
<div
|
<div
|
||||||
|
@ -87,7 +86,7 @@ export default function LinkCard({ link, count, className }: Props) {
|
||||||
active: link,
|
active: link,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="flex items-start gap-5 sm:gap-10 h-full w-full"
|
className="flex items-start gap-5 sm:gap-10 h-full w-full p-5"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url.origin}&size=32`}
|
src={`https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url.origin}&size=32`}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faTrashCan } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faRightFromBracket,
|
||||||
|
faTrashCan,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
|
||||||
import useCollectionStore from "@/store/collections";
|
import useCollectionStore from "@/store/collections";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
toggleDeleteCollectionModal: Function;
|
toggleDeleteCollectionModal: Function;
|
||||||
|
@ -21,77 +25,92 @@ export default function DeleteCollection({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (!collection.id || collection.name !== inputField) return null;
|
if (permissions === true) if (collection.name !== inputField) return null;
|
||||||
|
|
||||||
const response = await removeCollection(collection.id);
|
const response = await removeCollection(collection.id as number);
|
||||||
if (response) {
|
if (response) {
|
||||||
toggleDeleteCollectionModal();
|
toggleDeleteCollectionModal();
|
||||||
router.push("/collections");
|
router.push("/collections");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const permissions = usePermissions(collection.id as number);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 justify-between sm:w-[35rem] w-80">
|
<div className="flex flex-col gap-3 justify-between sm:w-[35rem] w-80">
|
||||||
<p className="text-red-500 font-bold text-center">Warning!</p>
|
{permissions === true ? (
|
||||||
|
<>
|
||||||
|
<p className="text-red-500 font-bold text-center">Warning!</p>
|
||||||
|
|
||||||
<div className="max-h-[20rem] overflow-y-auto">
|
<div className="max-h-[20rem] overflow-y-auto">
|
||||||
<div className="text-gray-500">
|
<div className="text-gray-500">
|
||||||
<p>
|
<p>
|
||||||
Please note that deleting the collection will permanently remove all
|
Please note that deleting the collection will permanently remove
|
||||||
its contents, including the following:
|
all its contents, including the following:
|
||||||
</p>
|
</p>
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
<li className="list-inside">
|
<li className="list-inside">
|
||||||
Links: All links within the collection will be permanently
|
Links: All links within the collection will be permanently
|
||||||
deleted.
|
deleted.
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inside">
|
<li className="list-inside">
|
||||||
Tags: All tags associated with the collection will be removed.
|
Tags: All tags associated with the collection will be removed.
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inside">
|
<li className="list-inside">
|
||||||
Screenshots/PDFs: Any screenshots or PDFs attached to links within
|
Screenshots/PDFs: Any screenshots or PDFs attached to links
|
||||||
this collection will be permanently deleted.
|
within this collection will be permanently deleted.
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inside">
|
<li className="list-inside">
|
||||||
Members: Any members who have been granted access to the
|
Members: Any members who have been granted access to the
|
||||||
collection will lose their permissions and no longer be able to
|
collection will lose their permissions and no longer be able
|
||||||
view or interact with the content.
|
to view or interact with the content.
|
||||||
</li>
|
</li>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Please double-check that you have backed up any essential data
|
||||||
|
and have informed the relevant members about this action.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
|
||||||
Please double-check that you have backed up any essential data and
|
|
||||||
have informed the relevant members about this action.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<p className="text-sky-900 select-none text-center">
|
<p className="text-sky-900 select-none text-center">
|
||||||
To confirm, type "
|
To confirm, type "
|
||||||
<span className="font-bold text-sky-500">{collection.name}</span>
|
<span className="font-bold text-sky-500">{collection.name}</span>
|
||||||
" in the box below:
|
" in the box below:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
value={inputField}
|
||||||
|
onChange={(e) => setInputField(e.target.value)}
|
||||||
|
type="text"
|
||||||
|
placeholder={`Type "${collection.name}" Here.`}
|
||||||
|
className="w-72 sm:w-96 rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="text-gray-500">
|
||||||
|
Click the button below to leave the current collection:
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
<input
|
|
||||||
autoFocus
|
|
||||||
value={inputField}
|
|
||||||
onChange={(e) => setInputField(e.target.value)}
|
|
||||||
type="text"
|
|
||||||
placeholder={`Type "${collection.name}" Here.`}
|
|
||||||
className="w-72 sm:w-96 rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`mx-auto mt-2 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold duration-100 ${
|
className={`mx-auto mt-2 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold duration-100 ${
|
||||||
inputField === collection.name
|
permissions === true
|
||||||
? "bg-red-500 hover:bg-red-400 cursor-pointer"
|
? inputField === collection.name
|
||||||
: "cursor-not-allowed bg-red-300"
|
? "bg-red-500 hover:bg-red-400 cursor-pointer"
|
||||||
|
: "cursor-not-allowed bg-red-300"
|
||||||
|
: "bg-red-500 hover:bg-red-400 cursor-pointer"
|
||||||
}`}
|
}`}
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faTrashCan} className="h-5" />
|
<FontAwesomeIcon
|
||||||
Delete Collection
|
icon={permissions === true ? faTrashCan : faRightFromBracket}
|
||||||
|
className="h-5"
|
||||||
|
/>
|
||||||
|
{permissions === true ? "Delete" : "Leave"} Collection
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import addMemberToCollection from "@/lib/client/addMemberToCollection";
|
||||||
import Checkbox from "../../Checkbox";
|
import Checkbox from "../../Checkbox";
|
||||||
import SubmitButton from "@/components/SubmitButton";
|
import SubmitButton from "@/components/SubmitButton";
|
||||||
import ProfilePhoto from "@/components/ProfilePhoto";
|
import ProfilePhoto from "@/components/ProfilePhoto";
|
||||||
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
toggleCollectionModal: Function;
|
toggleCollectionModal: Function;
|
||||||
|
@ -29,6 +30,8 @@ export default function TeamManagement({
|
||||||
collection,
|
collection,
|
||||||
method,
|
method,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const permissions = usePermissions(collection.id as number);
|
||||||
|
|
||||||
const currentURL = new URL(document.URL);
|
const currentURL = new URL(document.URL);
|
||||||
|
|
||||||
const publicCollectionURL = `${currentURL.origin}/public/collections/${collection.id}`;
|
const publicCollectionURL = `${currentURL.origin}/public/collections/${collection.id}`;
|
||||||
|
@ -80,19 +83,23 @@ export default function TeamManagement({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 sm:w-[35rem] w-80">
|
<div className="flex flex-col gap-3 sm:w-[35rem] w-80">
|
||||||
<p className="text-sm text-sky-500">Make Public</p>
|
{permissions === true && (
|
||||||
|
<>
|
||||||
|
<p className="text-sm text-sky-500">Make Public</p>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Make this a public collection."
|
label="Make this a public collection."
|
||||||
state={collection.isPublic}
|
state={collection.isPublic}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCollection({ ...collection, isPublic: !collection.isPublic })
|
setCollection({ ...collection, isPublic: !collection.isPublic })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="text-gray-500 text-sm">
|
<p className="text-gray-500 text-sm">
|
||||||
This will let <b>Anyone</b> to view this collection.
|
This will let <b>Anyone</b> to view this collection.
|
||||||
</p>
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{collection.isPublic ? (
|
{collection.isPublic ? (
|
||||||
<div>
|
<div>
|
||||||
|
@ -116,54 +123,58 @@ export default function TeamManagement({
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<hr />
|
{permissions !== true && collection.isPublic && <hr />}
|
||||||
|
|
||||||
<p className="text-sm text-sky-500">Member Management</p>
|
{permissions === true && (
|
||||||
|
<>
|
||||||
|
<p className="text-sm text-sky-500">Member Management</p>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
value={member.user.email}
|
value={member.user.email}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setMember({
|
setMember({
|
||||||
...member,
|
...member,
|
||||||
user: { ...member.user, email: e.target.value },
|
user: { ...member.user, email: e.target.value },
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onKeyDown={(e) =>
|
onKeyDown={(e) =>
|
||||||
e.key === "Enter" &&
|
e.key === "Enter" &&
|
||||||
addMemberToCollection(
|
addMemberToCollection(
|
||||||
session.data?.user.email as string,
|
session.data?.user.email as string,
|
||||||
member.user.email,
|
member.user.email,
|
||||||
collection,
|
collection,
|
||||||
setMemberState
|
setMemberState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
className="w-full rounded-md p-3 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
className="w-full rounded-md p-3 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
addMemberToCollection(
|
addMemberToCollection(
|
||||||
session.data?.user.email as string,
|
session.data?.user.email as string,
|
||||||
member.user.email,
|
member.user.email,
|
||||||
collection,
|
collection,
|
||||||
setMemberState
|
setMemberState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="flex items-center justify-center bg-sky-500 hover:bg-sky-400 duration-100 text-white w-12 h-12 p-3 rounded-md cursor-pointer"
|
className="flex items-center justify-center bg-sky-500 hover:bg-sky-400 duration-100 text-white w-12 h-12 p-3 rounded-md cursor-pointer"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faUserPlus} className="w-5 h-5" />
|
<FontAwesomeIcon icon={faUserPlus} className="w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{collection?.members[0]?.user && (
|
{collection?.members[0]?.user && (
|
||||||
<>
|
<>
|
||||||
<p className="text-center text-gray-500 text-xs sm:text-sm">
|
<p className="text-center text-gray-500 text-xs sm:text-sm">
|
||||||
(All Members have <b>Read</b> access to this collection.)
|
(All Members have <b>Read</b> access to this collection.)
|
||||||
</p>
|
</p>
|
||||||
<div className="max-h-[20rem] overflow-auto flex flex-col gap-3 rounded-md shadow-inner">
|
<div className="max-h-[20rem] overflow-auto flex flex-col gap-3 rounded-md">
|
||||||
{collection.members
|
{collection.members
|
||||||
.sort((a, b) => (a.userId as number) - (b.userId as number))
|
.sort((a, b) => (a.userId as number) - (b.userId as number))
|
||||||
.map((e, i) => {
|
.map((e, i) => {
|
||||||
|
@ -172,24 +183,29 @@ export default function TeamManagement({
|
||||||
key={i}
|
key={i}
|
||||||
className="relative border p-2 rounded-md border-sky-100 flex flex-col sm:flex-row sm:items-center gap-2 justify-between"
|
className="relative border p-2 rounded-md border-sky-100 flex flex-col sm:flex-row sm:items-center gap-2 justify-between"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
{permissions === true && (
|
||||||
icon={faClose}
|
<FontAwesomeIcon
|
||||||
className="absolute right-2 top-2 text-gray-500 h-4 hover:text-red-500 duration-100 cursor-pointer"
|
icon={faClose}
|
||||||
title="Remove Member"
|
className="absolute right-2 top-2 text-gray-500 h-4 hover:text-red-500 duration-100 cursor-pointer"
|
||||||
onClick={() => {
|
title="Remove Member"
|
||||||
const updatedMembers = collection.members.filter(
|
onClick={() => {
|
||||||
(member) => {
|
const updatedMembers = collection.members.filter(
|
||||||
return member.user.email !== e.user.email;
|
(member) => {
|
||||||
}
|
return member.user.email !== e.user.email;
|
||||||
);
|
}
|
||||||
setCollection({
|
);
|
||||||
...collection,
|
setCollection({
|
||||||
members: updatedMembers,
|
...collection,
|
||||||
});
|
members: updatedMembers,
|
||||||
}}
|
});
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ProfilePhoto src={`/api/avatar/${e.userId}`} />
|
<ProfilePhoto
|
||||||
|
src={`/api/avatar/${e.userId}`}
|
||||||
|
className="border-[3px]"
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-500">
|
<p className="text-sm font-bold text-sky-500">
|
||||||
{e.user.name}
|
{e.user.name}
|
||||||
|
@ -197,104 +213,161 @@ export default function TeamManagement({
|
||||||
<p className="text-sky-900">{e.user.email}</p>
|
<p className="text-sky-900">{e.user.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex sm:block items-center gap-5">
|
<div className="flex sm:block items-center gap-5 min-w-[10rem]">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-sm text-sky-500">
|
<p
|
||||||
|
className={`font-bold text-sm text-sky-500 ${
|
||||||
|
permissions === true ? "" : "mb-2"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
Permissions
|
Permissions
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 mb-2">
|
{permissions === true && (
|
||||||
(Click to toggle.)
|
<p className="text-xs text-gray-500 mb-2">
|
||||||
|
(Click to toggle.)
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{permissions !== true &&
|
||||||
|
!e.canCreate &&
|
||||||
|
!e.canUpdate &&
|
||||||
|
!e.canDelete ? (
|
||||||
|
<p className="text-sm text-gray-500">
|
||||||
|
Has no permissions.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
) : (
|
||||||
|
<div>
|
||||||
<div>
|
<label
|
||||||
<label className="cursor-pointer mr-1">
|
className={
|
||||||
<input
|
permissions === true
|
||||||
type="checkbox"
|
? "cursor-pointer mr-1"
|
||||||
id="canCreate"
|
: "mr-1"
|
||||||
className="peer sr-only"
|
}
|
||||||
checked={e.canCreate}
|
>
|
||||||
onChange={() => {
|
<input
|
||||||
const updatedMembers = collection.members.map(
|
type="checkbox"
|
||||||
(member) => {
|
id="canCreate"
|
||||||
if (member.user.email === e.user.email) {
|
className="peer sr-only"
|
||||||
return {
|
checked={e.canCreate}
|
||||||
...member,
|
onChange={() => {
|
||||||
canCreate: !e.canCreate,
|
if (permissions === true) {
|
||||||
};
|
const updatedMembers = collection.members.map(
|
||||||
}
|
(member) => {
|
||||||
return member;
|
if (member.user.email === e.user.email) {
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
canCreate: !e.canCreate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCollection({
|
||||||
|
...collection,
|
||||||
|
members: updatedMembers,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
}}
|
||||||
setCollection({
|
/>
|
||||||
...collection,
|
<span
|
||||||
members: updatedMembers,
|
className={`text-sky-900 peer-checked:bg-sky-500 text-sm ${
|
||||||
});
|
permissions === true
|
||||||
}}
|
? "hover:bg-slate-200 duration-75"
|
||||||
/>
|
: ""
|
||||||
<span className="text-sky-900 peer-checked:bg-sky-500 text-sm hover:bg-slate-200 duration-75 peer-checked:text-white rounded p-1 select-none">
|
} peer-checked:text-white rounded p-1 select-none`}
|
||||||
Create
|
>
|
||||||
</span>
|
Create
|
||||||
</label>
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label className="cursor-pointer mr-1">
|
<label
|
||||||
<input
|
className={
|
||||||
type="checkbox"
|
permissions === true
|
||||||
id="canUpdate"
|
? "cursor-pointer mr-1"
|
||||||
className="peer sr-only"
|
: "mr-1"
|
||||||
checked={e.canUpdate}
|
}
|
||||||
onChange={() => {
|
>
|
||||||
const updatedMembers = collection.members.map(
|
<input
|
||||||
(member) => {
|
type="checkbox"
|
||||||
if (member.user.email === e.user.email) {
|
id="canUpdate"
|
||||||
return {
|
className="peer sr-only"
|
||||||
...member,
|
checked={e.canUpdate}
|
||||||
canUpdate: !e.canUpdate,
|
onChange={() => {
|
||||||
};
|
if (permissions === true) {
|
||||||
}
|
const updatedMembers = collection.members.map(
|
||||||
return member;
|
(member) => {
|
||||||
|
if (member.user.email === e.user.email) {
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
canUpdate: !e.canUpdate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCollection({
|
||||||
|
...collection,
|
||||||
|
members: updatedMembers,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
}}
|
||||||
setCollection({
|
/>
|
||||||
...collection,
|
<span
|
||||||
members: updatedMembers,
|
className={`text-sky-900 peer-checked:bg-sky-500 text-sm ${
|
||||||
});
|
permissions === true
|
||||||
}}
|
? "hover:bg-slate-200 duration-75"
|
||||||
/>
|
: ""
|
||||||
<span className="text-sky-900 peer-checked:bg-sky-500 text-sm hover:bg-slate-200 duration-75 peer-checked:text-white rounded p-1 select-none">
|
} peer-checked:text-white rounded p-1 select-none`}
|
||||||
Update
|
>
|
||||||
</span>
|
Update
|
||||||
</label>
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label className="cursor-pointer mr-1">
|
<label
|
||||||
<input
|
className={
|
||||||
type="checkbox"
|
permissions === true
|
||||||
id="canDelete"
|
? "cursor-pointer mr-1"
|
||||||
className="peer sr-only"
|
: "mr-1"
|
||||||
checked={e.canDelete}
|
}
|
||||||
onChange={() => {
|
>
|
||||||
const updatedMembers = collection.members.map(
|
<input
|
||||||
(member) => {
|
type="checkbox"
|
||||||
if (member.user.email === e.user.email) {
|
id="canDelete"
|
||||||
return {
|
className="peer sr-only"
|
||||||
...member,
|
checked={e.canDelete}
|
||||||
canDelete: !e.canDelete,
|
onChange={() => {
|
||||||
};
|
if (permissions === true) {
|
||||||
}
|
const updatedMembers = collection.members.map(
|
||||||
return member;
|
(member) => {
|
||||||
|
if (member.user.email === e.user.email) {
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
canDelete: !e.canDelete,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setCollection({
|
||||||
|
...collection,
|
||||||
|
members: updatedMembers,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
}}
|
||||||
setCollection({
|
/>
|
||||||
...collection,
|
<span
|
||||||
members: updatedMembers,
|
className={`text-sky-900 peer-checked:bg-sky-500 text-sm ${
|
||||||
});
|
permissions === true
|
||||||
}}
|
? "hover:bg-slate-200 duration-75"
|
||||||
/>
|
: ""
|
||||||
<span className="text-sky-900 peer-checked:bg-sky-500 text-sm hover:bg-slate-200 duration-75 peer-checked:text-white rounded p-1 select-none">
|
} peer-checked:text-white rounded p-1 select-none`}
|
||||||
Delete
|
>
|
||||||
</span>
|
Delete
|
||||||
</label>
|
</span>
|
||||||
</div>
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -303,12 +376,14 @@ export default function TeamManagement({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SubmitButton
|
{permissions === true && (
|
||||||
onClick={submit}
|
<SubmitButton
|
||||||
label={method === "CREATE" ? "Add" : "Save"}
|
onClick={submit}
|
||||||
icon={method === "CREATE" ? faPlus : faPenToSquare}
|
label={method === "CREATE" ? "Add" : "Save"}
|
||||||
className="mx-auto mt-2"
|
icon={method === "CREATE" ? faPlus : faPenToSquare}
|
||||||
/>
|
className="mx-auto mt-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ type Props =
|
||||||
toggleCollectionModal: Function;
|
toggleCollectionModal: Function;
|
||||||
activeCollection: CollectionIncludingMembersAndLinkCount;
|
activeCollection: CollectionIncludingMembersAndLinkCount;
|
||||||
method: "UPDATE";
|
method: "UPDATE";
|
||||||
|
isOwner: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
defaultIndex?: number;
|
defaultIndex?: number;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +18,7 @@ type Props =
|
||||||
toggleCollectionModal: Function;
|
toggleCollectionModal: Function;
|
||||||
activeCollection?: CollectionIncludingMembersAndLinkCount;
|
activeCollection?: CollectionIncludingMembersAndLinkCount;
|
||||||
method: "CREATE";
|
method: "CREATE";
|
||||||
|
isOwner: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
defaultIndex?: number;
|
defaultIndex?: number;
|
||||||
};
|
};
|
||||||
|
@ -25,6 +27,7 @@ export default function CollectionModal({
|
||||||
className,
|
className,
|
||||||
defaultIndex,
|
defaultIndex,
|
||||||
toggleCollectionModal,
|
toggleCollectionModal,
|
||||||
|
isOwner,
|
||||||
activeCollection,
|
activeCollection,
|
||||||
method,
|
method,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
@ -48,6 +51,17 @@ export default function CollectionModal({
|
||||||
<Tab.List className="flex justify-center flex-col max-w-[15rem] sm:max-w-[30rem] mx-auto sm:flex-row gap-2 sm:gap-3 mb-5 text-sky-600">
|
<Tab.List className="flex justify-center flex-col max-w-[15rem] sm:max-w-[30rem] mx-auto sm:flex-row gap-2 sm:gap-3 mb-5 text-sky-600">
|
||||||
{method === "UPDATE" && (
|
{method === "UPDATE" && (
|
||||||
<>
|
<>
|
||||||
|
{isOwner && (
|
||||||
|
<Tab
|
||||||
|
className={({ selected }) =>
|
||||||
|
selected
|
||||||
|
? "px-2 py-1 bg-sky-200 duration-100 rounded-md outline-none"
|
||||||
|
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Collection Info
|
||||||
|
</Tab>
|
||||||
|
)}
|
||||||
<Tab
|
<Tab
|
||||||
className={({ selected }) =>
|
className={({ selected }) =>
|
||||||
selected
|
selected
|
||||||
|
@ -55,7 +69,7 @@ export default function CollectionModal({
|
||||||
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Collection Info
|
{isOwner ? "Share & Collaborate" : "View Team"}
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
className={({ selected }) =>
|
className={({ selected }) =>
|
||||||
|
@ -64,29 +78,22 @@ export default function CollectionModal({
|
||||||
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Share & Collaborate
|
{isOwner ? "Delete Collection" : "Leave Collection"}
|
||||||
</Tab>
|
|
||||||
<Tab
|
|
||||||
className={({ selected }) =>
|
|
||||||
selected
|
|
||||||
? "px-2 py-1 bg-sky-200 duration-100 rounded-md outline-none"
|
|
||||||
: "px-2 py-1 hover:bg-slate-200 rounded-md duration-100 outline-none"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Delete Collection
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels>
|
<Tab.Panels>
|
||||||
<Tab.Panel>
|
{(isOwner || method === "CREATE") && (
|
||||||
<CollectionInfo
|
<Tab.Panel>
|
||||||
toggleCollectionModal={toggleCollectionModal}
|
<CollectionInfo
|
||||||
setCollection={setCollection}
|
toggleCollectionModal={toggleCollectionModal}
|
||||||
collection={collection}
|
setCollection={setCollection}
|
||||||
method={method}
|
collection={collection}
|
||||||
/>
|
method={method}
|
||||||
</Tab.Panel>
|
/>
|
||||||
|
</Tab.Panel>
|
||||||
|
)}
|
||||||
|
|
||||||
{method === "UPDATE" && (
|
{method === "UPDATE" && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -77,10 +77,10 @@ export default function ProfileSettings({
|
||||||
<div className="grid sm:grid-cols-2 gap-3 auto-rows-auto">
|
<div className="grid sm:grid-cols-2 gap-3 auto-rows-auto">
|
||||||
<div className="sm:row-span-2 sm:justify-self-center mx-auto mb-3">
|
<div className="sm:row-span-2 sm:justify-self-center mx-auto mb-3">
|
||||||
<p className="text-sm text-sky-500 mb-2 text-center">Profile Photo</p>
|
<p className="text-sm text-sky-500 mb-2 text-center">Profile Photo</p>
|
||||||
<div className="w-28 h-28 flex items-center justify-center border border-sky-100 rounded-full relative">
|
<div className="w-28 h-28 flex items-center justify-center rounded-full relative">
|
||||||
<ProfilePhoto
|
<ProfilePhoto
|
||||||
src={user.profilePic}
|
src={user.profilePic}
|
||||||
className="h-auto aspect-square w-28 border-[1px]"
|
className="h-auto w-28"
|
||||||
status={handleProfileStatus}
|
status={handleProfileStatus}
|
||||||
/>
|
/>
|
||||||
{profileStatus && (
|
{profileStatus && (
|
||||||
|
|
|
@ -40,6 +40,7 @@ export default function ModalManagement() {
|
||||||
<CollectionModal
|
<CollectionModal
|
||||||
toggleCollectionModal={toggleModal}
|
toggleCollectionModal={toggleModal}
|
||||||
method={modal.method}
|
method={modal.method}
|
||||||
|
isOwner={modal.isOwner}
|
||||||
defaultIndex={modal.defaultIndex}
|
defaultIndex={modal.defaultIndex}
|
||||||
activeCollection={
|
activeCollection={
|
||||||
modal.active as CollectionIncludingMembersAndLinkCount
|
modal.active as CollectionIncludingMembersAndLinkCount
|
||||||
|
|
|
@ -69,7 +69,7 @@ export default function Navbar() {
|
||||||
>
|
>
|
||||||
<ProfilePhoto
|
<ProfilePhoto
|
||||||
src={account.profilePic}
|
src={account.profilePic}
|
||||||
className="sm:group-hover:h-8 sm:group-hover:w-8 duration-100"
|
className="sm:group-hover:h-8 sm:group-hover:w-8 duration-100 border-[3px]"
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
id="profile-dropdown"
|
id="profile-dropdown"
|
||||||
|
|
|
@ -33,7 +33,7 @@ export default function ProfilePhoto({
|
||||||
|
|
||||||
return error || !src ? (
|
return error || !src ? (
|
||||||
<div
|
<div
|
||||||
className={`bg-sky-500 text-white h-10 w-10 aspect-square shadow rounded-full border-[3px] border-slate-200 flex items-center justify-center ${className}`}
|
className={`bg-sky-500 text-white h-10 w-10 aspect-square shadow rounded-full border border-slate-200 flex items-center justify-center ${className}`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faUser} className="w-1/2 h-1/2 aspect-square" />
|
<FontAwesomeIcon icon={faUser} className="w-1/2 h-1/2 aspect-square" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,7 @@ export default function ProfilePhoto({
|
||||||
src={src}
|
src={src}
|
||||||
height={112}
|
height={112}
|
||||||
width={112}
|
width={112}
|
||||||
className={`h-10 w-10 shadow rounded-full aspect-square border-[3px] border-slate-200 ${className}`}
|
className={`h-10 w-10 shadow rounded-full aspect-square border border-slate-200 ${className}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import useAccountStore from "@/store/account";
|
import useAccountStore from "@/store/account";
|
||||||
import useCollectionStore from "@/store/collections";
|
import useCollectionStore from "@/store/collections";
|
||||||
import { CollectionIncludingMembersAndLinkCount, Member } from "@/types/global";
|
import { Member } from "@/types/global";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function usePermissions(collectionId: number) {
|
export default function usePermissions(collectionId: number) {
|
||||||
const { collections } = useCollectionStore();
|
const { collections } = useCollectionStore();
|
||||||
|
@ -26,7 +26,7 @@ export default function usePermissions(collectionId: number) {
|
||||||
|
|
||||||
setPermissions(account.id === collection.ownerId || getPermission);
|
setPermissions(account.id === collection.ownerId || getPermission);
|
||||||
}
|
}
|
||||||
}, [collections]);
|
}, [account, collections, collectionId]);
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,45 @@
|
||||||
import { prisma } from "@/lib/api/db";
|
import { prisma } from "@/lib/api/db";
|
||||||
import getPermission from "@/lib/api/getPermission";
|
import getPermission from "@/lib/api/getPermission";
|
||||||
|
import { UsersAndCollections } from "@prisma/client";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
export default async function deleteCollection(
|
export default async function deleteCollection(
|
||||||
collection: { id: number },
|
collection: { id: number },
|
||||||
userId: number
|
userId: number
|
||||||
) {
|
) {
|
||||||
if (!collection.id)
|
const collectionId = collection.id;
|
||||||
|
|
||||||
|
if (!collectionId)
|
||||||
return { response: "Please choose a valid collection.", status: 401 };
|
return { response: "Please choose a valid collection.", status: 401 };
|
||||||
|
|
||||||
const collectionIsAccessible = await getPermission(userId, collection.id);
|
const collectionIsAccessible = await getPermission(userId, collectionId);
|
||||||
|
|
||||||
if (!(collectionIsAccessible?.ownerId === userId))
|
const memberHasAccess = collectionIsAccessible?.members.some(
|
||||||
|
(e: UsersAndCollections) => e.userId === userId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (collectionIsAccessible?.ownerId !== userId && memberHasAccess) {
|
||||||
|
// Remove relation/Leave collection
|
||||||
|
const deletedUsersAndCollectionsRelation =
|
||||||
|
await prisma.usersAndCollections.delete({
|
||||||
|
where: {
|
||||||
|
userId_collectionId: {
|
||||||
|
userId: userId,
|
||||||
|
collectionId: collectionId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { response: deletedUsersAndCollectionsRelation, status: 200 };
|
||||||
|
} else if (collectionIsAccessible?.ownerId !== userId) {
|
||||||
return { response: "Collection is not accessible.", status: 401 };
|
return { response: "Collection is not accessible.", status: 401 };
|
||||||
|
}
|
||||||
|
|
||||||
const deletedCollection = await prisma.$transaction(async () => {
|
const deletedCollection = await prisma.$transaction(async () => {
|
||||||
await prisma.usersAndCollections.deleteMany({
|
await prisma.usersAndCollections.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
collection: {
|
collection: {
|
||||||
id: collection.id,
|
id: collectionId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -26,13 +47,13 @@ export default async function deleteCollection(
|
||||||
await prisma.link.deleteMany({
|
await prisma.link.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
collection: {
|
collection: {
|
||||||
id: collection.id,
|
id: collectionId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.rmdirSync(`data/archives/${collection.id}`, { recursive: true });
|
fs.rmdirSync(`data/archives/${collectionId}`, { recursive: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(
|
console.log(
|
||||||
"Collection's archive directory wasn't deleted most likely because it didn't exist..."
|
"Collection's archive directory wasn't deleted most likely because it didn't exist..."
|
||||||
|
@ -41,7 +62,7 @@ export default async function deleteCollection(
|
||||||
|
|
||||||
return await prisma.collection.delete({
|
return await prisma.collection.delete({
|
||||||
where: {
|
where: {
|
||||||
id: collection.id,
|
id: collectionId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,6 +17,7 @@ import ProfilePhoto from "@/components/ProfilePhoto";
|
||||||
import SortDropdown from "@/components/SortDropdown";
|
import SortDropdown from "@/components/SortDropdown";
|
||||||
import useModalStore from "@/store/modals";
|
import useModalStore from "@/store/modals";
|
||||||
import useLinks from "@/hooks/useLinks";
|
import useLinks from "@/hooks/useLinks";
|
||||||
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const { setModal } = useModalStore();
|
const { setModal } = useModalStore();
|
||||||
|
@ -35,6 +36,8 @@ export default function Index() {
|
||||||
const [activeCollection, setActiveCollection] =
|
const [activeCollection, setActiveCollection] =
|
||||||
useState<CollectionIncludingMembersAndLinkCount>();
|
useState<CollectionIncludingMembersAndLinkCount>();
|
||||||
|
|
||||||
|
const permissions = usePermissions(activeCollection?.id as number);
|
||||||
|
|
||||||
useLinks({ collectionId: Number(router.query.id), sort: sortBy });
|
useLinks({ collectionId: Number(router.query.id), sort: sortBy });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -71,13 +74,13 @@ export default function Index() {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
activeCollection &&
|
|
||||||
setModal({
|
setModal({
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: activeCollection,
|
active: activeCollection,
|
||||||
defaultIndex: 1,
|
defaultIndex: permissions === true ? 1 : 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="flex justify-center sm:justify-end items-center w-fit mx-auto sm:mr-0 sm:ml-auto group cursor-pointer"
|
className="flex justify-center sm:justify-end items-center w-fit mx-auto sm:mr-0 sm:ml-auto group cursor-pointer"
|
||||||
|
@ -87,10 +90,7 @@ export default function Index() {
|
||||||
activeCollection.members[0] && "mr-1"
|
activeCollection.members[0] && "mr-1"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{activeCollection.ownerId === data?.user.id
|
{permissions === true ? "Manage" : "View"} Team
|
||||||
? "Manage"
|
|
||||||
: "View"}{" "}
|
|
||||||
Team
|
|
||||||
</div>
|
</div>
|
||||||
{activeCollection?.members
|
{activeCollection?.members
|
||||||
.sort((a, b) => (a.userId as number) - (b.userId as number))
|
.sort((a, b) => (a.userId as number) - (b.userId as number))
|
||||||
|
@ -99,7 +99,7 @@ export default function Index() {
|
||||||
<ProfilePhoto
|
<ProfilePhoto
|
||||||
key={i}
|
key={i}
|
||||||
src={`/api/avatar/${e.userId}`}
|
src={`/api/avatar/${e.userId}`}
|
||||||
className="-mr-3 duration-100"
|
className="-mr-3 duration-100 border-[3px]"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -155,54 +155,68 @@ export default function Index() {
|
||||||
{expandDropdown ? (
|
{expandDropdown ? (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={[
|
items={[
|
||||||
|
permissions === true || permissions?.canCreate
|
||||||
|
? {
|
||||||
|
name: "Add Link Here",
|
||||||
|
onClick: () => {
|
||||||
|
setModal({
|
||||||
|
modal: "LINK",
|
||||||
|
state: true,
|
||||||
|
method: "CREATE",
|
||||||
|
});
|
||||||
|
setExpandDropdown(false);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
permissions === true
|
||||||
|
? {
|
||||||
|
name: "Edit Collection Info",
|
||||||
|
onClick: () => {
|
||||||
|
activeCollection &&
|
||||||
|
setModal({
|
||||||
|
modal: "COLLECTION",
|
||||||
|
state: true,
|
||||||
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
|
active: activeCollection,
|
||||||
|
});
|
||||||
|
setExpandDropdown(false);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
{
|
{
|
||||||
name: "Add Link Here",
|
name:
|
||||||
onClick: () => {
|
permissions === true
|
||||||
setModal({
|
? "Share/Collaborate"
|
||||||
modal: "LINK",
|
: "View Team",
|
||||||
state: true,
|
|
||||||
method: "CREATE",
|
|
||||||
});
|
|
||||||
setExpandDropdown(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Edit Collection Info",
|
|
||||||
onClick: () => {
|
|
||||||
activeCollection &&
|
|
||||||
setModal({
|
|
||||||
modal: "COLLECTION",
|
|
||||||
state: true,
|
|
||||||
method: "UPDATE",
|
|
||||||
active: activeCollection,
|
|
||||||
});
|
|
||||||
setExpandDropdown(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Share/Collaborate",
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
activeCollection &&
|
activeCollection &&
|
||||||
setModal({
|
setModal({
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: activeCollection,
|
active: activeCollection,
|
||||||
defaultIndex: 1,
|
defaultIndex: 1,
|
||||||
});
|
});
|
||||||
setExpandDropdown(false);
|
setExpandDropdown(false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "Delete Collection",
|
name:
|
||||||
|
permissions === true
|
||||||
|
? "Delete Collection"
|
||||||
|
: "Leave Collection",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
activeCollection &&
|
activeCollection &&
|
||||||
setModal({
|
setModal({
|
||||||
modal: "COLLECTION",
|
modal: "COLLECTION",
|
||||||
state: true,
|
state: true,
|
||||||
method: "UPDATE",
|
method: "UPDATE",
|
||||||
|
isOwner: permissions === true,
|
||||||
active: activeCollection,
|
active: activeCollection,
|
||||||
defaultIndex: 2,
|
defaultIndex: permissions === true ? 2 : 1,
|
||||||
});
|
});
|
||||||
setExpandDropdown(false);
|
setExpandDropdown(false);
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,6 +30,7 @@ type Modal =
|
||||||
modal: "COLLECTION";
|
modal: "COLLECTION";
|
||||||
state: boolean;
|
state: boolean;
|
||||||
method: "UPDATE";
|
method: "UPDATE";
|
||||||
|
isOwner: boolean;
|
||||||
active: CollectionIncludingMembersAndLinkCount;
|
active: CollectionIncludingMembersAndLinkCount;
|
||||||
defaultIndex?: number;
|
defaultIndex?: number;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +38,7 @@ type Modal =
|
||||||
modal: "COLLECTION";
|
modal: "COLLECTION";
|
||||||
state: boolean;
|
state: boolean;
|
||||||
method: "CREATE";
|
method: "CREATE";
|
||||||
|
isOwner: boolean;
|
||||||
active?: CollectionIncludingMembersAndLinkCount;
|
active?: CollectionIncludingMembersAndLinkCount;
|
||||||
defaultIndex?: number;
|
defaultIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
Ŝarĝante…
Reference in New Issue