Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-711] style: profile and its settings pages responsiveness #4022

Merged
merged 8 commits into from
Apr 29, 2024
38 changes: 38 additions & 0 deletions web/components/profile/preferences/preferences-mobile-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Link from "next/link";
import router from "next/router";
// helpers
import { cn } from "@/helpers/common.helper";

export const PreferencesMobileHeader = () => {
const profilePreferenceLinks: Array<{
label: string;
href: string;
}> = [
{
label: "Theme",
href: `/profile/preferences/theme`,
},
{
label: "Email",
href: `/profile/preferences/email`,
},
];

return (
<div className={cn("sticky top-0 flex md:hidden w-full border-b border-custom-border-200")}>
{profilePreferenceLinks.map((link, index) => (
<Link
key={index}
href={link.href}
onClick={() => console.log(router.asPath)}
className={cn(
"flex justify-around py-2 w-full",
router.asPath.includes(link.label.toLowerCase()) ? "border-b-2 border-custom-primary-100" : ""
)}
>
<div className="text-sm text-custom-text-200">{link.label}</div>
</Link>
))}
</div>
);
};
177 changes: 177 additions & 0 deletions web/components/profile/profile-issues-mobile-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { useCallback } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
// icons
import { ChevronDown } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "@plane/types";
// ui
import { CustomMenu } from "@plane/ui";
// components
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown } from "@/components/issues";
// constants
import { EIssueFilterType, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_LAYOUT, ISSUE_LAYOUTS } from "@/constants/issue";
// hooks
import { useIssues, useLabel } from "@/hooks/store";


const ProfileIssuesMobileHeader = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, userId } = router.query;
// store hook
const {
issuesFilter: { issueFilters, updateFilters },
} = useIssues(EIssuesStoreType.PROFILE);

const { workspaceLabels } = useLabel();
// derived values
const states = undefined;
// const members = undefined;
// const activeLayout = issueFilters?.displayFilters?.layout;
// const states = undefined;
const members = undefined;
const activeLayout = issueFilters?.displayFilters?.layout;

const handleLayoutChange = useCallback(
(layout: TIssueLayouts) => {
if (!workspaceSlug || !userId) return;
updateFilters(
workspaceSlug.toString(),
undefined,
EIssueFilterType.DISPLAY_FILTERS,
{ layout: layout },
userId.toString()
);
},
[workspaceSlug, updateFilters, userId]
);

const handleFiltersUpdate = useCallback(
(key: keyof IIssueFilterOptions, value: string | string[]) => {
if (!workspaceSlug || !userId) return;
const newValues = issueFilters?.filters?.[key] ?? [];

if (Array.isArray(value)) {
value.forEach((val) => {
if (!newValues.includes(val)) newValues.push(val);
});
} else {
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
else newValues.push(value);
}

updateFilters(
workspaceSlug.toString(),
undefined,
EIssueFilterType.FILTERS,
{ [key]: newValues },
userId.toString()
);
},
[workspaceSlug, issueFilters, updateFilters, userId]
);

const handleDisplayFilters = useCallback(
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
if (!workspaceSlug || !userId) return;
updateFilters(
workspaceSlug.toString(),
undefined,
EIssueFilterType.DISPLAY_FILTERS,
updatedDisplayFilter,
userId.toString()
);
},
[workspaceSlug, updateFilters, userId]
);

