forked from github/plane
Style/workspace project settings layout (#3520)
* style: project settings layout for mobile screens * style: workspace settings layout for mobile screens
This commit is contained in:
parent
0d036e6bf5
commit
3a4c893368
@ -31,7 +31,6 @@ export const GoogleSignInButton: FC<Props> = (props) => {
|
||||
size: "large",
|
||||
logo_alignment: "center",
|
||||
text: type === "sign_in" ? "signin_with" : "signup_with",
|
||||
width: 384,
|
||||
} as GsiButtonConfiguration // customization attributes
|
||||
);
|
||||
} catch (err) {
|
||||
|
@ -77,7 +77,7 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
|
||||
})}
|
||||
>
|
||||
Issues
|
||||
<span className="flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-medium py-1 px-1.5 rounded-xl h-4 min-w-6 flex items-center text-center justify-center">
|
||||
<span className="flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-medium rounded-xl h-4 min-w-6 flex items-center text-center justify-center">
|
||||
{totalIssues}
|
||||
</span>
|
||||
</h6>
|
||||
|
@ -72,14 +72,14 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
||||
startedCount > 0
|
||||
? "started"
|
||||
: unStartedCount > 0
|
||||
? "unstarted"
|
||||
: backlogCount > 0
|
||||
? "backlog"
|
||||
: completedCount > 0
|
||||
? "completed"
|
||||
: canceledCount > 0
|
||||
? "cancelled"
|
||||
: null;
|
||||
? "unstarted"
|
||||
: backlogCount > 0
|
||||
? "backlog"
|
||||
: completedCount > 0
|
||||
? "completed"
|
||||
: canceledCount > 0
|
||||
? "cancelled"
|
||||
: null;
|
||||
|
||||
setActiveStateGroup(stateGroup);
|
||||
setDefaultStateGroup(stateGroup);
|
||||
@ -151,13 +151,13 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
||||
/>
|
||||
</div>
|
||||
{totalCount > 0 ? (
|
||||
<div className="flex items-center pl-20 md:pl-11 lg:pl-14 pr-11 mt-11">
|
||||
<div className="flex md:flex-col lg:flex-row items-center gap-x-10 gap-y-8 w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="flex items-center pl-10 md:pl-11 lg:pl-14 pr-11 mt-11">
|
||||
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row items-center justify-evenly gap-x-10 gap-y-8 w-full">
|
||||
<div>
|
||||
<PieGraph
|
||||
data={chartData}
|
||||
height="220px"
|
||||
width="220px"
|
||||
width="200px"
|
||||
innerRadius={0.6}
|
||||
cornerRadius={5}
|
||||
colors={(datum) => datum.data.color}
|
||||
@ -189,7 +189,7 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
||||
layers={["arcs", CenteredMetric]}
|
||||
/>
|
||||
</div>
|
||||
<div className="justify-self-end space-y-6 w-min whitespace-nowrap">
|
||||
<div className="space-y-6 w-min whitespace-nowrap">
|
||||
{chartData.map((item) => (
|
||||
<div key={item.id} className="flex items-center justify-between gap-6">
|
||||
<div className="flex items-center gap-2.5 w-24">
|
||||
|
@ -2,13 +2,13 @@ import { FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// ui
|
||||
import { Breadcrumbs } from "@plane/ui";
|
||||
import { Breadcrumbs, CustomMenu } from "@plane/ui";
|
||||
// helper
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
// hooks
|
||||
import { useProject, useUser } from "hooks/store";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { EUserProjectRoles, PROJECT_SETTINGS_LINKS } from "constants/project";
|
||||
// components
|
||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||
|
||||
@ -20,7 +20,7 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
||||
const { title } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
@ -31,29 +31,48 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
|
||||
|
||||
return (
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||
<SidebarHamburgerToggle />
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<SidebarHamburgerToggle/>
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
label={currentProjectDetails?.name ?? "Project"}
|
||||
icon={
|
||||
currentProjectDetails?.emoji ? (
|
||||
renderEmoji(currentProjectDetails.emoji)
|
||||
) : currentProjectDetails?.icon_prop ? (
|
||||
renderEmoji(currentProjectDetails.icon_prop)
|
||||
) : (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{currentProjectDetails?.name.charAt(0)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||
/>
|
||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||
</Breadcrumbs>
|
||||
<div className="z-50">
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
label={currentProjectDetails?.name ?? "Project"}
|
||||
icon={
|
||||
currentProjectDetails?.emoji ? (
|
||||
renderEmoji(currentProjectDetails.emoji)
|
||||
) : currentProjectDetails?.icon_prop ? (
|
||||
renderEmoji(currentProjectDetails.icon_prop)
|
||||
) : (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{currentProjectDetails?.name.charAt(0)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
link={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}
|
||||
/>
|
||||
<div className="hidden sm:hidden md:block lg:block">
|
||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||
</div>
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
</div>
|
||||
<CustomMenu
|
||||
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
||||
maxHeight="lg"
|
||||
customButton={
|
||||
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">{title}</span>
|
||||
}
|
||||
placement="bottom-start"
|
||||
closeOnSelect
|
||||
>
|
||||
{PROJECT_SETTINGS_LINKS.map((item) => (
|
||||
<CustomMenu.MenuItem key={item.key} onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}>
|
||||
{item.label}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
// ui
|
||||
import { Breadcrumbs } from "@plane/ui";
|
||||
import { Breadcrumbs, CustomMenu } from "@plane/ui";
|
||||
import { Settings } from "lucide-react";
|
||||
// hooks
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { SidebarHamburgerToggle } from "components/core/sidebar/sidebar-menu-hamburger-toggle";
|
||||
import { WORKSPACE_SETTINGS_LINKS } from "constants/workspace";
|
||||
|
||||
export interface IWorkspaceSettingHeader {
|
||||
title: string;
|
||||
@ -21,7 +22,7 @@ export const WorkspaceSettingHeader: FC<IWorkspaceSettingHeader> = observer((pro
|
||||
return (
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<SidebarHamburgerToggle/>
|
||||
<SidebarHamburgerToggle />
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
@ -30,9 +31,26 @@ export const WorkspaceSettingHeader: FC<IWorkspaceSettingHeader> = observer((pro
|
||||
icon={<Settings className="h-4 w-4 text-custom-text-300" />}
|
||||
link={`/${workspaceSlug}/settings`}
|
||||
/>
|
||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||
<div className="hidden sm:hidden md:block lg:block">
|
||||
<Breadcrumbs.BreadcrumbItem type="text" label={title} />
|
||||
</div>
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<CustomMenu
|
||||
className="flex-shrink-0 block sm:block md:hidden lg:hidden"
|
||||
maxHeight="lg"
|
||||
customButton={
|
||||
<span className="text-xs px-1.5 py-1 border rounded-md text-custom-text-200 border-custom-border-300">{title}</span>
|
||||
}
|
||||
placement="bottom-start"
|
||||
closeOnSelect
|
||||
>
|
||||
{WORKSPACE_SETTINGS_LINKS.map((item) => (
|
||||
<CustomMenu.MenuItem key={item.key} onClick={() => router.push(`/${workspaceSlug}${item.href}`)}>
|
||||
{item.label}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -147,10 +147,10 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="relative mt-6 h-44 w-full">
|
||||
<div className="absolute inset-0 z-[1] bg-gradient-to-t from-black/50 to-transparent" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
|
||||
|
||||
<img src={watch("cover_image")!} alt={watch("cover_image")!} className="h-44 w-full rounded-md object-cover" />
|
||||
<div className="absolute bottom-4 z-10 flex w-full items-end justify-between gap-3 px-4">
|
||||
<div className="absolute bottom-4 z-5 flex w-full items-end justify-between gap-3 px-4">
|
||||
<div className="flex flex-grow gap-3 truncate">
|
||||
<div className="flex h-[52px] w-[52px] flex-shrink-0 items-center justify-center rounded-lg bg-custom-background-90">
|
||||
<div className="grid h-7 w-7 place-items-center">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Fragment } from "react";
|
||||
import { Fragment, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
@ -6,6 +6,7 @@ import { useTheme } from "next-themes";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { mutate } from "swr";
|
||||
import { Check, ChevronDown, CircleUserRound, LogOut, Mails, PlusSquare, Settings, UserCircle2 } from "lucide-react";
|
||||
import { usePopper } from "react-popper";
|
||||
// hooks
|
||||
import { useApplication, useUser, useWorkspace } from "hooks/store";
|
||||
// hooks
|
||||
@ -14,7 +15,6 @@ import useToast from "hooks/use-toast";
|
||||
import { Avatar, Loader } from "@plane/ui";
|
||||
// types
|
||||
import { IWorkspace } from "@plane/types";
|
||||
|
||||
// Static Data
|
||||
const userLinks = (workspaceSlug: string, userId: string) => [
|
||||
{
|
||||
@ -36,7 +36,6 @@ const userLinks = (workspaceSlug: string, userId: string) => [
|
||||
icon: Settings,
|
||||
},
|
||||
];
|
||||
|
||||
const profileLinks = (workspaceSlug: string, userId: string) => [
|
||||
{
|
||||
name: "View profile",
|
||||
@ -49,7 +48,6 @@ const profileLinks = (workspaceSlug: string, userId: string) => [
|
||||
link: "/profile",
|
||||
},
|
||||
];
|
||||
|
||||
export const WorkspaceSidebarDropdown = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
@ -64,12 +62,25 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
// popper-js refs
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
// popper-js init
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
placement: "right",
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
options: {
|
||||
padding: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const handleWorkspaceNavigation = (workspace: IWorkspace) =>
|
||||
updateCurrentUser({
|
||||
last_workspace_id: workspace?.id,
|
||||
});
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await signOut()
|
||||
.then(() => {
|
||||
@ -85,16 +96,12 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleItemClick = () => {
|
||||
console.log('CLICKED')
|
||||
if (window.innerWidth < 768) {
|
||||
toggleSidebar();
|
||||
}
|
||||
};
|
||||
|
||||
const workspacesList = Object.values(workspaces ?? {});
|
||||
|
||||
// TODO: fix workspaces list scroll
|
||||
return (
|
||||
<div className="flex items-center gap-x-3 gap-y-2 px-4 pt-4">
|
||||
@ -121,14 +128,12 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
activeWorkspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!sidebarCollapsed && (
|
||||
<h4 className="truncate text-base font-medium text-custom-text-100">
|
||||
{activeWorkspace?.name ? activeWorkspace.name : "Loading..."}
|
||||
</h4>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!sidebarCollapsed && (
|
||||
<ChevronDown
|
||||
className={`mx-1 hidden h-4 w-4 flex-shrink-0 group-hover/menu-button:block ${open ? "rotate-180" : ""
|
||||
@ -137,7 +142,6 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
)}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -185,7 +189,6 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
workspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</span>
|
||||
|
||||
<h5
|
||||
className={`truncate text-sm font-medium ${workspaceSlug === workspace.slug ? "" : "text-custom-text-200"
|
||||
}`}
|
||||
@ -226,9 +229,14 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
{userLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
|
||||
<Link key={link.key} href={link.href} className="w-full" onClick={() => {
|
||||
if (index > 0) handleItemClick();
|
||||
}}>
|
||||
<Link
|
||||
key={link.key}
|
||||
href={link.href}
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
if (index > 0) handleItemClick();
|
||||
}}
|
||||
>
|
||||
<Menu.Item
|
||||
as="div"
|
||||
className="flex items-center gap-2 rounded px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 font-medium"
|
||||
@ -256,10 +264,9 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
|
||||
{!sidebarCollapsed && (
|
||||
<Menu as="div" className="relative flex-shrink-0">
|
||||
<Menu.Button className="grid place-items-center outline-none">
|
||||
<Menu.Button className="grid place-items-center outline-none" ref={setReferenceElement}>
|
||||
<Avatar
|
||||
name={currentUser?.display_name}
|
||||
src={currentUser?.avatar}
|
||||
@ -268,7 +275,6 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
className="!text-base"
|
||||
/>
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
@ -281,11 +287,20 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
<Menu.Items
|
||||
className="absolute left-0 z-20 mt-1 flex w-52 origin-top-left flex-col divide-y
|
||||
divide-custom-sidebar-border-200 rounded-md border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 text-xs shadow-lg outline-none"
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
>
|
||||
<div className="flex flex-col gap-2.5 pb-2">
|
||||
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
||||
{profileLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
|
||||
<Link key={index} href={link.link} onClick={() => { if (index == 0) handleItemClick(); }}>
|
||||
<Link
|
||||
key={index}
|
||||
href={link.link}
|
||||
onClick={() => {
|
||||
if (index == 0) handleItemClick();
|
||||
}}
|
||||
>
|
||||
<Menu.Item key={index} as="div">
|
||||
<span className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
||||
<link.icon className="h-4 w-4 stroke-[1.5]" />
|
||||
@ -323,4 +338,4 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
});
|
@ -41,11 +41,13 @@ export const ProjectSettingLayout: FC<IProjectSettingLayout> = observer((props)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full w-full gap-2 overflow-x-hidden overflow-y-scroll">
|
||||
<div className="w-80 flex-shrink-0 overflow-y-hidden pt-8">
|
||||
<div className="inset-y-0 z-20 flex flex-grow-0 h-full w-full gap-2 overflow-x-hidden overflow-y-scroll">
|
||||
<div className="w-80 flex-shrink-0 overflow-y-hidden pt-8 sm:hidden hidden md:block lg:block">
|
||||
<ProjectSettingsSidebar />
|
||||
</div>
|
||||
{children}
|
||||
<div className="w-full pl-10 sm:pl-10 md:pl-0 lg:pl-0">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -10,11 +10,13 @@ export const WorkspaceSettingLayout: FC<IWorkspaceSettingLayout> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full gap-2 overflow-x-hidden overflow-y-scroll">
|
||||
<div className="w-80 flex-shrink-0 overflow-y-hidden pt-8">
|
||||
<div className="inset-y-0 z-20 flex h-full w-full gap-2 overflow-x-hidden overflow-y-scroll">
|
||||
<div className="w-80 flex-shrink-0 overflow-y-hidden pt-8 sm:hidden hidden md:block lg:block">
|
||||
<WorkspaceSettingsSidebar />
|
||||
</div>
|
||||
{children}
|
||||
<div className="w-full pl-10 sm:pl-10 md:pl-0 lg:pl-0">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user