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