2023-04-23 08:26:39 -05:00
// Copyright (C) 2022-present Daniel31x13 <daniel31x13@gmail.com>
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
2023-04-07 20:10:55 -05:00
import Dropdown from "@/components/Dropdown" ;
2023-03-05 15:03:20 -06:00
import LinkList from "@/components/LinkList" ;
2023-04-25 07:39:46 -05:00
import Modal from "@/components/Modal" ;
import AddLink from "@/components/Modal/AddLink" ;
2023-05-26 23:29:45 -05:00
import CollectionModal from "@/components/Modal/CollectionModal" ;
2023-04-28 16:10:29 -05:00
import DeleteCollection from "@/components/Modal/DeleteCollection" ;
2023-04-07 20:10:55 -05:00
import useCollectionStore from "@/store/collections" ;
2023-03-22 18:11:54 -05:00
import useLinkStore from "@/store/links" ;
2023-05-26 23:11:29 -05:00
import { CollectionIncludingMembers } from "@/types/global" ;
2023-04-07 20:10:55 -05:00
import {
faAdd ,
faEllipsis ,
faFolder ,
faPenToSquare ,
2023-05-15 15:45:06 -05:00
faSort ,
2023-04-07 20:10:55 -05:00
faTrashCan ,
2023-05-25 13:44:08 -05:00
faUser ,
2023-04-07 20:10:55 -05:00
} from "@fortawesome/free-solid-svg-icons" ;
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
2023-02-24 11:32:28 -06:00
import { useRouter } from "next/router" ;
2023-05-15 15:45:06 -05:00
import { ChangeEvent , useEffect , useState } from "react" ;
2023-05-14 10:41:08 -05:00
import MainLayout from "@/layouts/MainLayout" ;
2023-05-15 15:45:06 -05:00
import RadioButton from "@/components/RadioButton" ;
import ClickAwayHandler from "@/components/ClickAwayHandler" ;
2023-05-25 13:44:08 -05:00
import ImageWithFallback from "@/components/ImageWithFallback" ;
2023-05-26 14:52:18 -05:00
import { useSession } from "next-auth/react" ;
2023-02-24 11:32:28 -06:00
export default function ( ) {
const router = useRouter ( ) ;
2023-04-07 20:10:55 -05:00
2023-03-22 18:11:54 -05:00
const { links } = useLinkStore ( ) ;
2023-04-07 20:10:55 -05:00
const { collections } = useCollectionStore ( ) ;
2023-02-24 11:32:28 -06:00
2023-05-26 14:52:18 -05:00
const { data } = useSession ( ) ;
2023-04-23 08:26:39 -05:00
const [ expandDropdown , setExpandDropdown ] = useState ( false ) ;
2023-04-25 07:39:46 -05:00
const [ linkModal , setLinkModal ] = useState ( false ) ;
2023-04-28 16:10:29 -05:00
const [ editCollectionModal , setEditCollectionModal ] = useState ( false ) ;
const [ deleteCollectionModal , setDeleteCollectionModal ] = useState ( false ) ;
2023-05-15 15:45:06 -05:00
const [ sortDropdown , setSortDropdown ] = useState ( false ) ;
const [ sortBy , setSortBy ] = useState ( "Name (A-Z)" ) ;
2023-04-27 18:13:21 -05:00
const [ activeCollection , setActiveCollection ] =
2023-05-26 23:11:29 -05:00
useState < CollectionIncludingMembers > ( ) ;
2023-05-15 15:45:06 -05:00
const [ sortedLinks , setSortedLinks ] = useState ( links ) ;
2023-04-07 20:10:55 -05:00
2023-04-25 07:39:46 -05:00
const toggleLinkModal = ( ) = > {
setLinkModal ( ! linkModal ) ;
} ;
2023-04-28 16:10:29 -05:00
const toggleEditCollectionModal = ( ) = > {
setEditCollectionModal ( ! editCollectionModal ) ;
} ;
const toggleDeleteCollectionModal = ( ) = > {
setDeleteCollectionModal ( ! deleteCollectionModal ) ;
2023-04-27 18:13:21 -05:00
} ;
2023-05-15 15:45:06 -05:00
const handleSortChange = ( event : ChangeEvent < HTMLInputElement > ) = > {
setSortBy ( event . target . value ) ;
} ;
2023-04-07 20:10:55 -05:00
2023-05-15 15:45:06 -05:00
useEffect ( ( ) = > {
2023-04-07 20:10:55 -05:00
setActiveCollection (
collections . find ( ( e ) = > e . id === Number ( router . query . id ) )
) ;
2023-05-15 15:45:06 -05:00
// Sorting logic
const linksArray = [
. . . links . filter ( ( e ) = > e . collection . id === Number ( router . query . id ) ) ,
] ;
if ( sortBy === "Name (A-Z)" )
setSortedLinks ( linksArray . sort ( ( a , b ) = > a . name . localeCompare ( b . name ) ) ) ;
else if ( sortBy === "Title (A-Z)" )
setSortedLinks ( linksArray . sort ( ( a , b ) = > a . title . localeCompare ( b . title ) ) ) ;
else if ( sortBy === "Name (Z-A)" )
setSortedLinks ( linksArray . sort ( ( a , b ) = > b . name . localeCompare ( a . name ) ) ) ;
else if ( sortBy === "Title (Z-A)" )
setSortedLinks ( linksArray . sort ( ( a , b ) = > b . title . localeCompare ( a . title ) ) ) ;
else if ( sortBy === "Date (Newest First)" )
setSortedLinks (
linksArray . sort (
( a , b ) = >
new Date ( b . createdAt ) . getTime ( ) - new Date ( a . createdAt ) . getTime ( )
)
) ;
else if ( sortBy === "Date (Oldest First)" )
setSortedLinks (
linksArray . sort (
( a , b ) = >
new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( )
)
) ;
} , [ links , router , collections , sortBy ] ) ;
2023-02-24 11:32:28 -06:00
2023-03-05 15:03:20 -06:00
return (
2023-05-14 10:41:08 -05:00
< MainLayout >
2023-04-30 15:54:40 -05:00
< div className = "p-5 flex flex-col gap-5 w-full" >
2023-05-25 13:44:08 -05:00
< div className = "bg-gradient-to-tr from-sky-100 from-10% via-gray-100 via-20% rounded shadow min-h-[10rem] p-5 flex gap-5 flex-col justify-between" >
2023-05-26 14:52:18 -05:00
< div className = "flex flex-col sm:flex-row gap-3 justify-between items-center sm:items-start" >
2023-05-25 13:44:08 -05:00
< div className = "flex gap-3 items-center" >
< div className = "flex gap-2" >
2023-05-15 15:45:06 -05:00
< FontAwesomeIcon
2023-05-25 13:44:08 -05:00
icon = { faFolder }
className = "sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300"
2023-05-15 15:45:06 -05:00
/ >
2023-05-25 13:44:08 -05:00
< p className = "sm:text-4xl text-3xl text-sky-900 capitalize" >
{ activeCollection ? . name }
< / p >
2023-05-15 15:45:06 -05:00
< / div >
< / div >
2023-05-25 13:45:54 -05:00
{ activeCollection ? . members [ 0 ] ? (
< div >
2023-05-26 14:52:18 -05:00
< div className = "text-sky-400 flex justify-end items-center w-60 mr-3" >
< div className = "mr-1 bg-sky-500 p-2 leading-3 select-none cursor-pointer hover:bg-sky-400 duration-100 text-white rounded-full text-xs" >
{ activeCollection . ownerId === data ? . user . id
? "Manage"
: "View" } { " " }
Team
2023-05-25 13:44:08 -05:00
< / div >
2023-05-25 13:45:54 -05:00
{ activeCollection ? . members
2023-05-26 23:11:29 -05:00
. sort (
( a , b ) = > ( a . user . id as number ) - ( b . user . id as number )
)
2023-05-25 13:45:54 -05:00
. map ( ( e , i ) = > {
return (
< ImageWithFallback
key = { i }
src = { ` /api/avatar/ ${ e . userId } ` }
className = "h-10 w-10 shadow rounded-full border-[3px] border-sky-100 -mr-3"
>
< div className = "text-white bg-sky-500 h-10 w-10 shadow rounded-full border-[3px] border-sky-100 -mr-3 flex items-center justify-center" >
< FontAwesomeIcon
icon = { faUser }
className = "w-5 h-5"
/ >
< / div >
< / ImageWithFallback >
) ;
} )
. slice ( 0 , 4 ) }
{ activeCollection ? . members . length &&
activeCollection . members . length - 4 > 0 ? (
< div className = "h-10 w-10 text-white flex items-center justify-center rounded-full border-[3px] bg-sky-500 border-sky-100 -mr-3" >
2023-05-26 14:52:18 -05:00
+ { activeCollection ? . members ? . length - 4 }
2023-05-25 13:45:54 -05:00
< / div >
) : null }
< / div >
2023-05-25 13:44:08 -05:00
< / div >
2023-05-25 13:45:54 -05:00
) : null }
2023-05-25 13:44:08 -05:00
< / div >
2023-05-15 15:45:06 -05:00
2023-05-25 13:44:08 -05:00
< div className = "text-gray-500 flex justify-between items-end gap-5" >
< p > { activeCollection ? . description } < / p >
< div className = "flex items-center gap-2" >
< div className = "relative" >
< div
onClick = { ( ) = > setSortDropdown ( ! sortDropdown ) }
id = "sort-dropdown"
className = "inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1"
>
< FontAwesomeIcon
icon = { faSort }
id = "sort-dropdown"
className = "w-5 h-5 text-gray-500"
2023-05-15 15:45:06 -05:00
/ >
2023-05-25 13:44:08 -05:00
< / div >
2023-04-25 07:39:46 -05:00
2023-05-25 13:44:08 -05:00
{ sortDropdown ? (
< ClickAwayHandler
onClickOutside = { ( e : Event ) = > {
const target = e . target as HTMLInputElement ;
if ( target . id !== "sort-dropdown" ) setSortDropdown ( false ) ;
} }
className = "absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48"
>
< p className = "mb-2 text-sky-900 text-center font-semibold" >
Sort by
< / p >
< div className = "flex flex-col gap-2" >
< RadioButton
label = "Name (A-Z)"
state = { sortBy === "Name (A-Z)" }
onClick = { handleSortChange }
/ >
2023-04-27 18:13:21 -05:00
2023-05-25 13:44:08 -05:00
< RadioButton
label = "Name (Z-A)"
state = { sortBy === "Name (Z-A)" }
onClick = { handleSortChange }
/ >
2023-04-28 16:10:29 -05:00
2023-05-25 13:44:08 -05:00
< RadioButton
label = "Title (A-Z)"
state = { sortBy === "Title (A-Z)" }
onClick = { handleSortChange }
/ >
2023-05-15 15:45:06 -05:00
2023-05-25 13:44:08 -05:00
< RadioButton
label = "Title (Z-A)"
state = { sortBy === "Title (Z-A)" }
onClick = { handleSortChange }
/ >
< RadioButton
label = "Date (Newest First)"
state = { sortBy === "Date (Newest First)" }
onClick = { handleSortChange }
/ >
2023-05-15 15:45:06 -05:00
2023-05-25 13:44:08 -05:00
< RadioButton
label = "Date (Oldest First)"
state = { sortBy === "Date (Oldest First)" }
onClick = { handleSortChange }
/ >
< / div >
< / ClickAwayHandler >
) : null }
< / div >
< div className = "relative" >
< div
onClick = { ( ) = > setExpandDropdown ( ! expandDropdown ) }
id = "edit-dropdown"
className = "inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1"
>
< FontAwesomeIcon
icon = { faEllipsis }
id = "edit-dropdown"
title = "More"
className = "w-5 h-5 text-gray-500"
2023-05-15 15:45:06 -05:00
/ >
< / div >
2023-05-25 13:44:08 -05:00
{ expandDropdown ? (
< Dropdown
items = { [
{
name : "Add Link Here" ,
icon : < FontAwesomeIcon icon = { faAdd } / > ,
onClick : ( ) = > {
toggleLinkModal ( ) ;
setExpandDropdown ( false ) ;
} ,
} ,
{
name : "Edit Collection" ,
icon : < FontAwesomeIcon icon = { faPenToSquare } / > ,
onClick : ( ) = > {
toggleEditCollectionModal ( ) ;
setExpandDropdown ( false ) ;
} ,
} ,
{
name : "Delete Collection" ,
icon : < FontAwesomeIcon icon = { faTrashCan } / > ,
onClick : ( ) = > {
toggleDeleteCollectionModal ( ) ;
setExpandDropdown ( false ) ;
} ,
} ,
] }
onClickOutside = { ( e : Event ) = > {
const target = e . target as HTMLInputElement ;
if ( target . id !== "edit-dropdown" )
setExpandDropdown ( false ) ;
} }
className = "absolute top-8 right-0 z-10 w-44"
/ >
) : null }
{ linkModal ? (
< Modal toggleModal = { toggleLinkModal } >
< AddLink toggleLinkModal = { toggleLinkModal } / >
< / Modal >
) : null }
{ editCollectionModal && activeCollection ? (
< Modal toggleModal = { toggleEditCollectionModal } >
2023-05-26 23:29:45 -05:00
< CollectionModal
2023-05-25 13:44:08 -05:00
toggleCollectionModal = { toggleEditCollectionModal }
2023-05-26 23:11:29 -05:00
activeCollection = { activeCollection }
2023-05-26 23:29:45 -05:00
method = "UPDATE"
2023-05-25 13:44:08 -05:00
/ >
< / Modal >
) : null }
{ deleteCollectionModal && activeCollection ? (
< Modal toggleModal = { toggleDeleteCollectionModal } >
< DeleteCollection
collection = { activeCollection }
toggleDeleteCollectionModal = { toggleDeleteCollectionModal }
/ >
< / Modal >
) : null }
< / div >
< / div >
2023-04-30 15:54:40 -05:00
< / div >
2023-04-07 20:10:55 -05:00
< / div >
2023-05-25 09:17:20 -05:00
< div className = "grid 2xl:grid-cols-3 xl:grid-cols-2 gap-5" >
{ sortedLinks . map ( ( e , i ) = > {
return < LinkList key = { i } link = { e } count = { i } / > ;
} ) }
< / div >
2023-04-07 20:10:55 -05:00
< / div >
2023-05-14 10:41:08 -05:00
< / MainLayout >
2023-03-05 15:03:20 -06:00
) ;
2023-02-24 11:32:28 -06:00
}