feat(frontend): new design for brain table (#2193)

# Description

Please include a summary of the changes and the related issue. Please
also include relevant motivation and context.

## Checklist before requesting a review

Please delete options that are not relevant.

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented hard-to-understand areas
- [ ] I have ideally added tests that prove my fix is effective or that
my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged

## Screenshots (if appropriate):
This commit is contained in:
Antoine Dewez 2024-02-14 20:08:58 -08:00 committed by GitHub
parent ba5ef60362
commit ff7393e441
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 106 additions and 22 deletions

View File

@ -1,20 +1,25 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
.brain_item_wrapper {
padding-inline: Spacings.$spacing05;
border-top: 1px solid Colors.$primary-lightest;
overflow: hidden;
display: flex;
gap: Spacings.$spacing02;
justify-content: space-between;
align-items: center;
cursor: pointer;
margin-block: Spacings.$spacing03;
border: 1px solid Colors.$lightest-black;
border-radius: Radius.$normal;
padding-block: Spacings.$spacing03;
&:hover {
background-color: Colors.$lightest-black;
border-color: Colors.$primary;
background-color: Colors.$primary-lightest;
}
.brain_info_wrapper {
@ -28,6 +33,7 @@
.name {
@include Typography.EllipsisOverflow;
width: 200px;
color: Colors.$black;
}
.description {
@ -46,4 +52,32 @@
}
}
}
.options_menu {
position: absolute;
background-color: Colors.$highlight;
border-radius: Radius.$normal;
right: Spacings.$spacing07;
border-radius: Radius.$normal;
box-shadow: 0 1px 2px rgb(0, 0, 0, 0.25);
.option {
padding: Spacings.$spacing03;
padding-inline: Spacings.$spacing05;
display: flex;
gap: Spacings.$spacing05;
align-items: center;
cursor: pointer;
justify-content: space-between;
overflow: hidden;
&:not(:first-child) {
border-top: 1px solid Colors.$light-grey;
}
&:hover {
background-color: Colors.$primary-lightest;
}
}
}
}

View File

@ -1,5 +1,5 @@
import Link from "next/link";
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import { DeleteOrUnsubscribeConfirmationModal } from "@/app/studio/[brainId]/components/BrainManagementTabs/components/Modals/DeleteOrUnsubscribeConfirmationModal";
import { useBrainManagementTabs } from "@/app/studio/[brainId]/components/BrainManagementTabs/hooks/useBrainManagementTabs";
@ -16,13 +16,15 @@ type BrainItemProps = {
};
export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
const [optionsOpened, setOptionsOpened] = useState<boolean>(false);
const [deleteHovered, setDeleteHovered] = useState<boolean>(false);
const [editHovered, setEditHovered] = useState<boolean>(false);
const {
handleUnsubscribeOrDeleteBrain,
isDeleteOrUnsubscribeModalOpened,
setIsDeleteOrUnsubscribeModalOpened,
isDeleteOrUnsubscribeRequestPending,
} = useBrainManagementTabs(brain.id);
const [isHovered, setIsHovered] = useState<boolean>(false);
const { allBrains } = useBrainContext();
@ -31,33 +33,79 @@ export const BrainItem = ({ brain, even }: BrainItemProps): JSX.Element => {
userAccessibleBrains: allBrains,
});
const optionsRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
optionsRef.current &&
!optionsRef.current.contains(event.target as Node)
) {
setOptionsOpened(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
return (
<div
className={`
${even ? styles.even : styles.odd}
${styles.brain_item_wrapper}
`}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<Link className={styles.brain_info_wrapper} href={`/studio/${brain.id}`}>
<span className={styles.name}>{brain.name}</span>
<span className={styles.description}>{brain.description}</span>
</Link>
<Icon
name="edit"
size="normal"
color="black"
hovered={isHovered}
onClick={() => (window.location.href = `/studio/${brain.id}`)}
/>
<Icon
name="delete"
size="normal"
color="dangerous"
handleHover={true}
onClick={() => setIsDeleteOrUnsubscribeModalOpened(true)}
/>
<div>
<div
onClick={(event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
setOptionsOpened(!optionsOpened);
}}
>
<Icon name="options" size="normal" color="black" handleHover={true} />
</div>
{optionsOpened && (
<div className={styles.options_menu} ref={optionsRef}>
<div
className={styles.option}
onClick={() => setIsDeleteOrUnsubscribeModalOpened(true)}
onMouseEnter={() => setDeleteHovered(true)}
onMouseLeave={() => setDeleteHovered(false)}
>
<span>Delete</span>
<Icon
name="delete"
size="normal"
color="dangerous"
hovered={deleteHovered}
/>
</div>
<div
className={styles.option}
onClick={() => (window.location.href = `/studio/${brain.id}`)}
onMouseEnter={() => setEditHovered(true)}
onMouseLeave={() => setEditHovered(false)}
>
<span>Edit</span>
<Icon
name="edit"
size="normal"
color="black"
hovered={editHovered}
/>
</div>
</div>
)}
</div>
<DeleteOrUnsubscribeConfirmationModal
isOpen={isDeleteOrUnsubscribeModalOpened}
setOpen={setIsDeleteOrUnsubscribeModalOpened}

View File

@ -7,7 +7,6 @@
.brains_wrapper {
display: flex;
flex-direction: column;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
border-radius: Radius.$big;
overflow: hidden;
width: 100%;
@ -18,7 +17,8 @@
display: flex;
padding: Spacings.$spacing05;
gap: Spacings.$spacing05;
background-color: Colors.$lightest-grey;
background-color: Colors.$highlight;
margin-bottom: Spacings.$spacing03;
.name {
width: 200px;

View File

@ -39,6 +39,7 @@ import {
MdUploadFile,
} from "react-icons/md";
import { RiHashtag } from "react-icons/ri";
import { SlOptions } from "react-icons/sl";
import { TbNetwork } from "react-icons/tb";
import { VscGraph } from "react-icons/vsc";
@ -70,6 +71,7 @@ export const iconList: { [name: string]: IconType } = {
key: FaKey,
loader: AiOutlineLoading3Quarters,
logout: IoMdLogOut,
options: SlOptions,
redirection: BsArrowRightShort,
search: LuSearch,
settings: IoSettingsSharp,