const handleDisplayProperties = useCallback(
(property: Partial<IIssueDisplayProperties>) => {
if (!workspaceSlug || !userId) return;
updateFilters(
workspaceSlug.toString(),
undefined,
EIssueFilterType.DISPLAY_PROPERTIES,
property,
userId.toString()
);
},
[workspaceSlug, updateFilters, userId]
);
return (
<div className="flex justify-evenly border-b border-custom-border-200 py-2 md:hidden">
<CustomMenu
maxHeight={"md"}
className="flex flex-grow justify-center text-sm text-custom-text-200"
placement="bottom-start"
customButton={<span className="flex flex-grow justify-center text-sm text-custom-text-200">Layout</span>}
customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm"
closeOnSelect
>
{ISSUE_LAYOUTS.map((layout, index) => {
if (layout.key === "spreadsheet" || layout.key === "gantt_chart" || layout.key === "calendar") return;
return (
<CustomMenu.MenuItem
key={index}
onClick={() => {
handleLayoutChange(ISSUE_LAYOUTS[index].key);
}}
className="flex items-center gap-2"
>
<layout.icon className="h-3 w-3" />
<div className="text-custom-text-300">{layout.title}</div>
</CustomMenu.MenuItem>
);
})}
</CustomMenu>
<div className="flex flex-grow items-center justify-center border-l border-custom-border-200 text-sm text-custom-text-200">
<FiltersDropdown
title="Filters"
placement="bottom-end"
menuButton={
<span className="flex items-center text-sm text-custom-text-200">
Filters
<ChevronDown className="ml-2 h-4 w-4 text-custom-text-200" />
</span>
}
>
<FilterSelection
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
}
filters={issueFilters?.filters ?? {}}
handleFiltersUpdate={handleFiltersUpdate}
states={states}
labels={workspaceLabels}
memberIds={members}
/>
</FiltersDropdown>
</div>
<div className="flex flex-grow items-center justify-center border-l border-custom-border-200 text-sm text-custom-text-200">
<FiltersDropdown
title="Display"
placement="bottom-end"
menuButton={
<span className="flex items-center text-sm text-custom-text-200">
Display
<ChevronDown className="ml-2 h-4 w-4 text-custom-text-200" />
</span>
}
>
<DisplayFiltersSelection
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
}
displayFilters={issueFilters?.displayFilters ?? {}}
handleDisplayFiltersUpdate={handleDisplayFilters}
displayProperties={issueFilters?.displayProperties ?? {}}
handleDisplayPropertiesUpdate={handleDisplayProperties}
/>
</FiltersDropdown>
</div>
</div>
);
});

