UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: редизайн drafts stickies profile home и settings-shell
This commit is contained in:
parent
aa9c278b59
commit
290b00d251
|
|
@ -8,14 +8,13 @@ import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// ui
|
|
||||||
import { Button } from "@plane/propel/button";
|
|
||||||
import { DraftIcon } from "@plane/propel/icons";
|
import { DraftIcon } from "@plane/propel/icons";
|
||||||
import { EIssuesStoreType } from "@plane/types";
|
import { EIssuesStoreType } from "@plane/types";
|
||||||
import { Breadcrumbs, Header } from "@plane/ui";
|
import { Breadcrumbs, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
||||||
import { CountChip } from "@/components/common/count-chip";
|
import { CountChip } from "@/components/common/count-chip";
|
||||||
|
import { AppHeaderPrimaryActionButton } from "@/components/core/app-header/primary-action-button";
|
||||||
import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal";
|
import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -66,15 +65,12 @@ export const WorkspaceDraftHeader = observer(function WorkspaceDraftHeader() {
|
||||||
|
|
||||||
<Header.RightItem>
|
<Header.RightItem>
|
||||||
{joinedProjectIds && joinedProjectIds.length > 0 && (
|
{joinedProjectIds && joinedProjectIds.length > 0 && (
|
||||||
<Button
|
<AppHeaderPrimaryActionButton
|
||||||
variant="primary"
|
|
||||||
size="lg"
|
|
||||||
className="items-center gap-1"
|
|
||||||
onClick={() => setIsDraftIssueModalOpen(true)}
|
onClick={() => setIsDraftIssueModalOpen(true)}
|
||||||
disabled={!isAuthorizedUser}
|
disabled={!isAuthorizedUser}
|
||||||
>
|
>
|
||||||
{t("workspace_draft_issues.draft_an_issue")}
|
{t("workspace_draft_issues.draft_an_issue")}
|
||||||
</Button>
|
</AppHeaderPrimaryActionButton>
|
||||||
)}
|
)}
|
||||||
</Header.RightItem>
|
</Header.RightItem>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// components
|
// components
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { PageHead } from "@/components/core/page-title";
|
import { PageHead } from "@/components/core/page-title";
|
||||||
import { WorkspaceDraftIssuesRoot } from "@/components/issues/workspace-draft";
|
import { WorkspaceDraftIssuesRoot } from "@/components/issues/workspace-draft";
|
||||||
import type { Route } from "./+types/page";
|
import type { Route } from "./+types/page";
|
||||||
|
|
||||||
function WorkspaceDraftPage({ params }: Route.ComponentProps) {
|
function WorkspaceDraftPage({ params }: Route.ComponentProps) {
|
||||||
const { workspaceSlug } = params;
|
const { workspaceSlug } = params;
|
||||||
const pageTitle = "Workspace Draft";
|
const { t } = useTranslation();
|
||||||
|
const pageTitle = t("sidebar.drafts");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export const WorkspaceDashboardHeader = observer(function WorkspaceDashboardHead
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={() => toggleWidgetSettings(true)}
|
onClick={() => toggleWidgetSettings(true)}
|
||||||
className="my-auto mb-0"
|
className="nodedc-toolbar-pill my-auto mb-0"
|
||||||
prependIcon={<Shapes />}
|
prependIcon={<Shapes />}
|
||||||
>
|
>
|
||||||
<div className="hidden sm:hidden md:block">{t("home.manage_widgets")}</div>
|
<div className="hidden sm:hidden md:block">{t("home.manage_widgets")}</div>
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
// components
|
// components
|
||||||
import { PageHead } from "@/components/core/page-title";
|
import { PageHead } from "@/components/core/page-title";
|
||||||
import { ProfileIssuesPage } from "@/components/profile/profile-issues";
|
import { ProfileIssuesPage } from "@/components/profile/profile-issues";
|
||||||
import type { Route } from "./+types/page";
|
import type { Route } from "./+types/page";
|
||||||
|
|
||||||
const ProfilePageHeader = {
|
const ProfilePageHeader = {
|
||||||
assigned: "Profile - Assigned",
|
assigned: "profile.tabs.assigned",
|
||||||
created: "Profile - Created",
|
created: "profile.tabs.created",
|
||||||
subscribed: "Profile - Subscribed",
|
subscribed: "profile.tabs.subscribed",
|
||||||
};
|
};
|
||||||
|
|
||||||
function isValidProfileViewId(viewId: string): viewId is keyof typeof ProfilePageHeader {
|
function isValidProfileViewId(viewId: string): viewId is keyof typeof ProfilePageHeader {
|
||||||
|
|
@ -22,10 +23,11 @@ function isValidProfileViewId(viewId: string): viewId is keyof typeof ProfilePag
|
||||||
|
|
||||||
function ProfileIssuesTypePage({ params }: Route.ComponentProps) {
|
function ProfileIssuesTypePage({ params }: Route.ComponentProps) {
|
||||||
const { profileViewId } = params;
|
const { profileViewId } = params;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!isValidProfileViewId(profileViewId)) return null;
|
if (!isValidProfileViewId(profileViewId)) return null;
|
||||||
|
|
||||||
const header = ProfilePageHeader[profileViewId];
|
const header = `${t("profile.page_label")} - ${t(ProfilePageHeader[profileViewId])}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -51,20 +51,21 @@ function ProfileActivityPage() {
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
EUserPermissionsLevel.WORKSPACE
|
EUserPermissionsLevel.WORKSPACE
|
||||||
);
|
);
|
||||||
|
const pageTitle = `${t("profile.page_label")} - ${t("profile.tabs.activity")}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHead title="Profile - Activity" />
|
<PageHead title={pageTitle} />
|
||||||
<div className="flex h-full w-full flex-col overflow-hidden py-5">
|
<div className="nodedc-workspace-page-shell flex h-full w-full flex-col gap-5 overflow-hidden">
|
||||||
<div className="flex items-center justify-between gap-2 px-5 md:px-9">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h3 className="text-16 font-medium">{t("profile.stats.recent_activity.title")}</h3>
|
<h3 className="text-16 font-medium">{t("profile.stats.recent_activity.title")}</h3>
|
||||||
{canDownloadActivity && <DownloadActivityButton />}
|
{canDownloadActivity && <DownloadActivityButton />}
|
||||||
</div>
|
</div>
|
||||||
<div className="vertical-scrollbar flex scrollbar-md h-full flex-col overflow-y-auto px-5 md:px-9">
|
<div className="vertical-scrollbar flex scrollbar-md h-full flex-col overflow-y-auto">
|
||||||
{activityPages}
|
{activityPages}
|
||||||
{pageCount < totalPages && resultsCount !== 0 && (
|
{pageCount < totalPages && resultsCount !== 0 && (
|
||||||
<div className="flex w-full items-center justify-center text-11">
|
<div className="flex w-full items-center justify-center text-11">
|
||||||
<Button variant="secondary" onClick={handleLoadMore}>
|
<Button variant="secondary" size="lg" className="nodedc-toolbar-pill" onClick={handleLoadMore}>
|
||||||
{t("common.load_more")}
|
{t("common.load_more")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import { ProfileIssuesFilter } from "@/components/profile/profile-issues-filter"
|
||||||
// hooks
|
// hooks
|
||||||
import { useAppTheme } from "@/hooks/store/use-app-theme";
|
import { useAppTheme } from "@/hooks/store/use-app-theme";
|
||||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||||
import { Button } from "@plane/propel/button";
|
|
||||||
|
|
||||||
type TUserProfileHeader = {
|
type TUserProfileHeader = {
|
||||||
userProjectsData: IUserProfileProjectSegregation | undefined;
|
userProjectsData: IUserProfileProjectSegregation | undefined;
|
||||||
|
|
@ -75,8 +74,10 @@ export const UserProfileHeader = observer(function UserProfileHeader(props: TUse
|
||||||
<SelectionDropdown
|
<SelectionDropdown
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
menuButton={
|
menuButton={
|
||||||
<div className="flex items-center gap-2 rounded-md border border-subtle px-2 py-1.5">
|
<div className="nodedc-toolbar-pill flex items-center gap-2 px-3.5">
|
||||||
<span className="flex flex-grow justify-center text-13 text-secondary">{type}</span>
|
<span className="flex flex-grow justify-center text-13 text-secondary">
|
||||||
|
{type ? t(type) : t("profile.tabs.summary")}
|
||||||
|
</span>
|
||||||
<ChevronDownIcon className="h-4 w-4 text-placeholder" />
|
<ChevronDownIcon className="h-4 w-4 text-placeholder" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -89,16 +90,15 @@ export const UserProfileHeader = observer(function UserProfileHeader(props: TUse
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
<div className="shrink-0 md:hidden">
|
<div className="shrink-0 md:hidden">
|
||||||
<Button
|
<button
|
||||||
variant="ghost"
|
type="button"
|
||||||
size="lg"
|
className="nodedc-toolbar-icon-button grid h-10 w-10 place-items-center rounded-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleProfileSidebar();
|
toggleProfileSidebar();
|
||||||
}}
|
}}
|
||||||
appendIcon={
|
>
|
||||||
<PanelRight className={!profileSidebarCollapsed ? "text-accent-primary" : "text-secondary"} />
|
<PanelRight className={!profileSidebarCollapsed ? "text-accent-primary" : "text-secondary"} />
|
||||||
}
|
</button>
|
||||||
></Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Header.RightItem>
|
</Header.RightItem>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { useParams, usePathname } from "next/navigation";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { PROFILE_VIEWER_TAB, PROFILE_ADMINS_TAB } from "@plane/constants";
|
import { PROFILE_VIEWER_TAB, PROFILE_ADMINS_TAB } from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { Header, EHeaderVariant } from "@plane/ui";
|
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -25,17 +24,21 @@ export function ProfileNavbar(props: Props) {
|
||||||
const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB;
|
const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header variant={EHeaderVariant.SECONDARY} showOnMobile={false}>
|
<div className="hidden border-b border-white/6 px-5 py-3 md:block md:px-9">
|
||||||
<div className="flex items-center overflow-x-scroll">
|
<div className="flex items-center gap-2 overflow-x-auto">
|
||||||
{tabsList.map((tab) => (
|
{tabsList.map((tab) => (
|
||||||
<Link key={tab.route} href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}>
|
<Link
|
||||||
|
key={tab.route}
|
||||||
|
href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}
|
||||||
|
className={cn(
|
||||||
|
"nodedc-toolbar-pill whitespace-nowrap px-4 text-13 font-medium",
|
||||||
|
pathname === `/${workspaceSlug}/profile/${userId}${tab.selected}` ? "text-primary" : "text-secondary"
|
||||||
|
)}
|
||||||
|
data-active={pathname === `/${workspaceSlug}/profile/${userId}${tab.selected}` ? "true" : "false"}
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
`flex border-b-2 p-4 text-13 font-medium whitespace-nowrap text-tertiary outline-none hover:text-primary ${
|
"flex items-center justify-center"
|
||||||
pathname === `/${workspaceSlug}/profile/${userId}${tab.selected}`
|
|
||||||
? "border-accent-strong text-accent-primary hover:text-accent-primary"
|
|
||||||
: "border-transparent"
|
|
||||||
}`
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{t(tab.i18n_label)}
|
{t(tab.i18n_label)}
|
||||||
|
|
@ -43,6 +46,6 @@ export function ProfileNavbar(props: Props) {
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export default function ProfileOverviewPage({ params }: Route.ComponentProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHead title={t("profile.page_label")} />
|
<PageHead title={t("profile.page_label")} />
|
||||||
<ContentWrapper className="space-y-7">
|
<ContentWrapper className="nodedc-workspace-page-shell space-y-7">
|
||||||
<ProfileStats userProfile={userProfile} />
|
<ProfileStats userProfile={userProfile} />
|
||||||
<ProfileWorkload stateDistribution={stateDistribution} />
|
<ProfileWorkload stateDistribution={stateDistribution} />
|
||||||
<div className="grid grid-cols-1 items-stretch gap-5 xl:grid-cols-2">
|
<div className="grid grid-cols-1 items-stretch gap-5 xl:grid-cols-2">
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,12 @@
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { Button } from "@plane/propel/button";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { RecentStickyIcon } from "@plane/propel/icons";
|
import { RecentStickyIcon } from "@plane/propel/icons";
|
||||||
import { Breadcrumbs, Header } from "@plane/ui";
|
import { Breadcrumbs, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
||||||
|
import { AppHeaderPrimaryActionButton } from "@/components/core/app-header/primary-action-button";
|
||||||
import { StickySearch } from "@/components/stickies/modal/search";
|
import { StickySearch } from "@/components/stickies/modal/search";
|
||||||
import { useStickyOperations } from "@/components/stickies/sticky/use-operations";
|
import { useStickyOperations } from "@/components/stickies/sticky/use-operations";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -19,6 +20,7 @@ import { useSticky } from "@/hooks/use-stickies";
|
||||||
|
|
||||||
export const WorkspaceStickyHeader = observer(function WorkspaceStickyHeader() {
|
export const WorkspaceStickyHeader = observer(function WorkspaceStickyHeader() {
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
// hooks
|
// hooks
|
||||||
const { creatingSticky, toggleShowNewSticky } = useSticky();
|
const { creatingSticky, toggleShowNewSticky } = useSticky();
|
||||||
const { stickyOperations } = useStickyOperations({ workspaceSlug: workspaceSlug?.toString() });
|
const { stickyOperations } = useStickyOperations({ workspaceSlug: workspaceSlug?.toString() });
|
||||||
|
|
@ -32,7 +34,7 @@ export const WorkspaceStickyHeader = observer(function WorkspaceStickyHeader() {
|
||||||
<Breadcrumbs.Item
|
<Breadcrumbs.Item
|
||||||
component={
|
component={
|
||||||
<BreadcrumbLink
|
<BreadcrumbLink
|
||||||
label={`Stickies`}
|
label={t("sidebar.stickies")}
|
||||||
icon={<RecentStickyIcon className="size-5 rotate-90 text-secondary" />}
|
icon={<RecentStickyIcon className="size-5 rotate-90 text-secondary" />}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
@ -43,17 +45,15 @@ export const WorkspaceStickyHeader = observer(function WorkspaceStickyHeader() {
|
||||||
|
|
||||||
<Header.RightItem>
|
<Header.RightItem>
|
||||||
<StickySearch />
|
<StickySearch />
|
||||||
<Button
|
<AppHeaderPrimaryActionButton
|
||||||
variant="primary"
|
|
||||||
size="lg"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleShowNewSticky(true);
|
toggleShowNewSticky(true);
|
||||||
stickyOperations.create();
|
stickyOperations.create();
|
||||||
}}
|
}}
|
||||||
loading={creatingSticky}
|
loading={creatingSticky}
|
||||||
>
|
>
|
||||||
Add sticky
|
{t("stickies.add")}
|
||||||
</Button>
|
</AppHeaderPrimaryActionButton>
|
||||||
</Header.RightItem>
|
</Header.RightItem>
|
||||||
</Header>
|
</Header>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// components
|
// components
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { PageHead } from "@/components/core/page-title";
|
import { PageHead } from "@/components/core/page-title";
|
||||||
import { StickiesInfinite } from "@/components/stickies/layout/stickies-infinite";
|
import { StickiesInfinite } from "@/components/stickies/layout/stickies-infinite";
|
||||||
|
|
||||||
export default function WorkspaceStickiesPage() {
|
export default function WorkspaceStickiesPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHead title="Your stickies" />
|
<PageHead title={t("sidebar.stickies")} />
|
||||||
<div className="relative h-full w-full overflow-hidden overflow-y-auto">
|
<div className="relative h-full w-full overflow-hidden overflow-y-auto">
|
||||||
<div className="nodedc-workspace-page-shell h-full">
|
<div className="nodedc-workspace-page-shell h-full">
|
||||||
<StickiesInfinite />
|
<StickiesInfinite />
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ export default function SettingsLayout() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ProjectsAppPowerKProvider />
|
<ProjectsAppPowerKProvider />
|
||||||
<div className="relative flex size-full overflow-hidden rounded-lg border border-subtle">
|
<div className="relative flex size-full overflow-hidden bg-canvas p-2">
|
||||||
<main className="relative flex size-full flex-col overflow-hidden">
|
<main className="relative flex size-full flex-col overflow-hidden rounded-[1.6rem] border border-white/6 bg-[rgba(10,10,14,0.9)] shadow-[0_20px_48px_rgba(0,0,0,0.24)] backdrop-blur-2xl">
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<ContentWrapper className="w-full bg-surface-1 md:flex">
|
<ContentWrapper className="w-full bg-transparent md:flex">
|
||||||
<div className="size-full overflow-hidden">
|
<div className="size-full overflow-hidden">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export default function ProfileSettingsLayout() {
|
||||||
<ProjectsAppPowerKProvider />
|
<ProjectsAppPowerKProvider />
|
||||||
<AuthenticationWrapper>
|
<AuthenticationWrapper>
|
||||||
<div className="relative flex size-full overflow-hidden bg-canvas p-2">
|
<div className="relative flex size-full overflow-hidden bg-canvas p-2">
|
||||||
<main className="relative flex size-full flex-col overflow-hidden rounded-lg border border-subtle bg-surface-1">
|
<main className="relative flex size-full flex-col overflow-hidden rounded-[1.6rem] border border-white/6 bg-[rgba(10,10,14,0.9)] shadow-[0_20px_48px_rgba(0,0,0,0.24)] backdrop-blur-2xl">
|
||||||
<div className="size-full overflow-hidden">
|
<div className="size-full overflow-hidden">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -92,13 +92,13 @@ export const DashboardWidgets = observer(function DashboardWidgets() {
|
||||||
{!isWikiApp && <NoProjectsEmptyState />}
|
{!isWikiApp && <NoProjectsEmptyState />}
|
||||||
|
|
||||||
{isAnyWidgetEnabled ? (
|
{isAnyWidgetEnabled ? (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col gap-2">
|
||||||
{orderedWidgets.map((key) => {
|
{orderedWidgets.map((key) => {
|
||||||
const WidgetComponent = HOME_WIDGETS_LIST[key]?.component;
|
const WidgetComponent = HOME_WIDGETS_LIST[key]?.component;
|
||||||
const isEnabled = widgetsMap[key]?.is_enabled;
|
const isEnabled = widgetsMap[key]?.is_enabled;
|
||||||
if (!WidgetComponent || !isEnabled) return null;
|
if (!WidgetComponent || !isEnabled) return null;
|
||||||
return (
|
return (
|
||||||
<div key={key} className="py-4">
|
<div key={key} className="py-3">
|
||||||
<WidgetComponent workspaceSlug={workspaceSlug.toString()} />
|
<WidgetComponent workspaceSlug={workspaceSlug.toString()} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ export const WorkspaceHomeView = observer(function WorkspaceHomeView() {
|
||||||
)}
|
)}
|
||||||
<>
|
<>
|
||||||
<HomePeekOverviewsRoot />
|
<HomePeekOverviewsRoot />
|
||||||
<ContentWrapper className="mx-auto scrollbar-hide gap-6 bg-surface-1 px-page-x">
|
<ContentWrapper className="mx-auto scrollbar-hide gap-6 bg-transparent px-page-x">
|
||||||
<div className="mx-auto w-full max-w-[800px]">
|
<div className="nodedc-workspace-page-shell mx-auto w-full max-w-[980px]">
|
||||||
{currentUser && <UserGreetingsView user={currentUser} />}
|
{currentUser && <UserGreetingsView user={currentUser} />}
|
||||||
<DashboardWidgets />
|
<DashboardWidgets />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ export function AddLink(props: TProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary flex h-[56px] w-[230px] gap-4 rounded-md border-[0.5px] border-subtle bg-surface-1 px-4"
|
className="nodedc-workspace-list-row flex h-[4.75rem] w-[230px] items-center gap-4 px-4"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<div className="my-auto h-8 w-8 rounded-sm bg-layer-1/40 p-2">
|
<div className="grid size-9 place-items-center rounded-full bg-white/6">
|
||||||
<PlusIcon className="h-4 w-4 stroke-2 text-tertiary" />
|
<PlusIcon className="h-4 w-4 stroke-2 text-tertiary" />
|
||||||
</div>
|
</div>
|
||||||
<div className="my-auto text-13 font-medium">{t("home.quick_links.add")}</div>
|
<div className="text-13 font-medium text-primary">{t("home.quick_links.add")}</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export const ProjectLinkList = observer(function ProjectLinkList(props: TProject
|
||||||
maxHeight={150}
|
maxHeight={150}
|
||||||
containerClassName="box-border min-h-[30px] flex flex-col"
|
containerClassName="box-border min-h-[30px] flex flex-col"
|
||||||
fallback={<></>}
|
fallback={<></>}
|
||||||
buttonClassName="bg-surface-2/20"
|
buttonClassName="nodedc-toolbar-pill justify-center"
|
||||||
>
|
>
|
||||||
<div className="mb-2 flex flex-1 flex-wrap gap-2">
|
<div className="mb-2 flex flex-1 flex-wrap gap-2">
|
||||||
{links.map((linkId) => (
|
{links.map((linkId) => (
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { observer } from "mobx-react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
|
import { Button } from "@plane/propel/button";
|
||||||
import { PlusIcon } from "@plane/propel/icons";
|
import { PlusIcon } from "@plane/propel/icons";
|
||||||
import type { THomeWidgetProps } from "@plane/types";
|
import type { THomeWidgetProps } from "@plane/types";
|
||||||
import { useHome } from "@/hooks/store/use-home";
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
|
|
@ -49,12 +50,15 @@ export const DashboardQuickLinks = observer(function DashboardQuickLinks(props:
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<div className="text-14 font-semibold text-tertiary">{t("home.quick_links.title_plural")}</div>
|
<div className="text-14 font-semibold text-tertiary">{t("home.quick_links.title_plural")}</div>
|
||||||
<button
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="lg"
|
||||||
|
className="nodedc-toolbar-pill"
|
||||||
onClick={handleCreateLinkModal}
|
onClick={handleCreateLinkModal}
|
||||||
className="my-auto flex gap-1 text-13 font-medium text-accent-primary"
|
prependIcon={<PlusIcon className="size-4" />}
|
||||||
>
|
>
|
||||||
<PlusIcon className="my-auto size-4" /> <span>{t("home.quick_links.add")}</span>
|
<span>{t("home.quick_links.add")}</span>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-wrap">
|
<div className="flex w-full flex-wrap">
|
||||||
{/* rendering links */}
|
{/* rendering links */}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ export const RecentActivityWidget = observer(function RecentActivityWidget(props
|
||||||
maxHeight={415}
|
maxHeight={415}
|
||||||
containerClassName="box-border min-h-[250px]"
|
containerClassName="box-border min-h-[250px]"
|
||||||
fallback={<></>}
|
fallback={<></>}
|
||||||
buttonClassName="bg-surface-2/20"
|
buttonClassName="nodedc-toolbar-pill justify-center"
|
||||||
>
|
>
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<div className="text-14 font-semibold text-tertiary">{t("home.recents.title")}</div>
|
<div className="text-14 font-semibold text-tertiary">{t("home.recents.title")}</div>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { CopyIcon, EditIcon, TrashIcon } from "@plane/propel/icons";
|
||||||
import { Tooltip } from "@plane/propel/tooltip";
|
import { Tooltip } from "@plane/propel/tooltip";
|
||||||
import type { TWorkspaceDraftIssue } from "@plane/types";
|
import type { TWorkspaceDraftIssue } from "@plane/types";
|
||||||
import { EIssuesStoreType } from "@plane/types";
|
import { EIssuesStoreType } from "@plane/types";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { Row } from "@plane/ui";
|
import { Row } from "@plane/ui";
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
|
|
@ -46,6 +47,7 @@ export const DraftIssueBlock = observer(function DraftIssueBlock(props: Props) {
|
||||||
const { getIssueById, updateIssue, deleteIssue } = useWorkspaceDraftIssues();
|
const { getIssueById, updateIssue, deleteIssue } = useWorkspaceDraftIssues();
|
||||||
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
|
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
|
||||||
const { getProjectIdentifierById } = useProject();
|
const { getProjectIdentifierById } = useProject();
|
||||||
|
const { t } = useTranslation();
|
||||||
// ref
|
// ref
|
||||||
const issueRef = useRef<HTMLDivElement | null>(null);
|
const issueRef = useRef<HTMLDivElement | null>(null);
|
||||||
// derived values
|
// derived values
|
||||||
|
|
@ -65,7 +67,7 @@ export const DraftIssueBlock = observer(function DraftIssueBlock(props: Props) {
|
||||||
const MENU_ITEMS: TContextMenuItem[] = [
|
const MENU_ITEMS: TContextMenuItem[] = [
|
||||||
{
|
{
|
||||||
key: "edit",
|
key: "edit",
|
||||||
title: "edit",
|
title: t("edit"),
|
||||||
icon: EditIcon,
|
icon: EditIcon,
|
||||||
action: () => {
|
action: () => {
|
||||||
setIssueToEdit(issue);
|
setIssueToEdit(issue);
|
||||||
|
|
@ -74,7 +76,7 @@ export const DraftIssueBlock = observer(function DraftIssueBlock(props: Props) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "make-a-copy",
|
key: "make-a-copy",
|
||||||
title: "make_a_copy",
|
title: t("make_a_copy"),
|
||||||
icon: CopyIcon,
|
icon: CopyIcon,
|
||||||
action: () => {
|
action: () => {
|
||||||
setCreateUpdateIssueModal(true);
|
setCreateUpdateIssueModal(true);
|
||||||
|
|
@ -82,7 +84,7 @@ export const DraftIssueBlock = observer(function DraftIssueBlock(props: Props) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "move-to-issues",
|
key: "move-to-issues",
|
||||||
title: "move_to_project",
|
title: t("move_to_project"),
|
||||||
icon: SquareStackIcon,
|
icon: SquareStackIcon,
|
||||||
action: () => {
|
action: () => {
|
||||||
setMoveToIssue(true);
|
setMoveToIssue(true);
|
||||||
|
|
@ -92,7 +94,7 @@ export const DraftIssueBlock = observer(function DraftIssueBlock(props: Props) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "delete",
|
key: "delete",
|
||||||
title: "delete",
|
title: t("delete"),
|
||||||
icon: TrashIcon,
|
icon: TrashIcon,
|
||||||
action: () => {
|
action: () => {
|
||||||
setDeleteIssueModal(true);
|
setDeleteIssueModal(true);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// icons
|
// icons
|
||||||
import { DueDatePropertyIcon, StartDatePropertyIcon } from "@plane/propel/icons";
|
import { DueDatePropertyIcon, StartDatePropertyIcon } from "@plane/propel/icons";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
// types
|
// types
|
||||||
import type { TIssuePriorities, TWorkspaceDraftIssue } from "@plane/types";
|
import type { TIssuePriorities, TWorkspaceDraftIssue } from "@plane/types";
|
||||||
import { getDate, renderFormattedPayloadDate, shouldHighlightIssueDueDate } from "@plane/utils";
|
import { getDate, renderFormattedPayloadDate, shouldHighlightIssueDueDate } from "@plane/utils";
|
||||||
|
|
@ -49,6 +50,7 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
const { getStateById } = useProjectState();
|
const { getStateById } = useProjectState();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const projectDetails = getProjectById(issue.project_id);
|
const projectDetails = getProjectById(issue.project_id);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
|
|
@ -180,7 +182,7 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
value={issue.start_date ?? null}
|
value={issue.start_date ?? null}
|
||||||
onChange={handleStartDate}
|
onChange={handleStartDate}
|
||||||
maxDate={maxDate}
|
maxDate={maxDate}
|
||||||
placeholder="Start date"
|
placeholder={t("start_date")}
|
||||||
icon={<StartDatePropertyIcon className="h-3 w-3 flex-shrink-0" />}
|
icon={<StartDatePropertyIcon className="h-3 w-3 flex-shrink-0" />}
|
||||||
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||||
optionsClassName="z-10"
|
optionsClassName="z-10"
|
||||||
|
|
@ -195,7 +197,7 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
value={issue?.target_date ?? null}
|
value={issue?.target_date ?? null}
|
||||||
onChange={handleTargetDate}
|
onChange={handleTargetDate}
|
||||||
minDate={minDate}
|
minDate={minDate}
|
||||||
placeholder="Due date"
|
placeholder={t("due_date")}
|
||||||
icon={<DueDatePropertyIcon className="h-3 w-3 flex-shrink-0" />}
|
icon={<DueDatePropertyIcon className="h-3 w-3 flex-shrink-0" />}
|
||||||
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
||||||
buttonClassName={
|
buttonClassName={
|
||||||
|
|
@ -218,7 +220,7 @@ export const DraftIssueProperties = observer(function DraftIssueProperties(props
|
||||||
buttonVariant={issue.assignee_ids?.length > 0 ? "transparent-without-text" : "border-without-text"}
|
buttonVariant={issue.assignee_ids?.length > 0 ? "transparent-without-text" : "border-without-text"}
|
||||||
buttonClassName={issue.assignee_ids?.length > 0 ? "hover:bg-transparent px-0" : ""}
|
buttonClassName={issue.assignee_ids?.length > 0 ? "hover:bg-transparent px-0" : ""}
|
||||||
showTooltip={issue?.assignee_ids?.length === 0}
|
showTooltip={issue?.assignee_ids?.length === 0}
|
||||||
placeholder="Assignees"
|
placeholder={t("assignees")}
|
||||||
optionsClassName="z-10"
|
optionsClassName="z-10"
|
||||||
tooltipContent=""
|
tooltipContent=""
|
||||||
renderByDefault={isMobile}
|
renderByDefault={isMobile}
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,12 @@ export const WorkspaceDraftIssuesRoot = observer(function WorkspaceDraftIssuesRo
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={cn("h-11 text-13 font-medium transition-all", {
|
className={cn("h-11 text-13 font-medium transition-all", {
|
||||||
"nodedc-workspace-toolbar flex cursor-pointer items-center justify-center rounded-[1.25rem] border-0 bg-transparent px-4 py-3 text-primary underline-offset-2 hover:text-primary hover:underline":
|
"nodedc-toolbar-pill mx-auto flex cursor-pointer items-center justify-center px-5 text-primary":
|
||||||
paginationInfo?.next_page_results,
|
paginationInfo?.next_page_results,
|
||||||
})}
|
})}
|
||||||
onClick={handleNextIssues}
|
onClick={handleNextIssues}
|
||||||
>
|
>
|
||||||
Load More ↓
|
{t("common.load_more")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import Link from "next/link";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { History, MessageSquare } from "lucide-react";
|
import { History, MessageSquare } from "lucide-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import type { IUserActivityResponse } from "@plane/types";
|
import type { IUserActivityResponse } from "@plane/types";
|
||||||
import { calculateTimeAgo, getFileURL } from "@plane/utils";
|
import { calculateTimeAgo, getFileURL } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
|
|
@ -30,6 +31,7 @@ export const ActivityList = observer(function ActivityList(props: Props) {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const { getWorkspaceBySlug } = useWorkspace();
|
const { getWorkspaceBySlug } = useWorkspace();
|
||||||
|
const { t } = useTranslation();
|
||||||
// derived values
|
// derived values
|
||||||
const workspaceId = getWorkspaceBySlug(workspaceSlug?.toString() ?? "")?.id ?? "";
|
const workspaceId = getWorkspaceBySlug(workspaceSlug?.toString() ?? "")?.id ?? "";
|
||||||
|
|
||||||
|
|
@ -72,7 +74,7 @@ export const ActivityList = observer(function ActivityList(props: Props) {
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-0.5 text-11 text-secondary">
|
<p className="mt-0.5 text-11 text-secondary">
|
||||||
Commented {calculateTimeAgo(activityItem.created_at)}
|
{t("commented")} {calculateTimeAgo(activityItem.created_at)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="issue-comments-section p-0">
|
<div className="issue-comments-section p-0">
|
||||||
|
|
@ -102,7 +104,7 @@ export const ActivityList = observer(function ActivityList(props: Props) {
|
||||||
) &&
|
) &&
|
||||||
!activityItem.field ? (
|
!activityItem.field ? (
|
||||||
<span>
|
<span>
|
||||||
created <IssueLink activity={activityItem} />
|
{t("created").toLowerCase()} <IssueLink activity={activityItem} />
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<ActivityMessage activity={activityItem} showIssue />
|
<ActivityMessage activity={activityItem} showIssue />
|
||||||
|
|
@ -155,7 +157,7 @@ export const ActivityList = observer(function ActivityList(props: Props) {
|
||||||
>
|
>
|
||||||
<span className="text-gray font-medium">
|
<span className="text-gray font-medium">
|
||||||
{currentUser?.id === activityItem.actor_detail.id
|
{currentUser?.id === activityItem.actor_detail.id
|
||||||
? "You"
|
? t("you")
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import Link from "next/link";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// icons
|
// icons
|
||||||
import { History, MessageSquare } from "lucide-react";
|
import { History, MessageSquare } from "lucide-react";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { calculateTimeAgo, getFileURL } from "@plane/utils";
|
import { calculateTimeAgo, getFileURL } from "@plane/utils";
|
||||||
// hooks
|
// hooks
|
||||||
import { ActivityIcon, ActivityMessage } from "@/components/core/activity";
|
import { ActivityIcon, ActivityMessage } from "@/components/core/activity";
|
||||||
|
|
@ -35,6 +36,7 @@ export const ProfileActivityListPage = observer(function ProfileActivityListPage
|
||||||
const { cursor, perPage, updateResultsCount, updateTotalPages, updateEmptyState } = props;
|
const { cursor, perPage, updateResultsCount, updateTotalPages, updateEmptyState } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { data: userProfileActivity } = useSWR(
|
const { data: userProfileActivity } = useSWR(
|
||||||
USER_ACTIVITY({
|
USER_ACTIVITY({
|
||||||
|
|
@ -96,7 +98,7 @@ export const ProfileActivityListPage = observer(function ProfileActivityListPage
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-0.5 text-11 text-secondary">
|
<p className="mt-0.5 text-11 text-secondary">
|
||||||
Commented {calculateTimeAgo(activityItem.created_at)}
|
{t("commented")} {calculateTimeAgo(activityItem.created_at)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="issue-comments-section p-0">
|
<div className="issue-comments-section p-0">
|
||||||
|
|
@ -166,7 +168,7 @@ export const ProfileActivityListPage = observer(function ProfileActivityListPage
|
||||||
>
|
>
|
||||||
<span className="text-gray font-medium">
|
<span className="text-gray font-medium">
|
||||||
{currentUser?.id === activityItem.actor_detail.id
|
{currentUser?.id === activityItem.actor_detail.id
|
||||||
? "You"
|
? t("you")
|
||||||
: activityItem.actor_detail.display_name}
|
: activityItem.actor_detail.display_name}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export const ProfileActivity = observer(function ProfileActivity() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h3 className="text-16 font-medium">{t("profile.stats.recent_activity.title")}</h3>
|
<h3 className="text-16 font-medium">{t("profile.stats.recent_activity.title")}</h3>
|
||||||
<Card>
|
<Card className="nodedc-workspace-list-row border-0 bg-transparent p-5">
|
||||||
{userProfileActivity ? (
|
{userProfileActivity ? (
|
||||||
userProfileActivity.results.length > 0 ? (
|
userProfileActivity.results.length > 0 ? (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
|
|
@ -60,14 +60,14 @@ export const ProfileActivity = observer(function ProfileActivity() {
|
||||||
<p className="inline text-13 text-secondary">
|
<p className="inline text-13 text-secondary">
|
||||||
<span className="font-medium text-primary">
|
<span className="font-medium text-primary">
|
||||||
{currentUser?.id === activity.actor_detail?.id
|
{currentUser?.id === activity.actor_detail?.id
|
||||||
? "You"
|
? t("you")
|
||||||
: activity.actor_detail?.display_name}{" "}
|
: activity.actor_detail?.display_name}{" "}
|
||||||
</span>
|
</span>
|
||||||
{activity.field ? (
|
{activity.field ? (
|
||||||
<ActivityMessage activity={activity} showIssue />
|
<ActivityMessage activity={activity} showIssue />
|
||||||
) : (
|
) : (
|
||||||
<span>
|
<span>
|
||||||
created <IssueLink activity={activity} />
|
{t("created").toLowerCase()} <IssueLink activity={activity} />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export function ProfilePriorityDistribution({ userProfile }: Props) {
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
<h3 className="text-16 font-medium">{t("profile.stats.priority_distribution.title")}</h3>
|
<h3 className="text-16 font-medium">{t("profile.stats.priority_distribution.title")}</h3>
|
||||||
{userProfile ? (
|
{userProfile ? (
|
||||||
<Card>
|
<Card className="nodedc-workspace-list-row border-0 bg-transparent p-5">
|
||||||
{userProfile.priority_distribution.length > 0 ? (
|
{userProfile.priority_distribution.length > 0 ? (
|
||||||
<BarChart
|
<BarChart
|
||||||
className="h-[300px] w-full"
|
className="h-[300px] w-full"
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,16 @@ type Props = {
|
||||||
export function ProfileStateDistribution({ stateDistribution, userProfile }: Props) {
|
export function ProfileStateDistribution({ stateDistribution, userProfile }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
if (!userProfile) return null;
|
if (!userProfile) return null;
|
||||||
|
const stateGroupLabels = {
|
||||||
|
unstarted: t("yet_to_start"),
|
||||||
|
started: t("in_progress"),
|
||||||
|
completed: t("completed"),
|
||||||
|
} as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-2">
|
<div className="flex flex-col space-y-2">
|
||||||
<h3 className="text-16 font-medium">{t("profile.stats.state_distribution.title")}</h3>
|
<h3 className="text-16 font-medium">{t("profile.stats.state_distribution.title")}</h3>
|
||||||
<Card className="h-full">
|
<Card className="nodedc-workspace-list-row h-full border-0 bg-transparent p-5">
|
||||||
{userProfile.state_distribution.length > 0 ? (
|
{userProfile.state_distribution.length > 0 ? (
|
||||||
<div className="grid h-[300px] w-full grid-cols-1 gap-x-6 md:grid-cols-2">
|
<div className="grid h-[300px] w-full grid-cols-1 gap-x-6 md:grid-cols-2">
|
||||||
<PieChart
|
<PieChart
|
||||||
|
|
@ -42,7 +47,9 @@ export function ProfileStateDistribution({ stateDistribution, userProfile }: Pro
|
||||||
id: group.state_group,
|
id: group.state_group,
|
||||||
key: group.state_group,
|
key: group.state_group,
|
||||||
value: group.state_count,
|
value: group.state_count,
|
||||||
name: capitalizeFirstLetter(group.state_group),
|
name:
|
||||||
|
stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ??
|
||||||
|
capitalizeFirstLetter(group.state_group),
|
||||||
color: STATE_GROUPS[group.state_group]?.color,
|
color: STATE_GROUPS[group.state_group]?.color,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +76,10 @@ export function ProfileStateDistribution({ stateDistribution, userProfile }: Pro
|
||||||
STATE_GROUPS[group.state_group]?.color ?? "var(--background-color-accent-primary)",
|
STATE_GROUPS[group.state_group]?.color ?? "var(--background-color-accent-primary)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="whitespace-nowrap">{STATE_GROUPS[group.state_group].label}</div>
|
<div className="whitespace-nowrap">
|
||||||
|
{stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ??
|
||||||
|
STATE_GROUPS[group.state_group].label}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{group.state_count}</div>
|
<div>{group.state_count}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { useParams } from "next/navigation";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { UserCirclePropertyIcon, CreateIcon, LayerStackIcon } from "@plane/propel/icons";
|
import { UserCirclePropertyIcon, CreateIcon, LayerStackIcon } from "@plane/propel/icons";
|
||||||
import type { IUserProfileData } from "@plane/types";
|
import type { IUserProfileData } from "@plane/types";
|
||||||
import { Loader, Card, ECardSpacing, ECardDirection } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -51,15 +51,15 @@ export function ProfileStats({ userProfile }: Props) {
|
||||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{overviewCards.map((card) => (
|
{overviewCards.map((card) => (
|
||||||
<Link key={card.route} href={`/${workspaceSlug}/profile/${userId}/${card.route}`}>
|
<Link key={card.route} href={`/${workspaceSlug}/profile/${userId}/${card.route}`}>
|
||||||
<Card direction={ECardDirection.ROW} spacing={ECardSpacing.SM} className="h-full">
|
<div className="nodedc-workspace-stat-card flex h-full items-center gap-4 p-5">
|
||||||
<div className="grid h-11 w-11 place-items-center rounded-sm bg-surface-2">
|
<div className="grid h-11 w-11 place-items-center rounded-full bg-white/6">
|
||||||
<card.icon className="h-5 w-5" />
|
<card.icon className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-13 text-placeholder">{t(card.i18n_title)}</p>
|
<p className="text-13 text-placeholder">{t(card.i18n_title)}</p>
|
||||||
<p className="text-18 font-semibold">{card.value}</p>
|
<p className="text-18 font-semibold">{card.value}</p>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ type Props = {
|
||||||
|
|
||||||
export function ProfileWorkload({ stateDistribution }: Props) {
|
export function ProfileWorkload({ stateDistribution }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const stateGroupLabels = {
|
||||||
|
unstarted: t("yet_to_start"),
|
||||||
|
started: t("in_progress"),
|
||||||
|
completed: t("completed"),
|
||||||
|
} as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|
@ -26,7 +31,11 @@ export function ProfileWorkload({ stateDistribution }: Props) {
|
||||||
{stateDistribution.map((group) => (
|
{stateDistribution.map((group) => (
|
||||||
<div key={group.state_group}>
|
<div key={group.state_group}>
|
||||||
<a>
|
<a>
|
||||||
<Card direction={ECardDirection.ROW} spacing={ECardSpacing.SM}>
|
<Card
|
||||||
|
direction={ECardDirection.ROW}
|
||||||
|
spacing={ECardSpacing.SM}
|
||||||
|
className="nodedc-workspace-stat-card border-0 bg-transparent"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className="my-2 h-3 w-3 rounded-xs"
|
className="my-2 h-3 w-3 rounded-xs"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -35,11 +44,8 @@ export function ProfileWorkload({ stateDistribution }: Props) {
|
||||||
/>
|
/>
|
||||||
<div className="flex-col space-y-1">
|
<div className="flex-col space-y-1">
|
||||||
<span className="text-13 text-placeholder">
|
<span className="text-13 text-placeholder">
|
||||||
{group.state_group === "unstarted"
|
{stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ??
|
||||||
? "Not started"
|
STATE_GROUPS[group.state_group].label}
|
||||||
: group.state_group === "started"
|
|
||||||
? "Working on"
|
|
||||||
: STATE_GROUPS[group.state_group].label}
|
|
||||||
</span>
|
</span>
|
||||||
<p className="text-18 font-semibold">{group.state_count}</p>
|
<p className="text-18 font-semibold">{group.state_count}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
`vertical-scrollbar fixed z-5 scrollbar-md h-full w-full shrink-0 overflow-hidden overflow-y-auto border-l border-subtle bg-surface-1 shadow-raised-200 transition-all md:relative md:w-[300px]`,
|
`vertical-scrollbar fixed z-5 scrollbar-md h-full w-full shrink-0 overflow-hidden overflow-y-auto border-l border-white/6 bg-[rgba(10,10,14,0.92)] shadow-[0_28px_60px_rgba(0,0,0,0.32)] backdrop-blur-2xl transition-all md:relative md:w-[320px]`,
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
style={profileSidebarCollapsed ? { marginLeft: `${window?.innerWidth || 0}px` } : {}}
|
style={profileSidebarCollapsed ? { marginLeft: `${window?.innerWidth || 0}px` } : {}}
|
||||||
|
|
@ -99,6 +99,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<div className="absolute top-3.5 right-3.5">
|
<div className="absolute top-3.5 right-3.5">
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
className="nodedc-toolbar-icon-button rounded-full"
|
||||||
icon={EditIcon}
|
icon={EditIcon}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
toggleProfileSettingsModal({
|
toggleProfileSettingsModal({
|
||||||
|
|
@ -112,18 +113,18 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<CoverImage
|
<CoverImage
|
||||||
src={userData?.cover_image_url ?? undefined}
|
src={userData?.cover_image_url ?? undefined}
|
||||||
alt={userData?.display_name}
|
alt={userData?.display_name}
|
||||||
className="h-[110px] w-full"
|
className="h-[110px] w-full rounded-b-[1.35rem]"
|
||||||
showDefaultWhenEmpty
|
showDefaultWhenEmpty
|
||||||
/>
|
/>
|
||||||
<div className="absolute -bottom-[26px] left-5 h-[52px] w-[52px] rounded-sm">
|
<div className="absolute -bottom-[26px] left-5 h-[52px] w-[52px] rounded-[1rem]">
|
||||||
{userData?.avatar_url && userData?.avatar_url !== "" ? (
|
{userData?.avatar_url && userData?.avatar_url !== "" ? (
|
||||||
<img
|
<img
|
||||||
src={getFileURL(userData?.avatar_url)}
|
src={getFileURL(userData?.avatar_url)}
|
||||||
alt={userData?.display_name}
|
alt={userData?.display_name}
|
||||||
className="h-full w-full rounded-sm object-cover"
|
className="h-full w-full rounded-[1rem] object-cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-[52px] w-[52px] items-center justify-center rounded-sm bg-accent-primary text-on-color capitalize">
|
<div className="flex h-[52px] w-[52px] items-center justify-center rounded-[1rem] bg-accent-primary text-on-color capitalize">
|
||||||
{userData?.first_name?.[0]}
|
{userData?.first_name?.[0]}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -171,7 +172,11 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex flex-shrink-0 items-center gap-2">
|
||||||
{project.assigned_issues > 0 && (
|
{project.assigned_issues > 0 && (
|
||||||
<Tooltip tooltipContent="Completion percentage" position="left" isMobile={isMobile}>
|
<Tooltip
|
||||||
|
tooltipContent={`${t("completed")}: ${completedIssuePercentage}%`}
|
||||||
|
position="left"
|
||||||
|
isMobile={isMobile}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={`rounded-sm px-1 py-0.5 text-11 font-medium ${
|
className={`rounded-sm px-1 py-0.5 text-11 font-medium ${
|
||||||
completedIssuePercentage <= 35
|
completedIssuePercentage <= 35
|
||||||
|
|
@ -234,7 +239,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="h-2.5 w-2.5 rounded-xs bg-[#203b80]" />
|
<div className="h-2.5 w-2.5 rounded-xs bg-[#203b80]" />
|
||||||
Created
|
{t("created")}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{project.created_issues} {t("issues")}
|
{project.created_issues} {t("issues")}
|
||||||
|
|
@ -243,7 +248,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="h-2.5 w-2.5 rounded-xs bg-[#3f76ff]" />
|
<div className="h-2.5 w-2.5 rounded-xs bg-[#3f76ff]" />
|
||||||
Assigned
|
{t("assigned")}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{project.assigned_issues} {t("issues")}
|
{project.assigned_issues} {t("issues")}
|
||||||
|
|
@ -252,7 +257,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="h-2.5 w-2.5 rounded-xs bg-[#f59e0b]" />
|
<div className="h-2.5 w-2.5 rounded-xs bg-[#f59e0b]" />
|
||||||
Due
|
{t("due_date")}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{project.pending_issues} {t("issues")}
|
{project.pending_issues} {t("issues")}
|
||||||
|
|
@ -261,7 +266,7 @@ export const ProfileSidebar = observer(function ProfileSidebar(props: TProfileSi
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="h-2.5 w-2.5 rounded-xs bg-[#16a34a]" />
|
<div className="h-2.5 w-2.5 rounded-xs bg-[#16a34a]" />
|
||||||
Completed
|
{t("completed")}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{project.completed_issues} {t("issues")}
|
{project.completed_issues} {t("issues")}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ export function SettingsContentWrapper(props: Props) {
|
||||||
<div
|
<div
|
||||||
className={cn("py-9", {
|
className={cn("py-9", {
|
||||||
"w-full px-page-x lg:px-12": hugging,
|
"w-full px-page-x lg:px-12": hugging,
|
||||||
"mx-auto w-full max-w-225 px-page-x @min-[58.95rem]:px-0": !hugging, // 58.95rem = max-width(56.25rem) + padding-x(1.35rem * 2)
|
"mx-auto w-full max-w-[74rem] px-page-x @min-[58.95rem]:px-6": !hugging,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{children}
|
<div className="nodedc-workspace-page-shell space-y-7">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ export function SettingsControlItem(props: Props) {
|
||||||
const { control, description, title } = props;
|
const { control, description, title } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col items-start gap-4 py-3 md:flex-row md:items-center md:justify-between md:gap-8">
|
<div className="nodedc-settings-card flex w-full flex-col items-start gap-4 px-5 py-4 md:flex-row md:items-center md:justify-between md:gap-8">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1.5">
|
||||||
<h4 className="text-body-sm-medium text-primary">{title}</h4>
|
<h4 className="text-body-sm-medium text-primary">{title}</h4>
|
||||||
<p className="text-caption-md-regular text-secondary">{description}</p>
|
<p className="max-w-2xl text-caption-md-regular text-tertiary">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0">{control}</div>
|
<div className="shrink-0">{control}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ type Props = {
|
||||||
|
|
||||||
export function SettingsHeading({ className, control, description, title, variant = "h3" }: Props) {
|
export function SettingsHeading({ className, control, description, title, variant = "h3" }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col items-start justify-between gap-4 md:flex-row md:items-center", className)}>
|
<div className={cn("flex flex-col items-start justify-between gap-4 md:flex-row md:items-end", className)}>
|
||||||
<div className="flex flex-col items-start gap-1">
|
<div className="flex flex-col items-start gap-1.5">
|
||||||
{title && (
|
{title && (
|
||||||
<h3
|
<h3
|
||||||
className={cn("text-h3-medium text-primary", {
|
className={cn("text-h3-medium text-primary", {
|
||||||
|
|
@ -30,9 +30,9 @@ export function SettingsHeading({ className, control, description, title, varian
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
)}
|
)}
|
||||||
{description && <p className="text-body-xs-regular text-tertiary">{description}</p>}
|
{description && <p className="max-w-2xl text-body-xs-regular text-tertiary">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
{control}
|
{control && <div className="shrink-0">{control}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,22 @@ export const SettingsMobileNav = observer(function SettingsMobileNav(props: Prop
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-4 border-b border-subtle px-page-x py-3 md:hidden">
|
<div className="flex items-center gap-3 border-b border-white/6 px-page-x py-3 md:hidden">
|
||||||
<div ref={sidebarRef} className="relative z-50 w-fit">
|
<div ref={sidebarRef} className="relative z-50 w-fit">
|
||||||
{!sidebarCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<div className="absolute top-10.5 left-0 z-50">
|
<div className="absolute top-10.5 left-0 z-50">
|
||||||
<HamburgerContent className="max-h-100 rounded-lg border border-subtle pb-3" />
|
<HamburgerContent className="max-h-100 rounded-[1.35rem] pb-3 shadow-2xl" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<IconButton variant="secondary" className="group z-50 shrink-0" icon={Menu} onClick={() => toggleSidebar()} />
|
<IconButton
|
||||||
|
variant="secondary"
|
||||||
|
className="nodedc-toolbar-icon-button group z-50 shrink-0"
|
||||||
|
icon={Menu}
|
||||||
|
onClick={() => toggleSidebar()}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* path */}
|
{/* path */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="nodedc-toolbar-pill flex items-center gap-2 px-3.5">
|
||||||
<ChevronRightIcon className="size-4 text-tertiary" />
|
<ChevronRightIcon className="size-4 text-tertiary" />
|
||||||
<span className="text-13 font-medium text-secondary">{t(activePath)}</span>
|
<span className="text-13 font-medium text-secondary">{t(activePath)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,17 @@ export const ProfileSettingsContent = observer(function ProfileSettingsContent(p
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
className={cn("shrink-0 overflow-y-scroll bg-surface-1", className)}
|
className={cn("shrink-0 overflow-y-scroll bg-transparent", className)}
|
||||||
viewportClassName="px-8 py-9"
|
viewportClassName="px-8 py-9"
|
||||||
scrollType="hover"
|
scrollType="hover"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<Suspense>
|
<div className="nodedc-workspace-page-shell space-y-7">
|
||||||
<PageComponent />
|
<Suspense>
|
||||||
</Suspense>
|
<PageComponent />
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,21 @@ export const ProfileSettingsSidebarHeader = observer(function ProfileSettingsSid
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex shrink-0 items-center gap-2">
|
<div className="nodedc-settings-card flex shrink-0 items-center gap-3 px-4 py-3">
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<Avatar
|
<Avatar
|
||||||
src={getFileURL(currentUser?.avatar_url ?? "")}
|
src={getFileURL(currentUser?.avatar_url ?? "")}
|
||||||
name={currentUser?.display_name}
|
name={currentUser?.display_name}
|
||||||
size={32}
|
size={40}
|
||||||
shape="circle"
|
shape="circle"
|
||||||
className="text-16"
|
className="text-16"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate">
|
<div className="truncate">
|
||||||
<p className="truncate text-body-sm-medium">
|
<p className="truncate text-body-sm-medium text-primary">
|
||||||
{currentUser?.first_name} {currentUser?.last_name}
|
{currentUser?.first_name} {currentUser?.last_name}
|
||||||
</p>
|
</p>
|
||||||
<p className="truncate text-caption-md-regular">{currentUser?.email}</p>
|
<p className="truncate text-caption-md-medium text-tertiary">{currentUser?.email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export const ProfileSettingsSidebarItemCategories = observer(function ProfileSet
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={category} className="shrink-0">
|
<div key={category} className="shrink-0">
|
||||||
<div className="p-2 text-caption-md-medium text-tertiary capitalize">
|
<div className="px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.18em] text-tertiary">
|
||||||
{t(PROFILE_CATEGORY_I18N_KEYS[category])}
|
{t(PROFILE_CATEGORY_I18N_KEYS[category])}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export function ProfileSettingsSidebarRoot(props: Props) {
|
||||||
scrollType="hover"
|
scrollType="hover"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
size="sm"
|
size="sm"
|
||||||
rootClassName={cn("shrink-0 overflow-y-scroll border-r border-r-subtle bg-surface-2 px-3 py-4", className)}
|
rootClassName={cn("nodedc-settings-sidebar-shell shrink-0 overflow-y-scroll px-3 py-4", className)}
|
||||||
>
|
>
|
||||||
<ProfileSettingsSidebarHeader />
|
<ProfileSettingsSidebarHeader />
|
||||||
<ProfileSettingsSidebarItemCategories activeTab={activeTab} updateActiveTab={updateActiveTab} />
|
<ProfileSettingsSidebarItemCategories activeTab={activeTab} updateActiveTab={updateActiveTab} />
|
||||||
|
|
|
||||||
|
|
@ -41,22 +41,23 @@ export const ProjectSettingsSidebarHeader = observer(function ProjectSettingsSid
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<div className="flex items-center gap-1 py-3 pr-5 pl-4 text-body-md-medium">
|
<div className="flex items-center gap-2 px-3 pb-3 text-body-md-medium">
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="secondary"
|
||||||
size="base"
|
size="base"
|
||||||
icon={ArrowLeft}
|
icon={ArrowLeft}
|
||||||
|
className="nodedc-toolbar-icon-button"
|
||||||
onClick={() => router.push(`/${currentWorkspace?.slug}/projects/${projectId}/issues/`)}
|
onClick={() => router.push(`/${currentWorkspace?.slug}/projects/${projectId}/issues/`)}
|
||||||
/>
|
/>
|
||||||
<p>{t("project_settings_label")}</p>
|
<p>{t("project_settings_label")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1.5 flex items-center gap-2 truncate px-5 py-0.5">
|
<div className="nodedc-settings-card flex items-center gap-3 px-4 py-3">
|
||||||
<div className="grid size-8 shrink-0 place-items-center rounded bg-layer-2">
|
<div className="grid size-10 shrink-0 place-items-center rounded-[1rem] bg-white/6">
|
||||||
<Logo logo={projectDetails?.logo_props} size={20} />
|
<Logo logo={projectDetails?.logo_props} size={20} />
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate">
|
<div className="truncate">
|
||||||
<p className="truncate text-body-sm-medium">{projectDetails?.name}</p>
|
<p className="truncate text-body-sm-medium text-primary">{projectDetails?.name}</p>
|
||||||
<p className="truncate text-caption-md-regular">{t(ROLE_DETAILS[currentProjectRole].i18n_title)}</p>
|
<p className="truncate text-caption-md-medium text-tertiary">{t(ROLE_DETAILS[currentProjectRole].i18n_title)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export const ProjectSettingsSidebarItemCategories = observer(function ProjectSet
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 flex flex-col divide-y divide-subtle px-3">
|
<div className="mt-4 flex flex-col divide-y divide-white/6">
|
||||||
{PROJECT_SETTINGS_CATEGORIES.map((category) => {
|
{PROJECT_SETTINGS_CATEGORIES.map((category) => {
|
||||||
const categoryItems = GROUPED_PROJECT_SETTINGS[category];
|
const categoryItems = GROUPED_PROJECT_SETTINGS[category];
|
||||||
const accessibleItems = categoryItems.filter((item) =>
|
const accessibleItems = categoryItems.filter((item) =>
|
||||||
|
|
@ -51,8 +51,8 @@ export const ProjectSettingsSidebarItemCategories = observer(function ProjectSet
|
||||||
if (accessibleItems.length === 0) return null;
|
if (accessibleItems.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={category} className="shrink-0 py-3 first:pt-0 last:pb-0">
|
<div key={category} className="shrink-0 py-3.5 first:pt-0 last:pb-0">
|
||||||
<div className="p-2 text-caption-md-medium text-tertiary capitalize">
|
<div className="px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.18em] text-tertiary">
|
||||||
{t(PROJECT_CATEGORY_I18N_KEYS[category])}
|
{t(PROJECT_CATEGORY_I18N_KEYS[category])}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export function ProjectSettingsSidebarRoot(props: Props) {
|
||||||
scrollType="hover"
|
scrollType="hover"
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
size="sm"
|
size="sm"
|
||||||
rootClassName="shrink-0 animate-fade-in h-full w-[250px] bg-surface-1 border-r border-r-subtle overflow-y-scroll"
|
rootClassName="nodedc-settings-sidebar-shell h-full w-[296px] shrink-0 animate-fade-in overflow-y-scroll px-3 py-4"
|
||||||
viewportClassName="pb-5"
|
viewportClassName="pb-5"
|
||||||
>
|
>
|
||||||
<ProjectSettingsSidebarHeader projectId={projectId} />
|
<ProjectSettingsSidebarHeader projectId={projectId} />
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,9 @@ export function SettingsSidebarItem(props: Props) {
|
||||||
const { as, isActive, label } = props;
|
const { as, isActive, label } = props;
|
||||||
// common class
|
// common class
|
||||||
const className = cn(
|
const className = cn(
|
||||||
"flex items-center gap-2 rounded-lg px-2 py-1.5 text-left text-body-sm-medium text-secondary transition-colors",
|
"nodedc-settings-sidebar-item flex items-center gap-3 text-left text-body-sm-medium transition-colors",
|
||||||
{
|
{
|
||||||
"bg-layer-transparent-selected text-primary": isActive,
|
"text-primary": isActive,
|
||||||
"hover:bg-layer-transparent-hover": !isActive,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// common content
|
// common content
|
||||||
|
|
@ -46,14 +45,14 @@ export function SettingsSidebarItem(props: Props) {
|
||||||
|
|
||||||
if (as === "button") {
|
if (as === "button") {
|
||||||
return (
|
return (
|
||||||
<button type="button" className={className} onClick={props.onClick}>
|
<button type="button" className={className} onClick={props.onClick} data-active={isActive ? "true" : "false"}>
|
||||||
{content}
|
{content}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link className={className} href={props.href}>
|
<Link className={className} href={props.href} data-active={isActive ? "true" : "false"}>
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -35,26 +35,27 @@ export const WorkspaceSettingsSidebarHeader = observer(function WorkspaceSetting
|
||||||
if (!currentWorkspaceRole) return null;
|
if (!currentWorkspaceRole) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<div className="flex items-center gap-1 py-3 pr-5 pl-4 text-body-md-medium">
|
<div className="flex items-center gap-2 px-3 pb-3 text-body-md-medium">
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="secondary"
|
||||||
size="base"
|
size="base"
|
||||||
icon={ArrowLeft}
|
icon={ArrowLeft}
|
||||||
|
className="nodedc-toolbar-icon-button"
|
||||||
onClick={() => router.push(`/${currentWorkspace?.slug}/`)}
|
onClick={() => router.push(`/${currentWorkspace?.slug}/`)}
|
||||||
/>
|
/>
|
||||||
<p>{t("workspace_settings.label")}</p>
|
<p>{t("workspace_settings.label")}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1.5 flex items-center justify-between gap-2 px-5 py-0.5">
|
<div className="nodedc-settings-card flex items-center justify-between gap-3 px-4 py-3">
|
||||||
<div className="flex items-center gap-2 truncate">
|
<div className="flex items-center gap-3 truncate">
|
||||||
<WorkspaceLogo
|
<WorkspaceLogo
|
||||||
logo={currentWorkspace?.logo_url}
|
logo={currentWorkspace?.logo_url}
|
||||||
name={currentWorkspace?.name}
|
name={currentWorkspace?.name}
|
||||||
classNames="shrink-0 size-8 border border-subtle"
|
classNames="shrink-0 size-10 rounded-[1rem] border border-white/8"
|
||||||
/>
|
/>
|
||||||
<div className="truncate">
|
<div className="truncate">
|
||||||
<p className="truncate text-body-sm-medium">{currentWorkspace?.name}</p>
|
<p className="truncate text-body-sm-medium text-primary">{currentWorkspace?.name}</p>
|
||||||
<p className="truncate text-caption-md-regular">{t(ROLE_DETAILS[currentWorkspaceRole].i18n_title)}</p>
|
<p className="truncate text-caption-md-medium text-tertiary">{t(ROLE_DETAILS[currentWorkspaceRole].i18n_title)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export const WorkspaceSettingsSidebarItemCategories = observer(function Workspac
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 flex flex-col divide-y divide-subtle px-3">
|
<div className="mt-4 flex flex-col divide-y divide-white/6">
|
||||||
{WORKSPACE_SETTINGS_CATEGORIES.map((category) => {
|
{WORKSPACE_SETTINGS_CATEGORIES.map((category) => {
|
||||||
const categoryItems = GROUPED_WORKSPACE_SETTINGS[category];
|
const categoryItems = GROUPED_WORKSPACE_SETTINGS[category];
|
||||||
const accessibleItems = categoryItems.filter(
|
const accessibleItems = categoryItems.filter(
|
||||||
|
|
@ -42,8 +42,10 @@ export const WorkspaceSettingsSidebarItemCategories = observer(function Workspac
|
||||||
if (accessibleItems.length === 0) return null;
|
if (accessibleItems.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={category} className="shrink-0 py-3 first:pt-0 last:pb-0">
|
<div key={category} className="shrink-0 py-3.5 first:pt-0 last:pb-0">
|
||||||
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t(category)}</div>
|
<div className="px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.18em] text-tertiary">
|
||||||
|
{t(category)}
|
||||||
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{accessibleItems.map((item) => {
|
{accessibleItems.map((item) => {
|
||||||
const isItemActive =
|
const isItemActive =
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export function WorkspaceSettingsSidebarRoot(props: Props) {
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
size="sm"
|
size="sm"
|
||||||
rootClassName={cn(
|
rootClassName={cn(
|
||||||
"h-full w-[250px] shrink-0 animate-fade-in overflow-y-scroll border-r border-r-subtle bg-surface-1",
|
"nodedc-settings-sidebar-shell h-full w-[296px] shrink-0 animate-fade-in overflow-y-scroll px-3 py-4",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import useSWR from "swr";
|
||||||
import { StickyNote as StickyIcon } from "lucide-react";
|
import { StickyNote as StickyIcon } from "lucide-react";
|
||||||
// plane hooks
|
// plane hooks
|
||||||
import { useOutsideClickDetector } from "@plane/hooks";
|
import { useOutsideClickDetector } from "@plane/hooks";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
// plane ui
|
// plane ui
|
||||||
import { RecentStickyIcon, StickyNoteIcon, PlusIcon, CloseIcon } from "@plane/propel/icons";
|
import { RecentStickyIcon, StickyNoteIcon, PlusIcon, CloseIcon } from "@plane/propel/icons";
|
||||||
import { Tooltip } from "@plane/propel/tooltip";
|
import { Tooltip } from "@plane/propel/tooltip";
|
||||||
|
|
@ -31,6 +32,7 @@ export const StickyActionBar = observer(function StickyActionBar() {
|
||||||
const [showRecentSticky, setShowRecentSticky] = useState(false);
|
const [showRecentSticky, setShowRecentSticky] = useState(false);
|
||||||
// navigation
|
// navigation
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
// refs
|
// refs
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
// store hooks
|
// store hooks
|
||||||
|
|
@ -61,9 +63,9 @@ export const StickyActionBar = observer(function StickyActionBar() {
|
||||||
<div
|
<div
|
||||||
className={`flex origin-bottom flex-col gap-2 transition-all duration-300 ease-in-out ${isExpanded ? "mb-2 scale-y-100 opacity-100 " : "h-0 scale-y-0 opacity-0"}`}
|
className={`flex origin-bottom flex-col gap-2 transition-all duration-300 ease-in-out ${isExpanded ? "mb-2 scale-y-100 opacity-100 " : "h-0 scale-y-0 opacity-0"}`}
|
||||||
>
|
>
|
||||||
<Tooltip tooltipContent="All stickies" isMobile={false} position="left">
|
<Tooltip tooltipContent={t("stickies.all")} isMobile={false} position="left">
|
||||||
<button
|
<button
|
||||||
className="btn btn--icon shadow-sm flex h-10 w-10 items-center justify-center rounded-full bg-surface-1"
|
className="nodedc-toolbar-icon-button grid h-10 w-10 place-items-center rounded-full"
|
||||||
onClick={() => toggleAllStickiesModal(true)}
|
onClick={() => toggleAllStickiesModal(true)}
|
||||||
>
|
>
|
||||||
<RecentStickyIcon className="size-5 rotate-90 text-tertiary" />
|
<RecentStickyIcon className="size-5 rotate-90 text-tertiary" />
|
||||||
|
|
@ -92,7 +94,7 @@ export const StickyActionBar = observer(function StickyActionBar() {
|
||||||
disabled={showRecentSticky}
|
disabled={showRecentSticky}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="btn btn--icon shadow-sm flex h-10 w-10 items-center justify-center rounded-full bg-surface-1"
|
className="nodedc-toolbar-icon-button grid h-10 w-10 place-items-center rounded-full"
|
||||||
onClick={() => setShowRecentSticky(true)}
|
onClick={() => setShowRecentSticky(true)}
|
||||||
style={{ color: recentStickyBackgroundColor }}
|
style={{ color: recentStickyBackgroundColor }}
|
||||||
>
|
>
|
||||||
|
|
@ -100,9 +102,9 @@ export const StickyActionBar = observer(function StickyActionBar() {
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip tooltipContent="Add sticky" isMobile={false} position="left">
|
<Tooltip tooltipContent={t("stickies.add")} isMobile={false} position="left">
|
||||||
<button
|
<button
|
||||||
className="btn btn--icon shadow-sm flex h-10 w-10 items-center justify-center rounded-full bg-surface-1"
|
className="nodedc-toolbar-icon-button grid h-10 w-10 place-items-center rounded-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateActiveStickyId("");
|
updateActiveStickyId("");
|
||||||
toggleShowNewSticky(true);
|
toggleShowNewSticky(true);
|
||||||
|
|
@ -115,7 +117,7 @@ export const StickyActionBar = observer(function StickyActionBar() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`btn btn--icon shadow-sm flex h-10 w-10 items-center justify-center rounded-full bg-surface-1 transition-transform duration-300 ${isExpanded ? "rotate-180" : ""}`}
|
className={`nodedc-toolbar-icon-button grid h-10 w-10 place-items-center rounded-full transition-transform duration-300 ${isExpanded ? "rotate-180" : ""}`}
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
>
|
>
|
||||||
{isExpanded ? (
|
{isExpanded ? (
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,7 @@ export const StickiesTruncated = observer(function StickiesTruncated(props: Stic
|
||||||
customButton={
|
customButton={
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspaceSlug}/stickies`}
|
href={`/${workspaceSlug}/stickies`}
|
||||||
className={cn(
|
className={cn("nodedc-toolbar-pill flex w-full items-center justify-center gap-2 text-13 font-medium")}
|
||||||
"w-full gap-1 bg-surface-2/20 text-13 font-medium text-accent-primary transition-opacity duration-300"
|
|
||||||
)}
|
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
{t("show_all")}
|
{t("show_all")}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export const StickySearch = observer(function StickySearch() {
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="-mr-2"
|
className="nodedc-toolbar-icon-button"
|
||||||
icon={SearchIcon}
|
icon={SearchIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsSearchOpen(true);
|
setIsSearchOpen(true);
|
||||||
|
|
@ -68,9 +68,9 @@ export const StickySearch = observer(function StickySearch() {
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto flex w-0 items-center justify-start gap-1 overflow-hidden rounded-md border border-transparent text-placeholder opacity-0 transition-[width] ease-linear",
|
"nodedc-toolbar-pill ml-auto flex w-0 items-center justify-start gap-2 overflow-hidden px-0 text-placeholder opacity-0 transition-[width] ease-linear",
|
||||||
{
|
{
|
||||||
"w-30 border-subtle px-2.5 py-1.5 opacity-100 md:w-64": isSearchOpen,
|
"w-30 px-3 opacity-100 md:w-64": isSearchOpen,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -89,7 +89,7 @@ export const StickySearch = observer(function StickySearch() {
|
||||||
{isSearchOpen && (
|
{isSearchOpen && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="grid place-items-center"
|
className="grid h-7 w-7 flex-shrink-0 place-items-center rounded-full text-secondary transition-colors hover:bg-white/6 hover:text-primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateSearchQuery("");
|
updateSearchQuery("");
|
||||||
setIsSearchOpen(false);
|
setIsSearchOpen(false);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
// plane ui
|
// plane ui
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
|
import { Button } from "@plane/propel/button";
|
||||||
import { RecentStickyIcon, PlusIcon, CloseIcon } from "@plane/propel/icons";
|
import { RecentStickyIcon, PlusIcon, CloseIcon } from "@plane/propel/icons";
|
||||||
// hooks
|
// hooks
|
||||||
import { useSticky } from "@/hooks/use-stickies";
|
import { useSticky } from "@/hooks/use-stickies";
|
||||||
|
|
@ -24,6 +26,7 @@ export const Stickies = observer(function Stickies(props: TProps) {
|
||||||
const { handleClose } = props;
|
const { handleClose } = props;
|
||||||
// navigation
|
// navigation
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { creatingSticky, toggleShowNewSticky } = useSticky();
|
const { creatingSticky, toggleShowNewSticky } = useSticky();
|
||||||
// sticky operations
|
// sticky operations
|
||||||
|
|
@ -36,20 +39,23 @@ export const Stickies = observer(function Stickies(props: TProps) {
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div className="flex items-center gap-2 text-secondary">
|
<div className="flex items-center gap-2 text-secondary">
|
||||||
<RecentStickyIcon className="size-5 flex-shrink-0 rotate-90" />
|
<RecentStickyIcon className="size-5 flex-shrink-0 rotate-90" />
|
||||||
<p className="text-18 font-medium">Your stickies</p>
|
<p className="text-18 font-medium">{t("stickies.title")}</p>
|
||||||
</div>
|
</div>
|
||||||
{/* actions */}
|
{/* actions */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<StickySearch />
|
<StickySearch />
|
||||||
<button
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="lg"
|
||||||
|
className="nodedc-toolbar-primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleShowNewSticky(true);
|
toggleShowNewSticky(true);
|
||||||
stickyOperations.create();
|
stickyOperations.create();
|
||||||
}}
|
}}
|
||||||
className="my-auto flex gap-1 text-13 font-medium text-accent-primary"
|
|
||||||
disabled={creatingSticky}
|
disabled={creatingSticky}
|
||||||
|
prependIcon={<PlusIcon className="size-4" />}
|
||||||
>
|
>
|
||||||
<PlusIcon className="my-auto size-4" /> <span>Add sticky</span>
|
<span>{t("stickies.add")}</span>
|
||||||
{creatingSticky && (
|
{creatingSticky && (
|
||||||
<div className="ml-2 flex items-center justify-center">
|
<div className="ml-2 flex items-center justify-center">
|
||||||
<div
|
<div
|
||||||
|
|
@ -59,12 +65,12 @@ export const Stickies = observer(function Stickies(props: TProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
</Button>
|
||||||
{handleClose && (
|
{handleClose && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
className="my-auto grid flex-shrink-0 place-items-center rounded-sm p-1 text-tertiary transition-colors hover:bg-layer-1 hover:text-primary"
|
className="nodedc-toolbar-icon-button my-auto grid h-10 w-10 flex-shrink-0 place-items-center rounded-full"
|
||||||
>
|
>
|
||||||
<CloseIcon className="size-4 text-placeholder" />
|
<CloseIcon className="size-4 text-placeholder" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -80,14 +80,17 @@ export const StickyNote = observer(function StickyNote(props: TProps) {
|
||||||
handleClose={() => setIsDeleteModalOpen(false)}
|
handleClose={() => setIsDeleteModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn("group/sticky flex h-fit w-full flex-col overflow-y-scroll rounded-sm", className)}
|
className={cn(
|
||||||
|
"group/sticky flex h-fit w-full flex-col overflow-y-scroll rounded-[1.35rem] shadow-[0_16px_36px_rgba(0,0,0,0.18)]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* {isStickiesPage && <StickyItemDragHandle isDragging={false} />}{" "} */}
|
{/* {isStickiesPage && <StickyItemDragHandle isDragging={false} />}{" "} */}
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<button type="button" className="flex flex-shrink-0 justify-end p-2.5" onClick={onClose}>
|
<button type="button" className="flex flex-shrink-0 justify-end p-3" onClick={onClose}>
|
||||||
<Minimize2 className="size-4" />
|
<Minimize2 className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { useParams } from "next/navigation";
|
||||||
|
|
||||||
// plane imports
|
// plane imports
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
|
import { Button } from "@plane/propel/button";
|
||||||
import { PlusIcon } from "@plane/propel/icons";
|
import { PlusIcon } from "@plane/propel/icons";
|
||||||
// hooks
|
// hooks
|
||||||
import { useSticky } from "@/hooks/use-stickies";
|
import { useSticky } from "@/hooks/use-stickies";
|
||||||
|
|
@ -35,15 +36,17 @@ export const StickiesWidget = observer(function StickiesWidget() {
|
||||||
{/* actions */}
|
{/* actions */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<StickySearch />
|
<StickySearch />
|
||||||
<button
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="lg"
|
||||||
|
className="nodedc-toolbar-primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleShowNewSticky(true);
|
toggleShowNewSticky(true);
|
||||||
stickyOperations.create();
|
stickyOperations.create();
|
||||||
}}
|
}}
|
||||||
className="my-auto flex gap-1 text-13 font-medium text-accent-primary"
|
|
||||||
disabled={creatingSticky}
|
disabled={creatingSticky}
|
||||||
|
prependIcon={<PlusIcon className="size-4" />}
|
||||||
>
|
>
|
||||||
<PlusIcon className="my-auto size-4" />
|
|
||||||
<span>{t("stickies.add")}</span>
|
<span>{t("stickies.add")}</span>
|
||||||
{creatingSticky && (
|
{creatingSticky && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -52,7 +55,7 @@ export const StickiesWidget = observer(function StickiesWidget() {
|
||||||
aria-label="loading"
|
aria-label="loading"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="-mx-2">
|
<div className="-mx-2">
|
||||||
|
|
|
||||||
|
|
@ -717,6 +717,49 @@
|
||||||
-webkit-backdrop-filter: blur(18px);
|
-webkit-backdrop-filter: blur(18px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-sidebar-shell {
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow:
|
||||||
|
inset -1px 0 0 rgba(255, 255, 255, 0.06),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.015) !important;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.024) 0%, rgba(255, 255, 255, 0.008) 100%),
|
||||||
|
rgba(8, 8, 11, 0.9) !important;
|
||||||
|
-webkit-backdrop-filter: blur(28px);
|
||||||
|
backdrop-filter: blur(28px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-sidebar-item {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.1rem !important;
|
||||||
|
background: transparent !important;
|
||||||
|
color: rgba(255, 255, 255, 0.76) !important;
|
||||||
|
padding-inline: 0.95rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-sidebar-item:hover {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.014) 100%),
|
||||||
|
rgba(255, 255, 255, 0.028) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-sidebar-item[data-active="true"] {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.016) 100%),
|
||||||
|
rgba(255, 255, 255, 0.042) !important;
|
||||||
|
color: rgb(var(--nodedc-accent-rgb)) !important;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(var(--nodedc-accent-rgb), 0.24) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-sidebar-item[data-active="true"] * {
|
||||||
|
color: rgb(var(--nodedc-accent-rgb)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-settings-field {
|
.nodedc-settings-field {
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
|
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
|
||||||
|
|
|
||||||
|
|
@ -477,6 +477,7 @@ export default {
|
||||||
assignee: "Assignee",
|
assignee: "Assignee",
|
||||||
assignees: "Assignees",
|
assignees: "Assignees",
|
||||||
you: "You",
|
you: "You",
|
||||||
|
commented: "Commented",
|
||||||
labels: "Labels",
|
labels: "Labels",
|
||||||
create_new_label: "Create new label",
|
create_new_label: "Create new label",
|
||||||
start_date: "Start date",
|
start_date: "Start date",
|
||||||
|
|
|
||||||
|
|
@ -633,6 +633,7 @@ export default {
|
||||||
assignee: "Назначенный",
|
assignee: "Назначенный",
|
||||||
assignees: "Назначенные",
|
assignees: "Назначенные",
|
||||||
you: "Вы",
|
you: "Вы",
|
||||||
|
commented: "Прокомментировал",
|
||||||
labels: "Метки",
|
labels: "Метки",
|
||||||
create_new_label: "Создать новую метку",
|
create_new_label: "Создать новую метку",
|
||||||
start_date: "Дата начала",
|
start_date: "Дата начала",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue