mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-11 12:23:20 +00:00
top right buttons styling fixes and tooltip (profile dropdown/activity/settings)
This commit is contained in:
1 parent
a1aacad7dd
commit
9d6b31200e
3 files changed
+142
-174
No files matched your search
@@ -1,12 +1,12 @@
|
||||
import { faGear, faImages } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Activity } from "~/components/ui/Activity/Activity";
|
||||
|
||||
import { GalleryModal } from "@storyteller/ui-gallery-modal";
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { SettingsModal } from "@storyteller/ui-settings-modal";
|
||||
import { useState } from "react";
|
||||
import { AuthButtons } from "~/components/shared_authentication/AuthButtons";
|
||||
import { Tooltip } from "@storyteller/ui-tooltip";
|
||||
|
||||
export const ToolbarTopRight = () => {
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
@@ -18,11 +18,13 @@ export const ToolbarTopRight = () => {
|
||||
<div
|
||||
className={twMerge("relative z-50 flex h-fit w-fit items-center gap-2")}
|
||||
>
|
||||
<Button
|
||||
icon={faGear}
|
||||
className="h-[42px] w-[42px] bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90"
|
||||
onClick={() => setIsSettingsModalOpen(true)}
|
||||
/>
|
||||
<Tooltip content="Settings" position="bottom">
|
||||
<Button
|
||||
icon={faGear}
|
||||
className="h-[42px] w-[42px] bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90"
|
||||
onClick={() => setIsSettingsModalOpen(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button
|
||||
icon={faImages}
|
||||
className="h-[42px] bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90"
|
||||
@@ -31,7 +33,7 @@ export const ToolbarTopRight = () => {
|
||||
My Gallery
|
||||
</Button>
|
||||
<Activity />
|
||||
<AuthButtons/>
|
||||
<AuthButtons />
|
||||
</div>
|
||||
<SettingsModal
|
||||
isOpen={isSettingsModalOpen}
|
||||
|
||||
+70
-66
@@ -1,5 +1,4 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Fragment } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faChevronDown,
|
||||
@@ -8,10 +7,7 @@ import {
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
|
||||
import { Gravatar } from "../../ui/Gravatar";
|
||||
|
||||
import { authentication } from "~/signals";
|
||||
|
||||
// style constants
|
||||
import {
|
||||
paperWrapperStyles,
|
||||
transitionTimingStyles,
|
||||
@@ -35,71 +31,79 @@ export function ProfileDropdown() {
|
||||
|
||||
return (
|
||||
<Menu as="div" className="relative">
|
||||
<MenuButton
|
||||
className={twMerge(
|
||||
"flex cursor-pointer items-center gap-1.5",
|
||||
"data-[hover]:opacity-70",
|
||||
)}
|
||||
>
|
||||
<Gravatar
|
||||
size={34}
|
||||
username={userInfo.value.display_name}
|
||||
email_hash={userInfo.value.email_gravatar_hash}
|
||||
avatarIndex={
|
||||
Number(userInfo.value.core_info.default_avatar.image_index) || 0
|
||||
}
|
||||
backgroundIndex={
|
||||
Number(userInfo.value.core_info.default_avatar.color_index) || 0
|
||||
}
|
||||
/>
|
||||
<FontAwesomeIcon icon={faChevronDown} />
|
||||
</MenuButton>
|
||||
|
||||
<MenuItems
|
||||
anchor="bottom end"
|
||||
transition
|
||||
className={twMerge(
|
||||
paperWrapperStyles,
|
||||
"mt-4 flex w-48 flex-col rounded-xl shadow-lg",
|
||||
transitionTimingStyles,
|
||||
"data-[closed]:scale-95 data-[closed]:opacity-0",
|
||||
)}
|
||||
>
|
||||
<MenuItem
|
||||
as="button"
|
||||
href={profileUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-3 rounded-lg px-3 py-2",
|
||||
"text-md font-medium text-white hover:bg-gray-100 hover:text-gray-700",
|
||||
"transition-colors duration-150",
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUser} className="w-4" />
|
||||
My Profile
|
||||
</MenuItem>
|
||||
|
||||
{menuOptions.map((option, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
as="button"
|
||||
{({ close }) => (
|
||||
<>
|
||||
<MenuButton
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-3 rounded-lg px-3 py-2",
|
||||
"text-md font-medium text-white hover:bg-gray-100 hover:text-gray-700",
|
||||
"transition-colors duration-150",
|
||||
"flex cursor-pointer items-center gap-1.5",
|
||||
"data-[hover]:opacity-70",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
option.onClick();
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={option.icon} className="w-4" />
|
||||
<span>{option.label}</span>
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuItems>
|
||||
<Gravatar
|
||||
size={34}
|
||||
username={userInfo.value!.display_name}
|
||||
email_hash={userInfo.value!.email_gravatar_hash}
|
||||
avatarIndex={
|
||||
Number(userInfo.value!.core_info.default_avatar.image_index) ||
|
||||
0
|
||||
}
|
||||
backgroundIndex={
|
||||
Number(userInfo.value!.core_info.default_avatar.color_index) ||
|
||||
0
|
||||
}
|
||||
/>
|
||||
<FontAwesomeIcon icon={faChevronDown} />
|
||||
</MenuButton>
|
||||
|
||||
<MenuItems
|
||||
anchor="bottom end"
|
||||
transition
|
||||
className={twMerge(
|
||||
paperWrapperStyles,
|
||||
"mt-4 flex w-40 flex-col rounded-lg shadow-lg",
|
||||
transitionTimingStyles,
|
||||
"data-[closed]:scale-95 data-[closed]:opacity-0",
|
||||
"focus:outline-none focus:ring-0",
|
||||
)}
|
||||
>
|
||||
<MenuItem
|
||||
as="a"
|
||||
href={profileUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2",
|
||||
"text-sm font-medium !text-white hover:bg-[#63636B]/60",
|
||||
"transition-colors duration-150",
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUser} className="w-4" />
|
||||
My Profile
|
||||
</MenuItem>
|
||||
|
||||
{menuOptions.map((option, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
as="button"
|
||||
className={twMerge(
|
||||
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2",
|
||||
"text-sm font-medium text-white hover:bg-[#63636B]/60",
|
||||
"transition-colors duration-150",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
option.onClick();
|
||||
close();
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={option.icon} className="w-4" />
|
||||
<span>{option.label}</span>
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuItems>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { InProgressCard } from "./InProgressCard";
|
||||
import { useJobContext } from "~/components/JobContext";
|
||||
import { Api } from "~/KonvaApp/Api";
|
||||
import { toast } from "sonner";
|
||||
import { Tooltip } from "@storyteller/ui-tooltip";
|
||||
|
||||
// Dummy data types
|
||||
interface CompletedItem {
|
||||
@@ -27,48 +28,8 @@ interface ActiveJob {
|
||||
};
|
||||
}
|
||||
|
||||
// Dummy data for testing
|
||||
const dummyCompletedItems: CompletedItem[] = [
|
||||
{
|
||||
token: "1",
|
||||
maybe_title: "Completed Item 1",
|
||||
public_bucket_path: "/test1",
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
token: "2",
|
||||
maybe_title: "Completed Item 2",
|
||||
public_bucket_path: "/test2",
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
// const dummyJobs: ActiveJob[] = [
|
||||
// {
|
||||
// job_token: "job1",
|
||||
// request: {
|
||||
// maybe_model_title: "In Progress Job 1",
|
||||
// },
|
||||
// status: {
|
||||
// status: "STARTED",
|
||||
// progress_percentage: 45,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// job_token: "job2",
|
||||
// request: {
|
||||
// maybe_model_title: "In Progress Job 2",
|
||||
// },
|
||||
// status: {
|
||||
// status: "PENDING",
|
||||
// progress_percentage: 0,
|
||||
// },
|
||||
// },
|
||||
// ];
|
||||
|
||||
export function Activity() {
|
||||
const { jobTokens, addJobToken, removeJobToken, clearJobTokens } =
|
||||
useJobContext();
|
||||
const { jobTokens, removeJobToken } = useJobContext();
|
||||
const [completedItems, setCompletedItems] = useState<CompletedItem[]>([]);
|
||||
const [jobs, setJobs] = useState<ActiveJob[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -150,68 +111,69 @@ export function Activity() {
|
||||
}, [jobTokens, removeJobToken]);
|
||||
|
||||
return (
|
||||
<PopoverMenu
|
||||
mode="default"
|
||||
buttonClassName="h-[42px] px-4"
|
||||
panelClassName="w-[360px] p-2"
|
||||
position="bottom"
|
||||
align="end"
|
||||
triggerIcon={
|
||||
<div>
|
||||
<span className="text-md flex items-center gap-2">
|
||||
<>
|
||||
{jobs.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinnerThird}
|
||||
className="animate-spin"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faBell} />
|
||||
)}
|
||||
Activity
|
||||
</>
|
||||
</span>
|
||||
<Tooltip content="Activity" position="bottom">
|
||||
<PopoverMenu
|
||||
mode="default"
|
||||
buttonClassName="h-[42px] w-[42px] px-4"
|
||||
panelClassName="w-[360px] p-2"
|
||||
position="bottom"
|
||||
align="end"
|
||||
triggerIcon={
|
||||
<div>
|
||||
<span className="text-md flex items-center gap-2">
|
||||
<>
|
||||
{jobs.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinnerThird}
|
||||
className="animate-spin"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faBell} />
|
||||
)}
|
||||
</>
|
||||
</span>
|
||||
{jobs.length > 0 && (
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 animate-pulse rounded-full bg-blue-500" />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="max-h-[480px] overflow-y-auto">
|
||||
{/* In progress */}
|
||||
{jobs.length > 0 && (
|
||||
<div className="absolute -right-1 -top-1 h-3 w-3 animate-pulse rounded-full bg-blue-500" />
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="max-h-[480px] overflow-y-auto">
|
||||
{/* In progress */}
|
||||
{jobs.length > 0 && (
|
||||
<div>
|
||||
{jobs.map((job) => (
|
||||
<InProgressCard key={job.job_token} job={job} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="flex h-48 w-full flex-col justify-center gap-4 p-4 text-center align-middle">
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinnerThird}
|
||||
spin
|
||||
size="2x"
|
||||
className="text-gray-400"
|
||||
/>
|
||||
<h3 className="text-gray-300">Retrieving Activities</h3>
|
||||
</div>
|
||||
) : completedItems.length === 0 && jobs.length === 0 ? (
|
||||
<div className="flex h-48 w-full flex-col justify-center gap-4 p-4 text-center align-middle">
|
||||
<h3 className="text-gray-300">No activities yet</h3>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{/* Completed */}
|
||||
<div className="flex flex-col">
|
||||
{completedItems.map((item) => (
|
||||
<CompletedCard key={item.token} job={item} />
|
||||
<div>
|
||||
{jobs.map((job) => (
|
||||
<InProgressCard key={job.job_token} job={job} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverMenu>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="flex h-48 w-full flex-col justify-center gap-4 p-4 text-center align-middle">
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinnerThird}
|
||||
spin
|
||||
size="2x"
|
||||
className="text-gray-400"
|
||||
/>
|
||||
<h3 className="text-gray-300">Retrieving Activities</h3>
|
||||
</div>
|
||||
) : completedItems.length === 0 && jobs.length === 0 ? (
|
||||
<div className="flex h-48 w-full flex-col justify-center gap-4 p-4 text-center align-middle">
|
||||
<h3 className="text-gray-300">No activities yet</h3>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{/* Completed */}
|
||||
<div className="flex flex-col">
|
||||
{completedItems.map((item) => (
|
||||
<CompletedCard key={item.token} job={item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopoverMenu>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user