export default ProfileIssuesMobileHeader;
62 changes: 10 additions & 52 deletions web/layouts/settings-layout/profile/preferences/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { FC, ReactNode } from "react";
// layout
import Link from "next/link";
import { useRouter } from "next/router";
import { ChevronDown } from "lucide-react";
import { CustomMenu } from "@plane/ui";
import { SidebarHamburgerToggle } from "@/components/core/sidebar/sidebar-menu-hamburger-toggle";
import { PreferencesMobileHeader } from "@/components/profile/preferences/preferences-mobile-header";
import { useApplication } from "@/hooks/store";
import { ProfileSettingsLayout } from "@/layouts/settings-layout";
import { ProfilePreferenceSettingsSidebar } from "./sidebar";
Expand All @@ -16,65 +13,26 @@ interface IProfilePreferenceSettingsLayout {

export const ProfilePreferenceSettingsLayout: FC<IProfilePreferenceSettingsLayout> = (props) => {
const { children, header } = props;
const router = useRouter();
const { theme: themeStore } = useApplication();

const showMenuItem = () => {
const item = router.asPath.split("/");
let splittedItem = item[item.length - 1];
splittedItem = splittedItem.replace(splittedItem[0], splittedItem[0].toUpperCase());
return splittedItem;
};

const profilePreferenceLinks: Array<{
label: string;
href: string;
}> = [
{
label: "Theme",
href: `/profile/preferences/theme`,
},
{
label: "Email",
href: `/profile/preferences/email`,
},
];

return (
<ProfileSettingsLayout
header={
<div className="md:hidden flex flex-shrink-0 gap-4 items-center justify-start border-b border-custom-border-200 p-4">
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
<CustomMenu
maxHeight={"md"}
className="flex flex-grow justify-center text-custom-text-200 text-sm"
placement="bottom-start"
customButton={
<div className="flex gap-2 items-center px-2 py-1.5 border rounded-md border-custom-border-400">
<span className="flex flex-grow justify-center text-custom-text-200 text-sm">{showMenuItem()}</span>
<ChevronDown className="w-4 h-4 text-custom-text-400" />
</div>
}
customButtonClassName="flex flex-grow justify-start text-custom-text-200 text-sm"
>
<></>
{profilePreferenceLinks.map((link) => (
<CustomMenu.MenuItem className="flex items-center gap-2">
<Link key={link.href} href={link.href} className="text-custom-text-300 w-full">
{link.label}
</Link>
</CustomMenu.MenuItem>
))}
</CustomMenu>
</div>
}
>
<div className="relative flex h-screen w-full overflow-hidden">
<ProfilePreferenceSettingsSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
{header}
<div className="h-full w-full overflow-hidden">{children}</div>
</main>
<div className="h-full">
<PreferencesMobileHeader />
<div className="relative flex h-full w-full overflow-hidden">
<ProfilePreferenceSettingsSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
{header}
<div className="h-full w-full">{children}</div>
</main>
</div>
</div>
</ProfileSettingsLayout>
);
Expand Down
8 changes: 4 additions & 4 deletions web/layouts/user-profile-layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// components
import { ProfileNavbar, ProfileSidebar } from "@/components/profile";
// hooks
import { useUser } from "@/hooks/store";
// constants
import { EUserWorkspaceRoles } from "@/constants/workspace";
// hooks
import { useUser } from "@/hooks/store";

type Props = {
children: React.ReactNode;
Expand All @@ -28,8 +28,7 @@ export const ProfileAuthWrapper: React.FC<Props> = observer((props) => {
const isAuthorizedPath = router.pathname.includes("assigned" || "created" || "subscribed");

return (
<div className="h-full w-full md:flex md:flex-row-reverse md:overflow-hidden">
<ProfileSidebar />
<div className="h-full w-full flex md:overflow-hidden">
<div className="flex w-full flex-col md:h-full md:overflow-hidden">
<ProfileNavbar isAuthorized={!!isAuthorized} showProfileIssuesFilter={showProfileIssuesFilter} />
{isAuthorized || !isAuthorizedPath ? (
Expand All @@ -40,6 +39,7 @@ export const ProfileAuthWrapper: React.FC<Props> = observer((props) => {
</div>
)}
</div>
<ProfileSidebar />
</div>
);
});
7 changes: 4 additions & 3 deletions web/pages/[workspaceSlug]/profile/[userId]/assigned.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { ReactElement } from "react";
// layouts
// components
import { PageHead } from "@/components/core";
import { UserProfileHeader } from "@/components/headers";
import { ProfileIssuesPage } from "@/components/profile/profile-issues";
import ProfileIssuesMobileHeader from "@/components/profile/profile-issues-mobile-header";
// layouts
import { AppLayout } from "@/layouts/app-layout";
import { ProfileAuthWrapper } from "@/layouts/user-profile-layout";
// components
// types
import { NextPageWithLayout } from "@/lib/types";

Expand All @@ -18,7 +19,7 @@ const ProfileAssignedIssuesPage: NextPageWithLayout = () => (

ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
return (
<AppLayout header={<UserProfileHeader type="Assigned" />}>
<AppLayout header={<UserProfileHeader type="Assigned" />} mobileHeader={<ProfileIssuesMobileHeader />}>
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
</AppLayout>
);
Expand Down
7 changes: 4 additions & 3 deletions web/pages/[workspaceSlug]/profile/[userId]/created.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { ReactElement } from "react";
// store
import { observer } from "mobx-react-lite";
// layouts
// components
import { PageHead } from "@/components/core";
import { UserProfileHeader } from "@/components/headers";
import { ProfileIssuesPage } from "@/components/profile/profile-issues";
import ProfileIssuesMobileHeader from "@/components/profile/profile-issues-mobile-header";
// layouts
import { AppLayout } from "@/layouts/app-layout";
import { ProfileAuthWrapper } from "@/layouts/user-profile-layout";
// components
// types
import { NextPageWithLayout } from "@/lib/types";

Expand All @@ -20,7 +21,7 @@ const ProfileCreatedIssuesPage: NextPageWithLayout = () => (

ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
return (
<AppLayout header={<UserProfileHeader type="Created" />}>
<AppLayout header={<UserProfileHeader type="Created" />} mobileHeader={<ProfileIssuesMobileHeader />}>
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
</AppLayout>
);
Expand Down
Loading
Loading