[WEB-2273] Chore: page alignments (#5505)
* chore: headers + common containers * fix: filters code splitting * fix: home header * fix: header changes * chore: page alignments fixed * fix: uncommented filters * fix: used enums * fix: cards + filters * fix: enum changes * fix: reverted package changes * fix: reverted package changes * fix: Card + tags seperated + naming fixed * fix: card + tags seperated + naming fixed * fix: mobile headers fixed partially * fix: build errors + minor css * fix: checkbox spacing * fix: review changes * fix: lint errors * fix: minor review changes
This commit is contained in:
parent
c78b2344b8
commit
87dbb9b888
181 changed files with 1323 additions and 1122 deletions
|
|
@ -5,6 +5,7 @@ import useSWR from "swr";
|
|||
import { IAnalyticsParams } from "@plane/types";
|
||||
// services
|
||||
// components
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { CustomAnalyticsSelectBar, CustomAnalyticsMainContent, CustomAnalyticsSidebar } from "@/components/analytics";
|
||||
// types
|
||||
// fetch-keys
|
||||
|
|
@ -53,7 +54,7 @@ export const CustomAnalytics: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<div className={cn("relative flex h-full w-full overflow-hidden", isProjectLevel ? "flex-col-reverse" : "")}>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<ContentWrapper>
|
||||
<CustomAnalyticsSelectBar
|
||||
control={control}
|
||||
setValue={setValue}
|
||||
|
|
@ -67,7 +68,7 @@ export const CustomAnalytics: React.FC<Props> = observer((props) => {
|
|||
params={params}
|
||||
fullScreen={fullScreen}
|
||||
/>
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export const CustomAnalyticsMainContent: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
)
|
||||
) : (
|
||||
<Loader className="space-y-6 p-5">
|
||||
<Loader className="space-y-6">
|
||||
<Loader.Item height="300px" />
|
||||
<Loader className="space-y-4">
|
||||
<Loader.Item height="30px" />
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={`grid items-center gap-4 px-5 py-2.5 ${
|
||||
className={`grid items-center gap-4 pb-2.5 ${
|
||||
isProjectLevel ? "grid-cols-1 sm:grid-cols-3" : "grid-cols-2"
|
||||
} ${fullScreen ? "md:py-5 lg:grid-cols-4" : ""}`}
|
||||
} ${fullScreen ? "md:pb-5 lg:grid-cols-4" : ""}`}
|
||||
>
|
||||
{!isProjectLevel && (
|
||||
<div>
|
||||
<h6 className="text-xs text-custom-text-200">Project</h6>
|
||||
<h6 className="text-xs text-custom-text-200 mb-2">Project</h6>
|
||||
<Controller
|
||||
name="project"
|
||||
control={control}
|
||||
|
|
@ -52,7 +52,7 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h6 className="text-xs text-custom-text-200">Measure (y-axis)</h6>
|
||||
<h6 className="text-xs text-custom-text-200 mb-2">Measure (y-axis)</h6>
|
||||
<Controller
|
||||
name="y_axis"
|
||||
control={control}
|
||||
|
|
@ -60,7 +60,7 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="text-xs text-custom-text-200">Dimension (x-axis)</h6>
|
||||
<h6 className="text-xs text-custom-text-200 mb-2">Dimension (x-axis)</h6>
|
||||
<Controller
|
||||
name="x_axis"
|
||||
control={control}
|
||||
|
|
@ -79,7 +79,7 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="text-xs text-custom-text-200">Group</h6>
|
||||
<h6 className="text-xs text-custom-text-200 mb-2">Group</h6>
|
||||
<Controller
|
||||
name="segment"
|
||||
control={control}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, params, yAxisKey }) => (
|
||||
<div className="w-full overflow-hidden overflow-x-auto">
|
||||
<div className="w-full overflow-hidden overflow-x-auto absolute left-0">
|
||||
<table className="w-full overflow-hidden divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
|
||||
<thead className="bg-custom-background-80">
|
||||
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium">
|
||||
<th scope="col" className="px-page-x py-3 text-left font-medium">
|
||||
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
|
||||
</th>
|
||||
{params.segment ? (
|
||||
|
|
@ -31,7 +31,7 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
<th
|
||||
key={`segment-${key}`}
|
||||
scope="col"
|
||||
className={`px-2.5 py-3 text-left font-medium ${
|
||||
className={`px-page-x py-3 text-left font-medium ${
|
||||
params.segment === "priority" || params.segment === "state__group" ? "capitalize" : ""
|
||||
}`}
|
||||
>
|
||||
|
|
@ -51,7 +51,7 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
</th>
|
||||
))
|
||||
) : (
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium sm:pr-0">
|
||||
<th scope="col" className="px-page-x py-3 text-left font-medium sm:pr-0">
|
||||
{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === params.y_axis)?.label}
|
||||
</th>
|
||||
)}
|
||||
|
|
@ -60,7 +60,7 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
<tbody className="divide-y divide-custom-border-200">
|
||||
{barGraphData.data.map((item, index) => (
|
||||
<tr key={`table-row-${index}`} className="divide-x divide-custom-border-200 text-xs text-custom-text-200">
|
||||
<td className="px-2.5 py-2">
|
||||
<td className="px-page-x py-2">
|
||||
<div className="relative flex items-center gap-2 w-full overflow-hidden">
|
||||
<div className="flex-shrink-0 h-3 w-3 rounded overflow-hidden">
|
||||
{params.x_axis === "priority" ? (
|
||||
|
|
@ -90,12 +90,12 @@ export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
</td>
|
||||
{params.segment ? (
|
||||
barGraphData.xAxisKeys.map((key, index) => (
|
||||
<td key={`segment-value-${index}`} className="whitespace-nowrap px-2.5 py-2 sm:pr-0">
|
||||
<td key={`segment-value-${index}`} className="whitespace-nowrap px-page-x py-2 sm:pr-0">
|
||||
{item[key] ?? 0}
|
||||
</td>
|
||||
))
|
||||
) : (
|
||||
<td className="whitespace-nowrap px-2.5 py-2 sm:pr-0">{item[yAxisKey]}</td>
|
||||
<td className="whitespace-nowrap px-page-x py-2 sm:pr-0">{item[yAxisKey]}</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// types
|
||||
import { IDefaultAnalyticsResponse, TStateGroups } from "@plane/types";
|
||||
// constants
|
||||
import { Card } from "@plane/ui";
|
||||
import { STATE_GROUPS } from "@/constants/state";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -8,7 +9,7 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsDemand: React.FC<Props> = ({ defaultAnalytics }) => (
|
||||
<div className="space-y-3 rounded-[10px] border border-custom-border-200 p-3">
|
||||
<Card>
|
||||
<div>
|
||||
<h4 className="text-base font-medium text-custom-text-100">Total open tasks</h4>
|
||||
<h3 className="mt-1 text-xl font-semibold">{defaultAnalytics.open_issues}</h3>
|
||||
|
|
@ -47,5 +48,5 @@ export const AnalyticsDemand: React.FC<Props> = ({ defaultAnalytics }) => (
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// ui
|
||||
import { Card } from "@plane/ui";
|
||||
import { ProfileEmptyState } from "@/components/ui";
|
||||
// image
|
||||
import emptyUsers from "@/public/empty-state/empty_users.svg";
|
||||
|
|
@ -18,7 +19,7 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsLeaderBoard: React.FC<Props> = ({ users, title, emptyStateMessage, workspaceSlug }) => (
|
||||
<div className="rounded-[10px] border border-custom-border-200 p-3">
|
||||
<Card>
|
||||
<h6 className="text-base font-medium">{title}</h6>
|
||||
{users.length > 0 ? (
|
||||
<div className="mt-3 space-y-3">
|
||||
|
|
@ -57,5 +58,5 @@ export const AnalyticsLeaderBoard: React.FC<Props> = ({ users, title, emptyState
|
|||
<ProfileEmptyState title="No Data yet" description={emptyStateMessage} image={emptyUsers} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// ui
|
||||
import { Button, Loader } from "@plane/ui";
|
||||
import { Button, ContentWrapper, Loader } from "@plane/ui";
|
||||
// components
|
||||
import { AnalyticsDemand, AnalyticsLeaderBoard, AnalyticsScope, AnalyticsYearWiseIssues } from "@/components/analytics";
|
||||
// fetch-keys
|
||||
|
|
@ -50,7 +50,7 @@ export const ScopeAndDemand: React.FC<Props> = (props) => {
|
|||
<>
|
||||
{!defaultAnalyticsError ? (
|
||||
defaultAnalytics ? (
|
||||
<div className="h-full overflow-y-auto p-5 text-sm vertical-scrollbar scrollbar-lg">
|
||||
<ContentWrapper>
|
||||
<div className={`grid grid-cols-1 gap-5 ${fullScreen ? "md:grid-cols-2" : ""}`}>
|
||||
<AnalyticsDemand defaultAnalytics={defaultAnalytics} />
|
||||
<AnalyticsScope
|
||||
|
|
@ -87,7 +87,7 @@ export const ScopeAndDemand: React.FC<Props> = (props) => {
|
|||
<AnalyticsYearWiseIssues defaultAnalytics={defaultAnalytics} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
) : (
|
||||
<Loader className="grid grid-cols-1 gap-5 p-5 lg:grid-cols-2">
|
||||
<Loader.Item height="250px" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// ui
|
||||
import { IDefaultAnalyticsUser } from "@plane/types";
|
||||
import { Card } from "@plane/ui";
|
||||
import { BarGraph, ProfileEmptyState } from "@/components/ui";
|
||||
// image
|
||||
import emptyBarGraph from "@/public/empty-state/empty_bar_graph.svg";
|
||||
|
|
@ -11,7 +12,7 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsScope: React.FC<Props> = ({ pendingUnAssignedIssuesUser, pendingAssignedIssues }) => (
|
||||
<div className="rounded-[10px] border border-custom-border-200 p-3">
|
||||
<Card>
|
||||
<div className="divide-y divide-custom-border-200">
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -87,5 +88,5 @@ export const AnalyticsScope: React.FC<Props> = ({ pendingUnAssignedIssuesUser, p
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// ui
|
||||
import { IDefaultAnalyticsResponse } from "@plane/types";
|
||||
import { Card } from "@plane/ui";
|
||||
import { LineGraph, ProfileEmptyState } from "@/components/ui";
|
||||
// image
|
||||
import { MONTHS_LIST } from "@/constants/calendar";
|
||||
|
|
@ -12,8 +13,8 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) => (
|
||||
<div className="rounded-[10px] border border-custom-border-200 py-3">
|
||||
<h1 className="px-3 text-base font-medium">Issues closed in a year</h1>
|
||||
<Card>
|
||||
<h1 className="py-3 text-base font-medium">Issues closed in a year</h1>
|
||||
{defaultAnalytics.issue_completed_month_wise.length > 0 ? (
|
||||
<LineGraph
|
||||
data={[
|
||||
|
|
@ -55,5 +56,5 @@ export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) =
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const AppliedDateFilters: React.FC<Props> = observer((props) => {
|
|||
return (
|
||||
<>
|
||||
{values.map((date) => (
|
||||
<div key={date} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<div key={date} className="flex items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs">
|
||||
<span className="normal-case">{getDateLabel(date)}</span>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
|
|||
if (!memberDetails) return null;
|
||||
|
||||
return (
|
||||
<div key={memberId} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} />
|
||||
<div key={memberId} className="flex items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs">
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} size={"sm"} />
|
||||
<span className="normal-case">{memberDetails.display_name}</span>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export const BreadcrumbLink: React.FC<Props> = (props) => {
|
|||
</Link>
|
||||
) : (
|
||||
<div className="flex cursor-default items-center gap-1 text-sm font-medium text-custom-text-100">
|
||||
{icon && <div className="flex h-5 w-5 items-center justify-center overflow-hidden">{icon}</div>}
|
||||
{icon && <div className="flex h-5 w-5 items-center justify-start overflow-hidden">{icon}</div>}
|
||||
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { ReactNode } from "react";
|
||||
// components
|
||||
import { CustomRow } from "@plane/ui";
|
||||
import { Row } from "@plane/ui";
|
||||
import { SidebarHamburgerToggle } from "@/components/core";
|
||||
|
||||
export interface AppHeaderProps {
|
||||
|
|
@ -15,12 +15,12 @@ export const AppHeader = (props: AppHeaderProps) => {
|
|||
|
||||
return (
|
||||
<div className="z-[15]">
|
||||
<CustomRow className="h-[3.75rem] z-10 flex gap-2 w-full items-center border-b border-custom-border-200">
|
||||
<Row className="h-[3.75rem] z-10 flex gap-2 w-full items-center border-b border-custom-border-200">
|
||||
<div className="block bg-custom-sidebar-background-100 md:hidden">
|
||||
<SidebarHamburgerToggle />
|
||||
</div>
|
||||
<div className="w-full">{header}</div>
|
||||
</CustomRow>
|
||||
</Row>
|
||||
{mobileHeader && mobileHeader}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
import React, { FC } from "react";
|
||||
// ui
|
||||
import { ControlLink, Tooltip } from "@plane/ui";
|
||||
import { ControlLink, Row, Tooltip } from "@plane/ui";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
|
|
@ -51,9 +51,9 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
|||
|
||||
return (
|
||||
<div ref={parentRef} className="relative">
|
||||
<div
|
||||
<Row
|
||||
className={cn(
|
||||
"group min-h-[52px] flex w-full flex-col items-center justify-between gap-3 px-6 py-4 text-sm border-b border-custom-border-200 bg-custom-background-100 hover:bg-custom-background-90 ",
|
||||
"group min-h-[52px] flex w-full flex-col items-center justify-between gap-3 py-4 text-sm border-b border-custom-border-200 bg-custom-background-100 hover:bg-custom-background-90 ",
|
||||
{
|
||||
"xl:gap-5 xl:py-0 xl:flex-row": isSidebarOpen,
|
||||
"lg:gap-5 lg:py-0 lg:flex-row": !isSidebarOpen,
|
||||
|
|
@ -93,7 +93,7 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
|||
{actionableItems}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { FC } from "react";
|
||||
import { Row, ERowVariant } from "@plane/ui";
|
||||
|
||||
interface IListContainer {
|
||||
children: React.ReactNode;
|
||||
|
|
@ -6,5 +7,12 @@ interface IListContainer {
|
|||
|
||||
export const ListLayout: FC<IListContainer> = (props) => {
|
||||
const { children } = props;
|
||||
return <div className="flex h-full w-full flex-col overflow-y-auto vertical-scrollbar scrollbar-lg">{children}</div>;
|
||||
return (
|
||||
<Row
|
||||
variant={ERowVariant.HUGGING}
|
||||
className="flex h-full w-full flex-col overflow-y-auto vertical-scrollbar scrollbar-lg"
|
||||
>
|
||||
{children}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import { observer } from "mobx-react";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
// ui
|
||||
import { Row } from "@plane/ui";
|
||||
// components
|
||||
import {
|
||||
ActiveCycleProductivity,
|
||||
|
|
@ -36,14 +38,14 @@ export const ActiveCycleRoot: React.FC<IActiveCycleDetails> = observer((props) =
|
|||
<Disclosure as="div" className="flex flex-shrink-0 flex-col" defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-7 py-1 cursor-pointer">
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 cursor-pointer">
|
||||
<CycleListGroupHeader title="Active cycle" type="current" isExpanded={open} />
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel>
|
||||
{!currentProjectActiveCycle ? (
|
||||
<EmptyState type={EmptyStateType.PROJECT_CYCLE_ACTIVE} size="sm" />
|
||||
) : (
|
||||
<div className="flex flex-col bg-custom-background-90 border-b border-custom-border-200">
|
||||
<div className="flex flex-col border-b border-custom-border-200">
|
||||
{currentProjectActiveCycleId && (
|
||||
<CyclesListItem
|
||||
key={currentProjectActiveCycleId}
|
||||
|
|
@ -53,7 +55,7 @@ export const ActiveCycleRoot: React.FC<IActiveCycleDetails> = observer((props) =
|
|||
className="!border-b-transparent"
|
||||
/>
|
||||
)}
|
||||
<div className="bg-custom-background-100 pt-3 pb-6 px-6">
|
||||
<Row className="bg-custom-background-100 pt-3 pb-6">
|
||||
<div className="grid grid-cols-1 bg-custom-background-100 gap-3 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<ActiveCycleProgress
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
|
|
@ -75,7 +77,7 @@ export const ActiveCycleRoot: React.FC<IActiveCycleDetails> = observer((props) =
|
|||
cycleIssueDetails={cycleIssueDetails as ActiveCycleIssueDetails}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
</Disclosure.Panel>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export const AppliedDateFilters: React.FC<Props> = observer((props) => {
|
|||
return (
|
||||
<>
|
||||
{values.map((date) => (
|
||||
<div key={date} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<div key={date} className="flex items-center gap-1 rounded bg-custom-background-80 py-1 px-1.5 text-xs">
|
||||
<span className="normal-case">{getDateLabel(date)}</span>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||
import { X } from "lucide-react";
|
||||
import { TCycleFilters } from "@plane/types";
|
||||
// hooks
|
||||
import { Tag } from "@plane/ui";
|
||||
import { AppliedDateFilters, AppliedStatusFilters } from "@/components/cycles";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
||||
|
|
@ -42,10 +43,7 @@ export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
|
|||
if (Array.isArray(value) && value.length === 0) return;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={filterKey}
|
||||
className="flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 capitalize"
|
||||
>
|
||||
<Tag key={filterKey}>
|
||||
<span className="text-xs text-custom-text-300">{replaceUnderscoreIfSnakeCase(filterKey)}</span>
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
{filterKey === "status" && (
|
||||
|
|
@ -72,17 +70,15 @@ export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
|
|||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
{isEditingAllowed && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClearAllFilters}
|
||||
className="flex items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 text-xs text-custom-text-300 hover:text-custom-text-200"
|
||||
>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
<button type="button" onClick={handleClearAllFilters}>
|
||||
<Tag>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
</Tag>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export const AppliedStatusFilters: React.FC<Props> = observer((props) => {
|
|||
<div
|
||||
key={status}
|
||||
className={cn(
|
||||
"flex items-center gap-1 rounded p-1 text-xs",
|
||||
"flex items-center gap-1 rounded py-1 px-1.5 text-xs",
|
||||
statusDetails?.bgColor,
|
||||
statusDetails?.textColor
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -50,14 +50,12 @@ export const CyclesView: FC<ICyclesView> = observer((props) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CyclesList
|
||||
completedCycleIds={filteredCompletedCycleIds ?? []}
|
||||
upcomingCycleIds={filteredUpcomingCycleIds}
|
||||
cycleIds={filteredCycleIds}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
/>
|
||||
</>
|
||||
<CyclesList
|
||||
completedCycleIds={filteredCompletedCycleIds ?? []}
|
||||
upcomingCycleIds={filteredUpcomingCycleIds}
|
||||
cycleIds={filteredCycleIds}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ChevronDown } from "lucide-react";
|
|||
// types
|
||||
import { TCycleGroups } from "@plane/types";
|
||||
// icons
|
||||
import { CycleGroupIcon } from "@plane/ui";
|
||||
import { Row, CycleGroupIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ type Props = {
|
|||
export const CycleListGroupHeader: FC<Props> = (props) => {
|
||||
const { type, title, count, showCount = false, isExpanded = false } = props;
|
||||
return (
|
||||
<div className="relative flex items-center justify-between w-full gap-5 py-1.5">
|
||||
<Row className="flex items-center justify-between py-2.5">
|
||||
<div className="flex items-center gap-5 flex-shrink-0">
|
||||
<div className="flex h-5 w-5 flex-shrink-0 items-center justify-center overflow-hidden rounded-sm">
|
||||
<CycleGroupIcon cycleGroup={type} className="h-5 w-5" />
|
||||
|
|
@ -36,6 +36,6 @@ export const CycleListGroupHeader: FC<Props> = (props) => {
|
|||
"rotate-180": isExpanded,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { FC } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { Disclosure } from "@headlessui/react";
|
||||
// components
|
||||
import { ContentWrapper, ERowVariant } from "@plane/ui";
|
||||
import { ListLayout } from "@/components/core/list";
|
||||
import { ActiveCycleRoot, CycleListGroupHeader, CyclePeekOverview, CyclesListMap } from "@/components/cycles";
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
|
|||
const { completedCycleIds, upcomingCycleIds, cycleIds, workspaceSlug, projectId, isArchived = false } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full justify-between ">
|
||||
<ContentWrapper variant={ERowVariant.HUGGING}>
|
||||
<ListLayout>
|
||||
{isArchived ? (
|
||||
<>
|
||||
|
|
@ -32,7 +33,7 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
|
|||
<Disclosure as="div" className="flex flex-shrink-0 flex-col" defaultOpen>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-7 py-1 cursor-pointer">
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 cursor-pointer">
|
||||
<CycleListGroupHeader
|
||||
title="Upcoming cycle"
|
||||
type="upcoming"
|
||||
|
|
@ -52,7 +53,7 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
|
|||
<Disclosure as="div" className="flex flex-shrink-0 flex-col pb-7">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-7 py-1 cursor-pointer">
|
||||
<Disclosure.Button className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 cursor-pointer">
|
||||
<CycleListGroupHeader
|
||||
title="Completed cycle"
|
||||
type="completed"
|
||||
|
|
@ -71,6 +72,6 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
|
|||
)}
|
||||
</ListLayout>
|
||||
<CyclePeekOverview projectId={projectId} workspaceSlug={workspaceSlug} isArchived={isArchived} />
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Link from "next/link";
|
|||
import { Tab } from "@headlessui/react";
|
||||
import { TAssignedIssuesWidgetFilters, TAssignedIssuesWidgetResponse } from "@plane/types";
|
||||
// hooks
|
||||
import { Card } from "@plane/ui";
|
||||
import {
|
||||
DurationFilterDropdown,
|
||||
IssuesErrorState,
|
||||
|
|
@ -79,7 +80,7 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
if ((!widgetDetails || !widgetStats) && !widgetStatsError) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full hover:shadow-custom-shadow-4xl duration-300 flex flex-col min-h-96">
|
||||
<Card>
|
||||
{widgetStatsError ? (
|
||||
<IssuesErrorState
|
||||
isRefreshing={fetching}
|
||||
|
|
@ -93,7 +94,7 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
) : (
|
||||
widgetStats && (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
||||
<div className="flex items-center justify-between gap-2 mb-4">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/workspace-views/assigned/${filterParams}`}
|
||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||
|
|
@ -137,9 +138,7 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
}}
|
||||
className="h-full flex flex-col"
|
||||
>
|
||||
<div className="px-6">
|
||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||
</div>
|
||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||
<Tab.Panels as="div" className="h-full">
|
||||
{tabsList.map((tab) => {
|
||||
if (tab.key !== selectedTab) return null;
|
||||
|
|
@ -161,6 +160,6 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
</>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Link from "next/link";
|
|||
import { Tab } from "@headlessui/react";
|
||||
import { TCreatedIssuesWidgetFilters, TCreatedIssuesWidgetResponse } from "@plane/types";
|
||||
// hooks
|
||||
import { Card } from "@plane/ui";
|
||||
import {
|
||||
DurationFilterDropdown,
|
||||
IssuesErrorState,
|
||||
|
|
@ -76,7 +77,7 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
if ((!widgetDetails || !widgetStats) && !widgetStatsError) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full hover:shadow-custom-shadow-4xl duration-300 flex flex-col min-h-96">
|
||||
<Card>
|
||||
{widgetStatsError ? (
|
||||
<IssuesErrorState
|
||||
isRefreshing={fetching}
|
||||
|
|
@ -90,7 +91,7 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
) : (
|
||||
widgetStats && (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
||||
<div className="flex items-center justify-between gap-2 mb-4">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/workspace-views/created/${filterParams}`}
|
||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||
|
|
@ -134,9 +135,7 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
}}
|
||||
className="h-full flex flex-col"
|
||||
>
|
||||
<div className="px-6">
|
||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||
</div>
|
||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||
<Tab.Panels as="div" className="h-full">
|
||||
{tabsList.map((tab) => {
|
||||
if (tab.key !== selectedTab) return null;
|
||||
|
|
@ -158,6 +157,6 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
</>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
|
|||
<>
|
||||
<div className="h-full">
|
||||
{isLoading ? (
|
||||
<Loader className="space-y-4 mx-6 mt-7">
|
||||
<Loader className="space-y-4 mt-7">
|
||||
<Loader.Item height="25px" />
|
||||
<Loader.Item height="25px" />
|
||||
<Loader.Item height="25px" />
|
||||
|
|
@ -74,7 +74,7 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
|
|||
</Loader>
|
||||
) : issuesList.length > 0 ? (
|
||||
<>
|
||||
<div className="mt-7 mx-6 border-b-[0.5px] border-custom-border-200 grid grid-cols-12 gap-1 text-xs text-custom-text-300 pb-1">
|
||||
<div className="mt-7 border-b-[0.5px] border-custom-border-200 grid grid-cols-12 gap-1 text-xs text-custom-text-300 pb-1">
|
||||
<h6
|
||||
className={cn("pl-1 flex items-center gap-1 col-span-7", {
|
||||
"col-span-11": type === "assigned" && tab === "completed",
|
||||
|
|
@ -92,7 +92,7 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
|
|||
{type === "assigned" && tab !== "completed" && <h6 className="text-center col-span-2">Blocked by</h6>}
|
||||
{type === "created" && <h6 className="text-center col-span-2">Assigned to</h6>}
|
||||
</div>
|
||||
<div className="px-4 pb-3 mt-2">
|
||||
<div className="pb-3 mt-2">
|
||||
{issuesList.map((issue) => {
|
||||
const IssueListItem = ISSUE_LIST_ITEM[type][tab];
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Link from "next/link";
|
|||
// types
|
||||
import { TIssuesByPriorityWidgetFilters, TIssuesByPriorityWidgetResponse } from "@plane/types";
|
||||
// components
|
||||
import { Card } from "@plane/ui";
|
||||
import {
|
||||
DurationFilterDropdown,
|
||||
IssuesByPriorityEmptyState,
|
||||
|
|
@ -69,8 +70,8 @@ export const IssuesByPriorityWidget: React.FC<WidgetProps> = observer((props) =>
|
|||
}));
|
||||
|
||||
return (
|
||||
<div className="flex min-h-96 w-full flex-col overflow-hidden rounded-xl border-[0.5px] border-custom-border-200 bg-custom-background-100 py-6 duration-300 hover:shadow-custom-shadow-4xl">
|
||||
<div className="flex items-center justify-between gap-2 pl-7 pr-6">
|
||||
<Card>
|
||||
<div className="flex items-center justify-between gap-2 mb-4">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/workspace-views/assigned`}
|
||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||
|
|
@ -106,6 +107,6 @@ export const IssuesByPriorityWidget: React.FC<WidgetProps> = observer((props) =>
|
|||
<IssuesByPriorityEmptyState />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Link from "next/link";
|
|||
// types
|
||||
import { TIssuesByStateGroupsWidgetFilters, TIssuesByStateGroupsWidgetResponse, TStateGroups } from "@plane/types";
|
||||
// components
|
||||
import { Card } from "@plane/ui";
|
||||
import {
|
||||
DurationFilterDropdown,
|
||||
IssuesByStateGroupEmptyState,
|
||||
|
|
@ -79,14 +80,14 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
|||
startedCount > 0
|
||||
? "started"
|
||||
: unStartedCount > 0
|
||||
? "unstarted"
|
||||
: backlogCount > 0
|
||||
? "backlog"
|
||||
: completedCount > 0
|
||||
? "completed"
|
||||
: canceledCount > 0
|
||||
? "cancelled"
|
||||
: null;
|
||||
? "unstarted"
|
||||
: backlogCount > 0
|
||||
? "backlog"
|
||||
: completedCount > 0
|
||||
? "completed"
|
||||
: canceledCount > 0
|
||||
? "cancelled"
|
||||
: null;
|
||||
|
||||
setActiveStateGroup(stateGroup);
|
||||
setDefaultStateGroup(stateGroup);
|
||||
|
|
@ -134,8 +135,8 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300 overflow-hidden min-h-96 flex flex-col">
|
||||
<div className="flex items-center justify-between gap-2 pl-7 pr-6">
|
||||
<Card>
|
||||
<div className="flex items-center justify-between gap-2 mb-4">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/workspace-views/assigned`}
|
||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||
|
|
@ -154,7 +155,7 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
|||
/>
|
||||
</div>
|
||||
{totalCount > 0 ? (
|
||||
<div className="flex items-center pl-10 md:pl-11 lg:pl-14 pr-11 mt-11">
|
||||
<div className="flex items-center mt-11">
|
||||
<div className="flex flex-col sm:flex-row md:flex-row lg:flex-row items-center justify-evenly gap-x-10 gap-y-8 w-full">
|
||||
<div>
|
||||
<PieGraph
|
||||
|
|
@ -215,6 +216,6 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
|
|||
<IssuesByStateGroupEmptyState />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
import Link from "next/link";
|
||||
import { TOverviewStatsWidgetResponse } from "@plane/types";
|
||||
// hooks
|
||||
import { Card, ECardSpacing } from "@plane/ui";
|
||||
import { WidgetLoader } from "@/components/dashboard/widgets";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
|
|
@ -63,8 +64,9 @@ export const OverviewStatsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full grid lg:grid-cols-4 md:grid-cols-2 sm:grid-cols-2 grid-cols-2 p-0.5 hover:shadow-custom-shadow-4xl duration-300
|
||||
<Card
|
||||
spacing={ECardSpacing.SM}
|
||||
className="flex-row grid lg:grid-cols-4 md:grid-cols-2 sm:grid-cols-2 grid-cols-2 space-y-0
|
||||
[&>div>a>div]:border-r
|
||||
[&>div:last-child>a>div]:border-0
|
||||
[&>div>a>div]:border-custom-border-200
|
||||
|
|
@ -93,6 +95,6 @@ export const OverviewStatsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ import Link from "next/link";
|
|||
import { History } from "lucide-react";
|
||||
// types
|
||||
import { TRecentActivityWidgetResponse } from "@plane/types";
|
||||
// UI
|
||||
import { Avatar, getButtonStyling } from "@plane/ui";
|
||||
// components
|
||||
import { Card, Avatar, getButtonStyling } from "@plane/ui";
|
||||
import { ActivityIcon, ActivityMessage, IssueLink } from "@/components/core";
|
||||
import { RecentActivityEmptyState, WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
|
||||
// helpers
|
||||
|
|
@ -38,12 +37,12 @@ export const RecentActivityWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<div className="min-h-96 w-full rounded-xl border-[0.5px] border-custom-border-200 bg-custom-background-100 py-6 duration-300 hover:shadow-custom-shadow-4xl">
|
||||
<Link href={redirectionLink} className="mx-7 text-lg font-semibold text-custom-text-300 hover:underline">
|
||||
<Card>
|
||||
<Link href={redirectionLink} className="text-lg font-semibold text-custom-text-300 hover:underline mb-4">
|
||||
Your issue activities
|
||||
</Link>
|
||||
{widgetStats.length > 0 ? (
|
||||
<div className="mx-7 mt-4 space-y-6">
|
||||
<div className="mt-4 space-y-6">
|
||||
{widgetStats.map((activity) => (
|
||||
<div key={activity.id} className="flex gap-5">
|
||||
<div className="flex-shrink-0">
|
||||
|
|
@ -104,6 +103,6 @@ export const RecentActivityWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
<RecentActivityEmptyState />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState } from "react";
|
||||
import { Search } from "lucide-react";
|
||||
// types
|
||||
import { Card } from "@plane/ui";
|
||||
import { WidgetProps } from "@/components/dashboard/widgets";
|
||||
// components
|
||||
import { DefaultCollaboratorsList } from "./default-list";
|
||||
|
|
@ -14,15 +15,15 @@ export const RecentCollaboratorsWidget: React.FC<WidgetProps> = (props) => {
|
|||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-xl border-[0.5px] border-custom-border-200 bg-custom-background-100 duration-300 hover:shadow-custom-shadow-4xl">
|
||||
<div className="flex flex-col sm:flex-row items-start justify-between px-7 pt-6">
|
||||
<Card>
|
||||
<div className="flex flex-col sm:flex-row items-start justify-between mb-6">
|
||||
<div>
|
||||
<h4 className="text-lg font-semibold text-custom-text-300">Collaborators</h4>
|
||||
<p className="mt-2 text-xs font-medium text-custom-text-300">
|
||||
View and find all members you collaborate with across projects
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-0 flex min-w-72 items-center justify-start gap-2 rounded-md border border-custom-border-200 px-2.5 py-1.5 placeholder:text-custom-text-400">
|
||||
<div className="mt-5 sm:mt-0 flex min-w-full md:min-w-72 items-center justify-start gap-2 rounded-md border border-custom-border-200 px-2.5 py-1.5 placeholder:text-custom-text-400">
|
||||
<Search className="h-3.5 w-3.5 text-custom-text-400" />
|
||||
<input
|
||||
className="w-full border-none bg-transparent text-sm focus:outline-none"
|
||||
|
|
@ -42,6 +43,6 @@ export const RecentCollaboratorsWidget: React.FC<WidgetProps> = (props) => {
|
|||
) : (
|
||||
<DefaultCollaboratorsList dashboardId={dashboardId} perPage={PER_PAGE} workspaceSlug={workspaceSlug} />
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import { Plus } from "lucide-react";
|
|||
// types
|
||||
import { TRecentProjectsWidgetResponse } from "@plane/types";
|
||||
// ui
|
||||
import { Avatar, AvatarGroup } from "@plane/ui";
|
||||
import { Avatar, AvatarGroup, Card } from "@plane/ui";
|
||||
|
||||
// components
|
||||
import { Logo } from "@/components/common";
|
||||
import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
|
||||
|
|
@ -82,14 +83,14 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<div className="min-h-96 w-full rounded-xl border-[0.5px] border-custom-border-200 bg-custom-background-100 py-6 duration-300 hover:shadow-custom-shadow-4xl">
|
||||
<Card>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects`}
|
||||
className="mx-7 text-lg font-semibold text-custom-text-300 hover:underline"
|
||||
className="text-lg font-semibold text-custom-text-300 hover:underline mb-4"
|
||||
>
|
||||
Recent projects
|
||||
</Link>
|
||||
<div className="mx-7 mt-4 space-y-8">
|
||||
<div className="mt-4 space-y-8">
|
||||
{canCreateProject && (
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -113,6 +114,6 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
<ProjectListItem key={projectId} projectId={projectId} workspaceSlug={workspaceSlug} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ const ButtonContent: React.FC<ButtonContentProps> = (props) => {
|
|||
return (
|
||||
<div
|
||||
key={moduleId}
|
||||
className="flex max-w-full items-center gap-1 rounded bg-custom-background-80 px-1.5 py-1 text-custom-text-200"
|
||||
className="flex max-w-full items-center gap-1 rounded bg-custom-background-80 py-1 text-custom-text-200"
|
||||
>
|
||||
{!hideIcon && <DiceIcon className="h-2.5 w-2.5 flex-shrink-0" />}
|
||||
{!hideText && (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||
import { Expand, Shrink } from "lucide-react";
|
||||
// hooks
|
||||
// helpers
|
||||
import { Row } from "@plane/ui";
|
||||
import { VIEWS_LIST } from "@/components/gantt-chart/data";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// types
|
||||
|
|
@ -26,7 +27,7 @@ export const GanttChartHeader: React.FC<Props> = observer((props) => {
|
|||
const { currentView } = useGanttChart();
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full flex-shrink-0 flex-wrap items-center gap-2 whitespace-nowrap px-2.5 py-2">
|
||||
<Row className="relative flex w-full flex-shrink-0 flex-wrap items-center gap-2 whitespace-nowrap py-2">
|
||||
<div className="ml-auto">
|
||||
<div className="ml-auto text-sm font-medium">
|
||||
{blockIds ? `${blockIds.length} ${loaderTitle}` : "Loading..."}
|
||||
|
|
@ -65,6 +66,6 @@ export const GanttChartHeader: React.FC<Props> = observer((props) => {
|
|||
>
|
||||
{fullScreenMode ? <Shrink className="h-4 w-4" /> : <Expand className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export const CyclesSidebarBlock: React.FC<Props> = observer((props) => {
|
|||
>
|
||||
<div
|
||||
id={`sidebar-block-${block.id}`}
|
||||
className={cn("group w-full flex items-center gap-2 pl-2 pr-4", {
|
||||
className={cn("group w-full flex items-center gap-2 pr-4", {
|
||||
"bg-custom-background-90": isBlockActive(block.id),
|
||||
})}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { observer } from "mobx-react";
|
||||
// components
|
||||
import { Row } from "@plane/ui";
|
||||
import { MultipleSelectEntityAction } from "@/components/core";
|
||||
import { useGanttChart } from "@/components/gantt-chart/hooks";
|
||||
import { IssueGanttSidebarBlock } from "@/components/issues";
|
||||
|
|
@ -45,8 +46,8 @@ export const IssuesSidebarBlock = observer((props: Props) => {
|
|||
onMouseEnter={() => updateActiveBlockId(block.id)}
|
||||
onMouseLeave={() => updateActiveBlockId(null)}
|
||||
>
|
||||
<div
|
||||
className={cn("group w-full flex items-center gap-2 pl-2 pr-4", {
|
||||
<Row
|
||||
className={cn("group w-full flex items-center gap-2 pr-4", {
|
||||
"bg-custom-background-90": isBlockHoveredOn,
|
||||
"bg-custom-primary-100/5 hover:bg-custom-primary-100/10": isIssueSelected,
|
||||
"bg-custom-primary-100/10": isIssueSelected && isBlockHoveredOn,
|
||||
|
|
@ -55,8 +56,8 @@ export const IssuesSidebarBlock = observer((props: Props) => {
|
|||
height: `${BLOCK_HEIGHT}px`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{enableSelection && selectionHelpers && (
|
||||
{enableSelection && selectionHelpers && (
|
||||
<div className="flex items-center gap-2 absolute left-1">
|
||||
<MultipleSelectEntityAction
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||
|
|
@ -68,8 +69,8 @@ export const IssuesSidebarBlock = observer((props: Props) => {
|
|||
id={block.id}
|
||||
selectionHelpers={selectionHelpers}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex h-full flex-grow items-center justify-between gap-2 truncate">
|
||||
<div className="flex-grow truncate">
|
||||
<IssueGanttSidebarBlock issueId={block.data.id} />
|
||||
|
|
@ -82,7 +83,7 @@ export const IssuesSidebarBlock = observer((props: Props) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { observer } from "mobx-react";
|
||||
// hooks
|
||||
import { Row } from "@plane/ui";
|
||||
import { BLOCK_HEIGHT } from "@/components/gantt-chart/constants";
|
||||
import { useGanttChart } from "@/components/gantt-chart/hooks";
|
||||
// components
|
||||
|
|
@ -31,9 +32,9 @@ export const ModulesSidebarBlock: React.FC<Props> = observer((props) => {
|
|||
onMouseEnter={() => updateActiveBlockId(block.id)}
|
||||
onMouseLeave={() => updateActiveBlockId(null)}
|
||||
>
|
||||
<div
|
||||
<Row
|
||||
id={`sidebar-block-${block.id}`}
|
||||
className={cn("group w-full flex items-center gap-2 pl-2 pr-4", {
|
||||
className={cn("group w-full flex items-center gap-2 pr-4", {
|
||||
"bg-custom-background-90": isBlockActive(block.id),
|
||||
})}
|
||||
style={{
|
||||
|
|
@ -50,7 +51,7 @@ export const ModulesSidebarBlock: React.FC<Props> = observer((props) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { RefObject } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// components
|
||||
import { Row, ERowVariant } from "@plane/ui";
|
||||
import { MultipleSelectGroupAction } from "@/components/core";
|
||||
import { ChartDataType, IBlockUpdateData, IGanttBlock } from "@/components/gantt-chart";
|
||||
// helpers
|
||||
|
|
@ -44,27 +45,24 @@ export const GanttChartSidebar: React.FC<Props> = observer((props) => {
|
|||
const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(GANTT_SELECT_GROUP) === "empty";
|
||||
|
||||
return (
|
||||
<div
|
||||
<Row
|
||||
// DO NOT REMOVE THE ID
|
||||
id="gantt-sidebar"
|
||||
className="sticky left-0 z-10 min-h-full h-max flex-shrink-0 border-r-[0.5px] border-custom-border-200 bg-custom-background-100"
|
||||
style={{
|
||||
width: `${SIDEBAR_WIDTH}px`,
|
||||
}}
|
||||
variant={ERowVariant.HUGGING}
|
||||
>
|
||||
<div
|
||||
className="group/list-header box-border flex-shrink-0 flex items-end justify-between gap-2 border-b-[0.5px] border-custom-border-200 pb-2 pl-2 pr-4 text-sm font-medium text-custom-text-300 sticky top-0 z-10 bg-custom-background-100"
|
||||
<Row
|
||||
className="group/list-header box-border flex-shrink-0 flex items-end justify-between gap-2 border-b-[0.5px] border-custom-border-200 pb-2 pr-4 text-sm font-medium text-custom-text-300 sticky top-0 z-10 bg-custom-background-100"
|
||||
style={{
|
||||
height: `${HEADER_HEIGHT}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn("flex items-center gap-2", {
|
||||
"pl-2": !enableSelection,
|
||||
})}
|
||||
>
|
||||
<div className={cn("flex items-center gap-2")}>
|
||||
{enableSelection && (
|
||||
<div className="flex-shrink-0 flex items-center w-3.5">
|
||||
<div className="flex-shrink-0 flex items-center w-3.5 absolute left-1">
|
||||
<MultipleSelectGroupAction
|
||||
className={cn(
|
||||
"size-3.5 opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto !outline-none",
|
||||
|
|
@ -80,9 +78,9 @@ export const GanttChartSidebar: React.FC<Props> = observer((props) => {
|
|||
<h6>{title}</h6>
|
||||
</div>
|
||||
<h6>Duration</h6>
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
<div className="min-h-full h-max bg-custom-background-100 overflow-hidden">
|
||||
<Row variant={ERowVariant.HUGGING} className="min-h-full h-max bg-custom-background-100 overflow-hidden">
|
||||
{sidebarToRender &&
|
||||
sidebarToRender({
|
||||
title,
|
||||
|
|
@ -96,8 +94,8 @@ export const GanttChartSidebar: React.FC<Props> = observer((props) => {
|
|||
loadMoreBlocks,
|
||||
selectionHelpers,
|
||||
})}
|
||||
</div>
|
||||
</Row>
|
||||
{quickAdd ? quickAdd : null}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import {
|
|||
Link,
|
||||
Trash2,
|
||||
MoveRight,
|
||||
Copy
|
||||
Copy,
|
||||
} from "lucide-react";
|
||||
import { Button, ControlLink, CustomMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { Button, ControlLink, CustomMenu, Row, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import {
|
||||
DeclineIssueModal,
|
||||
|
|
@ -245,7 +245,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
/>
|
||||
</>
|
||||
|
||||
<div className="hidden relative lg:flex h-full w-full items-center justify-between gap-2 px-4">
|
||||
<Row className="hidden relative lg:flex h-full w-full items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-4">
|
||||
{isNotificationEmbed && (
|
||||
<button onClick={embedRemoveCurrentNotification}>
|
||||
|
|
@ -376,7 +376,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
|
||||
<div className="lg:hidden">
|
||||
<InboxIssueActionsMobileHeader
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
PanelLeft,
|
||||
MoveRight,
|
||||
} from "lucide-react";
|
||||
import { CustomHeader, CustomMenu, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, CustomMenu, EHeaderVariant } from "@plane/ui";
|
||||
// components
|
||||
import { InboxIssueStatus } from "@/components/inbox";
|
||||
import { IssueUpdateStatus } from "@/components/issues";
|
||||
|
|
@ -80,7 +80,7 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
if (!issue || !inboxIssue) return null;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.SECONDARY} className="flex">
|
||||
<Header variant={EHeaderVariant.SECONDARY} className="justify-start">
|
||||
{isNotificationEmbed && (
|
||||
<button onClick={embedRemoveCurrentNotification}>
|
||||
<MoveRight className="h-4 w-4 text-custom-text-300 hover:text-custom-text-200" />
|
||||
|
|
@ -181,6 +181,6 @@ export const InboxIssueActionsMobileHeader: React.FC<Props> = observer((props) =
|
|||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ export const InboxIssueContentProperties: React.FC<Props> = observer((props) =>
|
|||
if (!issue || !issue?.id) return <></>;
|
||||
|
||||
return (
|
||||
<div className="flex h-min w-full flex-col divide-y-2 divide-custom-border-200 overflow-hidden">
|
||||
<div className="h-min w-full overflow-y-auto px-3">
|
||||
<div className="flex w-full flex-col divide-y-2 divide-custom-border-200">
|
||||
<div className="w-full overflow-y-auto">
|
||||
<h5 className="text-sm font-medium my-4">Properties</h5>
|
||||
<div className={`divide-y-2 divide-custom-border-200 ${!isEditable ? "opacity-60" : ""}`}>
|
||||
<div className="flex flex-col gap-3">
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-lg space-y-4 pl-3">
|
||||
<div className="rounded-lg space-y-4">
|
||||
<IssueTitleInput
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={issue.project_id}
|
||||
|
|
@ -141,14 +141,12 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="pl-3">
|
||||
<IssueAttachmentRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
issueId={issue.id}
|
||||
disabled={!isEditable}
|
||||
/>
|
||||
</div>
|
||||
<IssueAttachmentRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
issueId={issue.id}
|
||||
disabled={!isEditable}
|
||||
/>
|
||||
|
||||
<InboxIssueContentProperties
|
||||
workspaceSlug={workspaceSlug}
|
||||
|
|
@ -159,9 +157,7 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
|||
duplicateIssueDetails={inboxIssue?.duplicate_issue_detail}
|
||||
/>
|
||||
|
||||
<div className="pb-12 pl-3">
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issue.id} isIntakeIssue />
|
||||
</div>
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issue.id} isIntakeIssue />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { FC, useEffect, useState } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import useSWR from "swr";
|
||||
// components
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
|
|
@ -83,7 +84,7 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
|||
embedRemoveCurrentNotification={embedRemoveCurrentNotification}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-full w-full space-y-5 divide-y-2 divide-custom-border-200 overflow-y-auto px-6 py-5 vertical-scrollbar scrollbar-md">
|
||||
<ContentWrapper className="space-y-5 divide-y-2 divide-custom-border-200">
|
||||
<InboxIssueMainContent
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
|
|
@ -92,7 +93,7 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
|||
isSubmitting={isSubmitting}
|
||||
setIsSubmitting={setIsSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
import { X } from "lucide-react";
|
||||
import { TInboxIssueFilterDateKeys } from "@plane/types";
|
||||
// helpers
|
||||
import { Tag } from "@plane/ui";
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
// constants
|
||||
import { PAST_DURATION_FILTER_OPTIONS } from "@/helpers/inbox.helper";
|
||||
|
|
@ -37,7 +38,7 @@ export const InboxIssueAppliedFiltersDate: FC<InboxIssueAppliedFiltersDate> = ob
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">{label}</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -61,6 +62,6 @@ export const InboxIssueAppliedFiltersDate: FC<InboxIssueAppliedFiltersDate> = ob
|
|||
>
|
||||
<X className={`w-3 h-3`} />
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { FC } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
// hooks
|
||||
import { Tag } from "@plane/ui";
|
||||
import { useLabel, useProjectInbox } from "@/hooks/store";
|
||||
|
||||
const LabelIcons = ({ color }: { color: string }) => (
|
||||
|
|
@ -23,7 +24,7 @@ export const InboxIssueAppliedFiltersLabel: FC = observer(() => {
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">Label</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -50,6 +51,6 @@ export const InboxIssueAppliedFiltersLabel: FC = observer(() => {
|
|||
>
|
||||
<X className={`w-3 h-3`} />
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FC } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import { TInboxIssueFilterMemberKeys } from "@plane/types";
|
||||
import { Avatar } from "@plane/ui";
|
||||
import { Avatar, Tag } from "@plane/ui";
|
||||
// hooks
|
||||
import { useMember, useProjectInbox } from "@/hooks/store";
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export const InboxIssueAppliedFiltersMember: FC<InboxIssueAppliedFiltersMember>
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">{label}</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -56,6 +56,6 @@ export const InboxIssueAppliedFiltersMember: FC<InboxIssueAppliedFiltersMember>
|
|||
>
|
||||
<X className={`w-3 h-3`} />
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FC } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import { TIssuePriorities } from "@plane/types";
|
||||
import { PriorityIcon } from "@plane/ui";
|
||||
import { PriorityIcon, Tag } from "@plane/ui";
|
||||
// constants
|
||||
import { ISSUE_PRIORITIES } from "@/constants/issue";
|
||||
// hooks
|
||||
|
|
@ -25,7 +25,7 @@ export const InboxIssueAppliedFiltersPriority: FC = observer(() => {
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">Priority</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -52,6 +52,6 @@ export const InboxIssueAppliedFiltersPriority: FC = observer(() => {
|
|||
>
|
||||
<X className={`w-3 h-3`} />
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import {
|
||||
InboxIssueAppliedFiltersStatus,
|
||||
InboxIssueAppliedFiltersPriority,
|
||||
|
|
@ -18,7 +18,7 @@ export const InboxIssueAppliedFilters: FC = observer(() => {
|
|||
|
||||
if (getAppliedFiltersCount === 0) return <></>;
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY} className="flex-wrap items-center gap-1 min-h-none">
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
{/* status */}
|
||||
<InboxIssueAppliedFiltersStatus />
|
||||
{/* state */}
|
||||
|
|
@ -35,6 +35,6 @@ export const InboxIssueAppliedFilters: FC = observer(() => {
|
|||
<InboxIssueAppliedFiltersDate filterKey="created_at" label="Created date" />
|
||||
{/* updated_at */}
|
||||
<InboxIssueAppliedFiltersDate filterKey="updated_at" label="Updated date" />
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import { StateGroupIcon } from "@plane/ui";
|
||||
import { StateGroupIcon, Tag } from "@plane/ui";
|
||||
// hooks
|
||||
import { useProjectInbox, useProjectState } from "@/hooks/store";
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ export const InboxIssueAppliedFiltersState: FC = observer(() => {
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">State</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -49,6 +49,6 @@ export const InboxIssueAppliedFiltersState: FC = observer(() => {
|
|||
>
|
||||
<X className={`w-3 h-3`} />
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
import { X } from "lucide-react";
|
||||
import { TInboxIssueStatus } from "@plane/types";
|
||||
// constants
|
||||
import { Tag } from "@plane/ui";
|
||||
import { INBOX_STATUS } from "@/constants/inbox";
|
||||
// hooks
|
||||
import { useProjectInbox } from "@/hooks/store";
|
||||
|
|
@ -19,7 +20,7 @@ export const InboxIssueAppliedFiltersStatus: FC = observer(() => {
|
|||
|
||||
if (filteredValues.length === 0) return <></>;
|
||||
return (
|
||||
<div className="relative flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1">
|
||||
<Tag>
|
||||
<div className="text-xs text-custom-text-200">Status</div>
|
||||
{filteredValues.map((value) => {
|
||||
const optionDetail = currentOptionDetail(value);
|
||||
|
|
@ -41,6 +42,6 @@ export const InboxIssueAppliedFiltersStatus: FC = observer(() => {
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FC, MouseEvent } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Tooltip, PriorityIcon } from "@plane/ui";
|
||||
import { Tooltip, PriorityIcon, Row } from "@plane/ui";
|
||||
// components
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { InboxIssueStatus } from "@/components/inbox";
|
||||
|
|
@ -53,9 +53,9 @@ export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props)
|
|||
href={`/${workspaceSlug}/projects/${projectId}/inbox?currentTab=${currentTab}&inboxIssueId=${issue.id}`}
|
||||
onClick={(e) => handleIssueRedirection(e, issue.id)}
|
||||
>
|
||||
<div
|
||||
<Row
|
||||
className={cn(
|
||||
`flex flex-col gap-2 relative border border-t-transparent border-l-transparent border-r-transparent border-b-custom-border-200 p-4 hover:bg-custom-primary/5 cursor-pointer transition-all`,
|
||||
`flex flex-col gap-2 relative border border-t-transparent border-l-transparent border-r-transparent border-b-custom-border-200 py-4 hover:bg-custom-primary/5 cursor-pointer transition-all`,
|
||||
{ "border-custom-primary-100 border": selectedInboxIssueId === issue.id }
|
||||
)}
|
||||
>
|
||||
|
|
@ -118,7 +118,7 @@ export const InboxIssueListItem: FC<InboxIssueListItemProps> = observer((props)
|
|||
{/* created by */}
|
||||
{createdByDetails && <ButtonAvatars showTooltip={false} userIds={createdByDetails?.id} />}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TInboxIssueCurrentTab } from "@plane/types";
|
||||
import { CustomHeader, Loader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, Loader, EHeaderVariant } from "@plane/ui";
|
||||
// components
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { FiltersRoot, InboxIssueAppliedFilters, InboxIssueList } from "@/components/inbox";
|
||||
|
|
@ -76,7 +76,7 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||
return (
|
||||
<div className="bg-custom-background-100 flex-shrink-0 w-full h-full border-r border-custom-border-300 ">
|
||||
<div className="relative w-full h-full flex flex-col overflow-hidden">
|
||||
<CustomHeader variant={EHeaderVariant.SECONDARY} className="flex">
|
||||
<Header variant={EHeaderVariant.SECONDARY}>
|
||||
{tabNavigationOptions.map((option) => (
|
||||
<div
|
||||
key={option?.key}
|
||||
|
|
@ -108,7 +108,7 @@ export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
|
|||
<div className="m-auto mr-0">
|
||||
<FiltersRoot />
|
||||
</div>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
<InboxIssueAppliedFilters />
|
||||
|
||||
{loader != undefined && loader === "filter-loading" && !inboxIssuePaginationInfo?.next_page_results ? (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
IIssueFilterOptions,
|
||||
TIssueKanbanFilters,
|
||||
} from "@plane/types";
|
||||
import { Row } from "@plane/ui";
|
||||
import { CalendarMonthsDropdown, CalendarOptionsDropdown } from "@/components/issues";
|
||||
// icons
|
||||
import { EIssueFilterType } from "@/constants/issue";
|
||||
|
|
@ -96,7 +97,7 @@ export const CalendarHeader: React.FC<ICalendarHeader> = observer((props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="mb-4 flex items-center justify-between gap-2 px-3">
|
||||
<Row className="mb-4 flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<button type="button" className="grid place-items-center" onClick={handlePrevious}>
|
||||
<ChevronLeft size={16} strokeWidth={2} />
|
||||
|
|
@ -116,6 +117,6 @@ export const CalendarHeader: React.FC<ICalendarHeader> = observer((props) => {
|
|||
</button>
|
||||
<CalendarOptionsDropdown issuesFilterStore={issuesFilterStore} updateFilters={updateFilters} />
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { X } from "lucide-react";
|
|||
// types
|
||||
import { IIssueFilterOptions, IIssueLabel, IState } from "@plane/types";
|
||||
// components
|
||||
import { Tag } from "@plane/ui";
|
||||
import {
|
||||
AppliedCycleFilters,
|
||||
AppliedDateFilters,
|
||||
|
|
@ -67,100 +68,90 @@ export const AppliedFiltersList: React.FC<Props> = observer((props) => {
|
|||
if (Array.isArray(value) && value.length === 0) return;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={filterKey}
|
||||
className="flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 capitalize truncate"
|
||||
>
|
||||
<div className="flex flex-wrap items-center gap-1.5 truncate">
|
||||
<span className="text-xs text-custom-text-300">{replaceUnderscoreIfSnakeCase(filterKey)}</span>
|
||||
{membersFilters.includes(filterKey) && (
|
||||
<AppliedMembersFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter(filterKey, val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{dateFilters.includes(filterKey) && (
|
||||
<AppliedDateFilters handleRemove={(val) => handleRemoveFilter(filterKey, val)} values={value} />
|
||||
)}
|
||||
{filterKey === "labels" && (
|
||||
<AppliedLabelsFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("labels", val)}
|
||||
labels={labels}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "priority" && (
|
||||
<AppliedPriorityFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("priority", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "state" && states && (
|
||||
<AppliedStateFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("state", val)}
|
||||
states={states}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "state_group" && (
|
||||
<AppliedStateGroupFilters
|
||||
handleRemove={(val) => handleRemoveFilter("state_group", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "project" && (
|
||||
<AppliedProjectFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("project", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "cycle" && (
|
||||
<AppliedCycleFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("cycle", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "module" && (
|
||||
<AppliedModuleFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("module", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "issue_type" && (
|
||||
<AppliedIssueTypeFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("issue_type", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{isEditingAllowed && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid place-items-center text-custom-text-300 hover:text-custom-text-200"
|
||||
onClick={() => handleRemoveFilter(filterKey, null)}
|
||||
>
|
||||
<X size={12} strokeWidth={2} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Tag key={filterKey}>
|
||||
<span className="text-xs text-custom-text-300">{replaceUnderscoreIfSnakeCase(filterKey)}</span>
|
||||
{membersFilters.includes(filterKey) && (
|
||||
<AppliedMembersFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter(filterKey, val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{dateFilters.includes(filterKey) && (
|
||||
<AppliedDateFilters handleRemove={(val) => handleRemoveFilter(filterKey, val)} values={value} />
|
||||
)}
|
||||
{filterKey === "labels" && (
|
||||
<AppliedLabelsFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("labels", val)}
|
||||
labels={labels}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "priority" && (
|
||||
<AppliedPriorityFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("priority", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "state" && states && (
|
||||
<AppliedStateFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("state", val)}
|
||||
states={states}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "state_group" && (
|
||||
<AppliedStateGroupFilters handleRemove={(val) => handleRemoveFilter("state_group", val)} values={value} />
|
||||
)}
|
||||
{filterKey === "project" && (
|
||||
<AppliedProjectFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("project", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "cycle" && (
|
||||
<AppliedCycleFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("cycle", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "module" && (
|
||||
<AppliedModuleFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("module", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{filterKey === "issue_type" && (
|
||||
<AppliedIssueTypeFilters
|
||||
editable={isEditingAllowed}
|
||||
handleRemove={(val) => handleRemoveFilter("issue_type", val)}
|
||||
values={value}
|
||||
/>
|
||||
)}
|
||||
{isEditingAllowed && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid place-items-center text-custom-text-300 hover:text-custom-text-200"
|
||||
onClick={() => handleRemoveFilter(filterKey, null)}
|
||||
>
|
||||
<X size={12} strokeWidth={2} />
|
||||
</button>
|
||||
)}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
{isEditingAllowed && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClearAllFilters}
|
||||
className="flex items-center gap-2 flex-shrink-0 rounded-md border border-custom-border-200 px-2 py-1 text-xs text-custom-text-300 hover:text-custom-text-200"
|
||||
>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
<button type="button" onClick={handleClearAllFilters}>
|
||||
<Tag>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
</Tag>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<div key={memberId} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} />
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} size={"sm"} />
|
||||
<span className="normal-case">{memberDetails.display_name}</span>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { observer } from "mobx-react";
|
|||
import { useParams } from "next/navigation";
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
// hooks
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { AppliedFiltersList, SaveFilterView } from "@/components/issues";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { useIssues, useLabel, useProjectState } from "@/hooks/store";
|
||||
|
|
@ -77,8 +77,8 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
if (Object.keys(appliedFilters).length === 0 || !workspaceSlug || !projectId || !cycleId) return null;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<CustomHeader.LeftItem>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<Header.LeftItem>
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
|
|
@ -86,7 +86,7 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
labels={projectLabels ?? []}
|
||||
states={projectStates}
|
||||
/>
|
||||
</CustomHeader.LeftItem>
|
||||
</Header.LeftItem>
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
|
|
@ -96,6 +96,6 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
display_properties: issueFilters?.displayProperties,
|
||||
}}
|
||||
/>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useParams } from "next/navigation";
|
|||
import { IIssueFilterOptions, TStaticViewTypes } from "@plane/types";
|
||||
//ui
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { AppliedFiltersList } from "@/components/issues";
|
||||
import { UpdateViewComponent } from "@/components/views/update-view-component";
|
||||
import { CreateUpdateWorkspaceViewModal } from "@/components/workspace";
|
||||
|
|
@ -133,7 +133,7 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||
if (areAppliedFiltersEmpty && areFiltersEqual) return null;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<CreateUpdateWorkspaceViewModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
|
|
@ -166,6 +166,6 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||
) : (
|
||||
<></>
|
||||
)}
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { observer } from "mobx-react";
|
|||
import { useParams } from "next/navigation";
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
// hooks
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { AppliedFiltersList, SaveFilterView } from "@/components/issues";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { useIssues, useLabel, useProjectState } from "@/hooks/store";
|
||||
|
|
@ -76,8 +76,8 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
if (!workspaceSlug || !projectId || !moduleId || Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<CustomHeader.LeftItem>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<Header.LeftItem>
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
|
|
@ -85,7 +85,7 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
labels={projectLabels ?? []}
|
||||
states={projectStates}
|
||||
/>
|
||||
</CustomHeader.LeftItem>
|
||||
</Header.LeftItem>
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
|
|
@ -95,6 +95,6 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||
display_properties: issueFilters?.displayProperties,
|
||||
}}
|
||||
/>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useParams } from "next/navigation";
|
|||
import { IIssueFilterOptions } from "@plane/types";
|
||||
// hooks
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { AppliedFiltersList, SaveFilterView } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
|
|
@ -68,25 +68,29 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
|
|||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
labels={projectLabels ?? []}
|
||||
states={projectStates}
|
||||
/>
|
||||
{isEditingAllowed && (
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
filterParams={{
|
||||
filters: appliedFilters,
|
||||
display_filters: issueFilters?.displayFilters,
|
||||
display_properties: issueFilters?.displayProperties,
|
||||
}}
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<Header.LeftItem>
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
labels={projectLabels ?? []}
|
||||
states={projectStates}
|
||||
/>
|
||||
)}
|
||||
</CustomHeader>
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem>
|
||||
{isEditingAllowed && (
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
filterParams={{
|
||||
filters: appliedFilters,
|
||||
display_filters: issueFilters?.displayFilters,
|
||||
display_properties: issueFilters?.displayProperties,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Header.RightItem>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useParams } from "next/navigation";
|
|||
// types
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { AppliedFiltersList } from "@/components/issues";
|
||||
import { CreateUpdateProjectViewModal } from "@/components/views";
|
||||
import { UpdateViewComponent } from "@/components/views/update-view-component";
|
||||
|
|
@ -114,7 +114,7 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||
const isOwner = viewDetails?.owned_by === data?.id;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<CreateUpdateProjectViewModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
|
|
@ -128,7 +128,7 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||
...viewFilters,
|
||||
}}
|
||||
/>
|
||||
<CustomHeader.LeftItem>
|
||||
<Header.LeftItem className="w-[70%]">
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters ?? {}}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
|
|
@ -137,15 +137,17 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||
states={projectStates}
|
||||
disableEditing={isLocked}
|
||||
/>
|
||||
</CustomHeader.LeftItem>
|
||||
<UpdateViewComponent
|
||||
isLocked={isLocked}
|
||||
areFiltersEqual={!!areFiltersEqual}
|
||||
isOwner={isOwner}
|
||||
isAuthorizedUser={isAuthorizedUser}
|
||||
setIsModalOpen={setIsModalOpen}
|
||||
handleUpdateView={handleUpdateView}
|
||||
/>
|
||||
</CustomHeader>
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem>
|
||||
<UpdateViewComponent
|
||||
isLocked={isLocked}
|
||||
areFiltersEqual={!!areFiltersEqual}
|
||||
isOwner={isOwner}
|
||||
isAuthorizedUser={isAuthorizedUser}
|
||||
setIsModalOpen={setIsModalOpen}
|
||||
handleUpdateView={handleUpdateView}
|
||||
/>
|
||||
</Header.RightItem>
|
||||
</Header>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = observer((props) => {
|
|||
<DropIndicator isVisible={!isCurrentBlockDragging && isDraggingOverBlock} />
|
||||
<div
|
||||
// make Z-index higher at the beginning of drag, to have a issue drag image of issue block without any overlaps
|
||||
className={cn("group/kanban-block relative p-1.5", { "z-[1]": isCurrentBlockDragging })}
|
||||
className={cn("group/kanban-block relative mb-2", { "z-[1]": isCurrentBlockDragging })}
|
||||
onDragStart={() => {
|
||||
if (isDragAllowed) setIsCurrentBlockDragging(true);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from "@plane/types";
|
||||
// constants
|
||||
// hooks
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { useCycle, useKanbanView, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
// types
|
||||
|
|
@ -134,7 +135,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||
const isGroupByCreatedBy = group_by === "created_by";
|
||||
|
||||
return (
|
||||
<div className={`relative w-full flex gap-2 px-2 ${sub_group_by ? "h-full" : "h-full"}`}>
|
||||
<ContentWrapper className={`flex-row relative gap-4`}>
|
||||
{list &&
|
||||
list.length > 0 &&
|
||||
list.map((subList: IGroupByColumn) => {
|
||||
|
|
@ -149,7 +150,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||
} `}
|
||||
>
|
||||
{sub_group_by === null && (
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 bg-custom-background-90 py-1">
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 bg-custom-background-90 py-1 mb-1">
|
||||
<HeaderGroupByCard
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
|
|
@ -193,6 +194,6 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
|||
/>
|
||||
)}
|
||||
<div
|
||||
className={`relative flex flex-shrink-0 gap-2 p-1.5 ${
|
||||
className={`relative flex flex-shrink-0 gap-2 py-1.5 ${
|
||||
verticalAlignPosition ? `w-[44px] flex-col items-center` : `w-full flex-row items-center`
|
||||
}`}
|
||||
>
|
||||
<div className="flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm">
|
||||
<div className="flex h-[20px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm">
|
||||
{icon ? icon : <Circle width={14} strokeWidth={2} />}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -217,8 +217,8 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
|||
const isSubGroup = !!sub_group_id && sub_group_id !== "null";
|
||||
|
||||
const issueIds = isSubGroup
|
||||
? (groupedIssueIds as TSubGroupedIssues)?.[groupId]?.[sub_group_id] ?? []
|
||||
: (groupedIssueIds as TGroupedIssues)?.[groupId] ?? [];
|
||||
? ((groupedIssueIds as TSubGroupedIssues)?.[groupId]?.[sub_group_id] ?? [])
|
||||
: ((groupedIssueIds as TGroupedIssues)?.[groupId] ?? []);
|
||||
|
||||
const groupIssueCount = getGroupIssueCount(groupId, sub_group_id, false) ?? 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ChevronRight } from "lucide-react";
|
|||
// types
|
||||
import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types";
|
||||
// ui
|
||||
import { Spinner, Tooltip, ControlLink, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
import { Spinner, Tooltip, ControlLink, setToast, TOAST_TYPE, Row } from "@plane/ui";
|
||||
// components
|
||||
import { MultipleSelectEntityAction } from "@/components/core";
|
||||
import { IssueProperties } from "@/components/issues/issue-layouts/properties";
|
||||
|
|
@ -130,13 +130,13 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||
};
|
||||
|
||||
//TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier
|
||||
const keyMinWidth = ((projectIdentifier?.length ?? 0) + 5) * 7;
|
||||
const keyMinWidth = (projectIdentifier?.length ?? 0) * 7;
|
||||
|
||||
return (
|
||||
<div
|
||||
<Row
|
||||
ref={issueRef}
|
||||
className={cn(
|
||||
"group/list-block min-h-11 relative flex flex-col gap-3 bg-custom-background-100 hover:bg-custom-background-90 p-3 pl-1.5 text-sm transition-colors border border-transparent",
|
||||
"group/list-block min-h-11 relative flex flex-col gap-3 bg-custom-background-100 hover:bg-custom-background-90 py-3 text-sm transition-colors border border-transparent",
|
||||
{
|
||||
"border-custom-primary-70": getIsIssuePeeked(issue.id) && peekIssue?.nestingLevel === nestingLevel,
|
||||
"border-custom-border-400": isIssueActive,
|
||||
|
|
@ -173,7 +173,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||
disabled={issue.project_id === projectId}
|
||||
renderByDefault={false}
|
||||
>
|
||||
<div className="flex-shrink-0 grid place-items-center w-3.5">
|
||||
<div className="flex-shrink-0 grid place-items-center w-3.5 absolute left-1">
|
||||
<MultipleSelectEntityAction
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||
|
|
@ -190,7 +190,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||
</Tooltip>
|
||||
)}
|
||||
{displayProperties && displayProperties?.key && (
|
||||
<div className="flex-shrink-0 pl-2" style={{ minWidth: `${keyMinWidth}px` }}>
|
||||
<div className="flex-shrink-0" style={{ minWidth: `${keyMinWidth}px` }}>
|
||||
{issue.project_id && (
|
||||
<IssueIdentifier
|
||||
issueId={issueId}
|
||||
|
|
@ -293,6 +293,6 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -86,12 +86,12 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="group/list-header relative w-full flex-shrink-0 flex items-center gap-2 py-1.5">
|
||||
<div className="group/list-header w-full flex-shrink-0 flex items-center gap-2 py-1.5">
|
||||
{canSelectIssues && (
|
||||
<div className="flex-shrink-0 flex items-center w-3.5">
|
||||
<div className="flex-shrink-0 flex items-center w-3.5 absolute left-1">
|
||||
<MultipleSelectGroupAction
|
||||
className={cn(
|
||||
"size-3.5 opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto !outline-none",
|
||||
"size-3.5 opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto !outline-none ",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": !isGroupSelectionEmpty,
|
||||
}
|
||||
|
|
@ -106,8 +106,10 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
|||
{icon ?? <CircleDashed className="size-3.5" strokeWidth={2} />}
|
||||
</div>
|
||||
|
||||
<div className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
|
||||
onClick={toggleListGroup}>
|
||||
<div
|
||||
className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
|
||||
onClick={toggleListGroup}
|
||||
>
|
||||
<div className="inline-block line-clamp-1 truncate font-medium text-custom-text-100">{title}</div>
|
||||
<div className="pl-2 text-sm font-medium text-custom-text-300">{count || 0}</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
TIssue,
|
||||
IIssueDisplayProperties,
|
||||
} from "@plane/types";
|
||||
import { setToast, TOAST_TYPE } from "@plane/ui";
|
||||
import { Row, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
// components
|
||||
import { ListLoaderItemRow } from "@/components/ui";
|
||||
// constants
|
||||
|
|
@ -230,7 +230,7 @@ export const ListGroup = observer((props: Props) => {
|
|||
"border-custom-error-200": isDraggingOverColumn && !!group.isDropDisabled,
|
||||
})}
|
||||
>
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 pl-2 pr-3 py-1">
|
||||
<Row className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 pr-3 py-1">
|
||||
<HeaderGroupByCard
|
||||
groupID={group.id}
|
||||
icon={group.icon}
|
||||
|
|
@ -243,7 +243,7 @@ export const ListGroup = observer((props: Props) => {
|
|||
selectionHelpers={selectionHelpers}
|
||||
toggleListGroup={toggleListGroup}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
{shouldExpand && (
|
||||
<div className="relative">
|
||||
<GroupDragOverlay
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export interface IIssuePropertyLabels {
|
|||
placeholderText?: string;
|
||||
onClose?: () => void;
|
||||
renderByDefault?: boolean;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((props) => {
|
||||
|
|
@ -52,6 +53,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
noLabelBorder = false,
|
||||
placeholderText,
|
||||
renderByDefault = true,
|
||||
fullWidth = false,
|
||||
} = props;
|
||||
// router
|
||||
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
||||
|
|
@ -149,7 +151,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase()));
|
||||
|
||||
const label = (
|
||||
<div className="flex h-5 w-full flex-wrap items-center gap-2 overflow-hidden">
|
||||
<div className="flex h-full w-full flex-wrap items-center gap-2 overflow-hidden">
|
||||
{value.length > 0 ? (
|
||||
value.length <= maxRender ? (
|
||||
<>
|
||||
|
|
@ -168,7 +170,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
key={label?.id}
|
||||
className={`flex overflow-hidden hover:bg-custom-background-80 ${
|
||||
!disabled && "cursor-pointer"
|
||||
} h-full max-w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 px-2.5 py-1 text-xs`}
|
||||
} h-full ${fullWidth && "w-full"} max-w-full flex-shrink-0 items-center rounded px-2.5 text-xs ${noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300"}`}
|
||||
>
|
||||
<div className="flex max-w-full items-center gap-1.5 overflow-hidden text-custom-text-200">
|
||||
<span
|
||||
|
|
@ -185,9 +187,9 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
</>
|
||||
) : (
|
||||
<div
|
||||
className={`flex h-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 px-2.5 py-1 text-xs ${
|
||||
className={`flex h-full ${fullWidth && "w-full"} flex-shrink-0 items-center rounded px-2.5 text-xs ${
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
}`}
|
||||
} ${noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300"}`}
|
||||
>
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
|
|
@ -215,8 +217,8 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
renderByDefault={false}
|
||||
>
|
||||
<div
|
||||
className={`flex h-full items-center justify-center gap-2 rounded px-2.5 py-1 text-xs hover:bg-custom-background-80 ${
|
||||
noLabelBorder ? "" : "border-[0.5px] border-custom-border-300"
|
||||
className={`flex h-full ${fullWidth && "w-full"} items-center justify-center gap-2 rounded px-2.5 py-1 text-xs hover:bg-custom-background-80 ${
|
||||
noLabelBorder ? "rounded-none" : "border-[0.5px] border-custom-border-300"
|
||||
}`}
|
||||
>
|
||||
<Tags className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
|
|
@ -231,7 +233,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
<button
|
||||
ref={setReferenceElement}
|
||||
type="button"
|
||||
className={`clickable flex w-full items-center justify-between gap-1 text-xs ${
|
||||
className={`clickable flex w-full h-full items-center justify-between gap-1 text-xs ${fullWidth && "hover:bg-custom-background-80"} ${
|
||||
disabled
|
||||
? "cursor-not-allowed text-custom-text-200"
|
||||
: value.length <= maxRender
|
||||
|
|
@ -249,7 +251,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
<ComboDropDown
|
||||
as="div"
|
||||
ref={dropdownRef}
|
||||
className={`w-auto max-w-full flex-shrink-0 text-left ${className}`}
|
||||
className={`w-auto max-w-full h-full flex-shrink-0 text-left ${className}`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
|
@ -261,7 +263,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
{isOpen && (
|
||||
<Combobox.Options className="fixed z-10" static>
|
||||
<div
|
||||
className={`z-10 my-1 w-48 whitespace-nowrap rounded border border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none ${optionsClassName}`}
|
||||
className={`z-10 my-1 w-48 h-auto whitespace-nowrap rounded border border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none ${optionsClassName}`}
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { Row } from "@plane/ui";
|
||||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const GanttQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
|
|
@ -9,11 +10,13 @@ export const GanttQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((prop
|
|||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="sticky bottom-0 z-[1] flex w-full cursor-pointer items-center gap-2 border-t-[1px] border-custom-border-200 bg-custom-background-100 px-3 pt-2 text-custom-text-350 hover:text-custom-text-300"
|
||||
className="sticky bottom-0 z-[1] flex w-full cursor-pointer items-center border-t-[1px] border-custom-border-200 bg-custom-background-100 text-custom-text-350 hover:text-custom-text-300"
|
||||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
<Row className="flex py-2 gap-2">
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2 my-auto" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
</Row>
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { Row } from "@plane/ui";
|
||||
import { TQuickAddIssueButton } from "../root";
|
||||
|
||||
export const ListQuickAddIssueButton: FC<TQuickAddIssueButton> = observer((props) => {
|
||||
const { onClick } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex w-full cursor-pointer items-center gap-2 px-2 py-3 text-custom-text-350 hover:text-custom-text-300"
|
||||
<Row
|
||||
className="flex w-full cursor-pointer items-center gap-2 py-3 text-custom-text-350 hover:text-custom-text-300"
|
||||
onClick={onClick}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
<span className="text-sm font-medium">New Issue</span>
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React from "react"
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// mobx store
|
||||
// components
|
||||
import { Row, ERowVariant } from "@plane/ui";
|
||||
import {
|
||||
IssuePeekOverview,
|
||||
ModuleAppliedFiltersRoot,
|
||||
|
|
@ -62,9 +63,9 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
|
|||
<IssuesStoreContext.Provider value={EIssuesStoreType.MODULE}>
|
||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||
<ModuleAppliedFiltersRoot />
|
||||
<div className="h-full w-full overflow-auto">
|
||||
<Row variant={ERowVariant.HUGGING} className="h-full w-full overflow-auto">
|
||||
<ModuleIssueLayout activeLayout={activeLayout} moduleId={moduleId?.toString()} />
|
||||
</div>
|
||||
</Row>
|
||||
{/* peek overview */}
|
||||
<IssuePeekOverview />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export const SpreadsheetAssigneeColumn: React.FC<Props> = observer((props: Props
|
|||
buttonVariant={
|
||||
issue?.assignee_ids && issue.assignee_ids.length > 1 ? "transparent-without-text" : "transparent-with-text"
|
||||
}
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonContainerClassName="w-full"
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { Row } from "@plane/ui";
|
||||
|
||||
type Props = {
|
||||
issue: TIssue;
|
||||
|
|
@ -11,8 +12,8 @@ export const SpreadsheetAttachmentColumn: React.FC<Props> = observer((props) =>
|
|||
const { issue } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
<Row className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
{issue?.attachment_count} {issue?.attachment_count === 1 ? "attachment" : "attachments"}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// helpers
|
||||
import { Row } from "@plane/ui";
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -13,8 +14,8 @@ export const SpreadsheetCreatedOnColumn: React.FC<Props> = observer((props: Prop
|
|||
const { issue } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
<Row className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
{renderFormattedDate(issue.created_at)}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ export const SpreadsheetCycleColumn: React.FC<Props> = observer((props) => {
|
|||
disabled={disabled}
|
||||
placeholder="Select cycle"
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonContainerClassName="w-full relative flex items-center p-2 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="relative leading-4 h-4.5 bg-transparent hover:bg-transparent"
|
||||
buttonContainerClassName="w-full relative flex items-center p-2 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonClassName="relative leading-4 h-4.5 bg-transparent hover:bg-transparent px-0"
|
||||
onClose={onClose}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export const SpreadsheetDueDateColumn: React.FC<Props> = observer((props: Props)
|
|||
buttonVariant="transparent-with-text"
|
||||
buttonContainerClassName="w-full"
|
||||
buttonClassName={cn(
|
||||
"rounded-none text-left group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10",
|
||||
"rounded-none text-left group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x",
|
||||
{
|
||||
"text-red-500": shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const SpreadsheetEstimateColumn: React.FC<Props> = observer((props: Props
|
|||
projectId={issue.project_id ?? undefined}
|
||||
disabled={disabled}
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonContainerClassName="w-full"
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
MoveRight,
|
||||
} from "lucide-react";
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssueOrderByOptions } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
import { CustomMenu, Row } from "@plane/ui";
|
||||
//hooks
|
||||
import { SPREADSHEET_PROPERTY_DETAILS } from "@/constants/spreadsheet";
|
||||
import useLocalStorage from "@/hooks/use-local-storage";
|
||||
|
|
@ -51,7 +51,7 @@ export const HeaderColumn = (props: Props) => {
|
|||
customButtonTabIndex={-1}
|
||||
className="!w-full"
|
||||
customButton={
|
||||
<div className="flex w-full cursor-pointer items-center justify-between gap-1.5 py-2 text-sm text-custom-text-200 hover:text-custom-text-100">
|
||||
<Row className="flex w-full cursor-pointer items-center justify-between gap-1.5 py-2 text-sm text-custom-text-200 hover:text-custom-text-100">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{<propertyDetails.icon className="h-4 w-4 text-custom-text-400" />}
|
||||
{propertyDetails.title}
|
||||
|
|
@ -64,7 +64,7 @@ export const HeaderColumn = (props: Props) => {
|
|||
)}
|
||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
}
|
||||
onMenuClose={onClose}
|
||||
placement="bottom-start"
|
||||
|
|
|
|||
|
|
@ -22,18 +22,22 @@ export const SpreadsheetLabelColumn: React.FC<Props> = observer((props: Props) =
|
|||
const defaultLabelOptions = issue?.label_ids?.map((id) => labelMap[id]) || [];
|
||||
|
||||
return (
|
||||
<IssuePropertyLabels
|
||||
projectId={issue.project_id ?? null}
|
||||
value={issue.label_ids}
|
||||
defaultOptions={defaultLabelOptions}
|
||||
onChange={(data) => onChange(issue, { label_ids: data }, { changed_property: "labels", change_details: data })}
|
||||
className="h-11 w-full border-b-[0.5px] border-custom-border-200"
|
||||
buttonClassName="px-2.5 h-full group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
hideDropdownArrow
|
||||
maxRender={1}
|
||||
disabled={disabled}
|
||||
placeholderText="Select labels"
|
||||
onClose={onClose}
|
||||
/>
|
||||
<div className="h-11 border-b-[0.5px] border-custom-border-200 w-full">
|
||||
<IssuePropertyLabels
|
||||
projectId={issue.project_id ?? null}
|
||||
value={issue.label_ids}
|
||||
defaultOptions={defaultLabelOptions}
|
||||
onChange={(data) => onChange(issue, { label_ids: data }, { changed_property: "labels", change_details: data })}
|
||||
className="h-full w-full "
|
||||
buttonClassName="px-page-x w-full h-full group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 rounded-none"
|
||||
hideDropdownArrow
|
||||
maxRender={1}
|
||||
disabled={disabled}
|
||||
placeholderText="Select labels"
|
||||
onClose={onClose}
|
||||
noLabelBorder
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { Row } from "@plane/ui";
|
||||
|
||||
type Props = {
|
||||
issue: TIssue;
|
||||
|
|
@ -11,8 +12,8 @@ export const SpreadsheetLinkColumn: React.FC<Props> = observer((props: Props) =>
|
|||
const { issue } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
<Row className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x">
|
||||
{issue?.link_count} {issue?.link_count === 1 ? "link" : "links"}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ export const SpreadsheetModuleColumn: React.FC<Props> = observer((props) => {
|
|||
disabled={disabled}
|
||||
placeholder="Select modules"
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonContainerClassName="w-full relative flex items-center p-2 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="relative leading-4 h-4.5 bg-transparent hover:bg-transparent"
|
||||
buttonContainerClassName="w-full relative flex items-center p-2 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonClassName="relative leading-4 h-4.5 bg-transparent hover:bg-transparent !px-0"
|
||||
onClose={onClose}
|
||||
multiple
|
||||
showCount
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const SpreadsheetPriorityColumn: React.FC<Props> = observer((props: Props
|
|||
onChange={(data) => onChange(issue, { priority: data }, { changed_property: "priority", change_details: data })}
|
||||
disabled={disabled}
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonContainerClassName="w-full"
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const SpreadsheetStartDateColumn: React.FC<Props> = observer((props: Prop
|
|||
placeholder="Start date"
|
||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonContainerClassName="w-full"
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export const SpreadsheetStateColumn: React.FC<Props> = observer((props) => {
|
|||
onChange={(data) => onChange(issue, { state_id: data }, { changed_property: "state", change_details: data })}
|
||||
disabled={disabled}
|
||||
buttonVariant="transparent-with-text"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10"
|
||||
buttonClassName="text-left rounded-none group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10 px-page-x"
|
||||
buttonContainerClassName="w-full"
|
||||
onClose={onClose}
|
||||
showTooltip
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useParams } from "next/navigation";
|
|||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// helpers
|
||||
import { Row } from "@plane/ui";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
|
|
@ -22,20 +23,22 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
|||
const subIssueCount = issue?.sub_issues_count ?? 0;
|
||||
|
||||
const redirectToIssueDetail = () => {
|
||||
router.push(`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${issue.id}#sub-issues`);
|
||||
router.push(
|
||||
`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}issues/${issue.id}#sub-issues`
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
<Row
|
||||
onClick={subIssueCount ? redirectToIssueDetail : () => {}}
|
||||
className={cn(
|
||||
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10",
|
||||
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10",
|
||||
{
|
||||
"cursor-pointer": subIssueCount,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{subIssueCount} {subIssueCount === 1 ? "sub-issue" : "sub-issues"}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react";
|
|||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// helpers
|
||||
import { Row } from "@plane/ui";
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -13,8 +14,8 @@ export const SpreadsheetUpdatedOnColumn: React.FC<Props> = observer((props: Prop
|
|||
const { issue } = props;
|
||||
|
||||
return (
|
||||
<div className="flex h-11 w-full items-center justify-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
<Row className="flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10">
|
||||
{renderFormattedDate(issue.updated_at)}
|
||||
</div>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|||
// types
|
||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
// ui
|
||||
import { ControlLink, Tooltip } from "@plane/ui";
|
||||
import { ControlLink, Row, Tooltip } from "@plane/ui";
|
||||
// components
|
||||
import { MultipleSelectEntityAction } from "@/components/core";
|
||||
import RenderIfVisible from "@/components/core/render-if-visible-HOC";
|
||||
|
|
@ -237,7 +237,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||
id={`issue-${issueId}`}
|
||||
ref={cellRef}
|
||||
tabIndex={0}
|
||||
className="sticky left-0 z-10 group/list-block bg-custom-background-100"
|
||||
className="relative md:sticky left-0 z-10 group/list-block bg-custom-background-100"
|
||||
>
|
||||
<ControlLink
|
||||
href={`/${workspaceSlug}/projects/${issueDetail.project_id}/issues/${issueId}`}
|
||||
|
|
@ -253,97 +253,99 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||
)}
|
||||
disabled={!!issueDetail?.tempId}
|
||||
>
|
||||
<div className="flex items-center gap-0.5 min-w-min py-2.5 pl-2">
|
||||
{/* select checkbox */}
|
||||
{projectId && canSelectIssues && (
|
||||
<Tooltip
|
||||
tooltipContent={
|
||||
<>
|
||||
Only issues within the current
|
||||
<br />
|
||||
project can be selected.
|
||||
</>
|
||||
}
|
||||
disabled={issueDetail.project_id === projectId}
|
||||
>
|
||||
<div className="flex-shrink-0 grid place-items-center w-3.5 mr-1">
|
||||
<MultipleSelectEntityAction
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": isIssueSelected,
|
||||
}
|
||||
)}
|
||||
groupId={SPREADSHEET_SELECT_GROUP}
|
||||
id={issueDetail.id}
|
||||
selectionHelpers={selectionHelpers}
|
||||
disabled={issueDetail.project_id !== projectId}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* sub issues indentation */}
|
||||
<div style={nestingLevel !== 0 ? { width: subIssueIndentation } : {}} />
|
||||
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="key">
|
||||
<div className="relative flex cursor-pointer items-center text-center text-xs hover:text-custom-text-100">
|
||||
<p className={`flex font-medium leading-7`} style={{ minWidth: `${keyMinWidth}px` }}>
|
||||
{issueDetail.project_id && (
|
||||
<IssueIdentifier
|
||||
issueId={issueDetail.id}
|
||||
projectId={issueDetail.project_id}
|
||||
textContainerClassName="text-sm md:text-xs text-custom-text-300"
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
|
||||
{/* sub-issues chevron */}
|
||||
<div className="grid place-items-center size-4">
|
||||
{subIssuesCount > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid place-items-center size-4 rounded-sm text-custom-text-400 hover:text-custom-text-300"
|
||||
onClick={handleToggleExpand}
|
||||
<Row className="flex item-center flex-row w-full">
|
||||
<div className="flex items-center gap-0.5 min-w-min py-2.5">
|
||||
{/* select checkbox */}
|
||||
{projectId && canSelectIssues && (
|
||||
<Tooltip
|
||||
tooltipContent={
|
||||
<>
|
||||
Only issues within the current
|
||||
<br />
|
||||
project can be selected.
|
||||
</>
|
||||
}
|
||||
disabled={issueDetail.project_id === projectId}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("size-4", {
|
||||
"rotate-90": isExpanded,
|
||||
})}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 justify-between h-full w-full pr-4 pl-1 truncate">
|
||||
<div className="w-full line-clamp-1 text-sm text-custom-text-100">
|
||||
<div className="w-full overflow-hidden">
|
||||
<Tooltip tooltipContent={issueDetail.name} isMobile={isMobile}>
|
||||
<div
|
||||
className="h-full w-full cursor-pointer truncate pr-4 text-left text-[0.825rem] text-custom-text-100 focus:outline-none"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{issueDetail.name}
|
||||
<div className="flex-shrink-0 grid place-items-center w-3.5 mr-1 absolute left-1">
|
||||
<MultipleSelectEntityAction
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none group-hover/list-block:opacity-100 group-hover/list-block:pointer-events-auto transition-opacity",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": isIssueSelected,
|
||||
}
|
||||
)}
|
||||
groupId={SPREADSHEET_SELECT_GROUP}
|
||||
id={issueDetail.id}
|
||||
selectionHelpers={selectionHelpers}
|
||||
disabled={issueDetail.project_id !== projectId}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* sub issues indentation */}
|
||||
{nestingLevel !== 0 && <div style={{ width: subIssueIndentation }} />}
|
||||
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="key">
|
||||
<div className="relative flex cursor-pointer items-center text-center text-xs hover:text-custom-text-100">
|
||||
<p className={`flex font-medium leading-7`} style={{ minWidth: `${keyMinWidth}px` }}>
|
||||
{issueDetail.project_id && (
|
||||
<IssueIdentifier
|
||||
issueId={issueDetail.id}
|
||||
projectId={issueDetail.project_id}
|
||||
textContainerClassName="text-sm md:text-xs text-custom-text-300"
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</WithDisplayPropertiesHOC>
|
||||
|
||||
{/* sub-issues chevron */}
|
||||
<div className="grid place-items-center size-4">
|
||||
{subIssuesCount > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid place-items-center size-4 rounded-sm text-custom-text-400 hover:text-custom-text-300"
|
||||
onClick={handleToggleExpand}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("size-4", {
|
||||
"rotate-90": isExpanded,
|
||||
})}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`hidden group-hover:block ${isMenuActive ? "!block" : ""}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{quickActions({
|
||||
issue: issueDetail,
|
||||
parentRef: cellRef,
|
||||
customActionButton,
|
||||
portalElement: portalElement.current,
|
||||
})}
|
||||
|
||||
<div className="flex items-center gap-2 justify-between h-full w-full truncate my-auto">
|
||||
<div className="w-full line-clamp-1 text-sm text-custom-text-100">
|
||||
<div className="w-full overflow-hidden">
|
||||
<Tooltip tooltipContent={issueDetail.name} isMobile={isMobile}>
|
||||
<div
|
||||
className="h-full w-full cursor-pointer truncate pr-4 text-left text-[0.825rem] text-custom-text-100 focus:outline-none"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{issueDetail.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`hidden group-hover:block ${isMenuActive ? "!block" : ""}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{quickActions({
|
||||
issue: issueDetail,
|
||||
parentRef: cellRef,
|
||||
customActionButton,
|
||||
portalElement: portalElement.current,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</ControlLink>
|
||||
</td>
|
||||
{/* Rest of the columns */}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const SpreadsheetHeaderColumn = observer((props: Props) => {
|
|||
shouldRenderProperty={() => shouldRenderProperty}
|
||||
>
|
||||
<th
|
||||
className="h-11 w-full min-w-36 max-w-48 items-center bg-custom-background-90 text-sm font-medium px-4 py-1 border border-b-0 border-t-0 border-custom-border-100"
|
||||
className="h-11 w-full min-w-36 max-w-48 items-center bg-custom-background-90 text-sm font-medium py-1 border border-b-0 border-t-0 border-custom-border-100"
|
||||
ref={tableHeaderCellRef}
|
||||
tabIndex={0}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useParams } from "next/navigation";
|
|||
// ui
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types";
|
||||
// components
|
||||
import { Row } from "@plane/ui";
|
||||
import { MultipleSelectGroupAction } from "@/components/core";
|
||||
import { SpreadsheetHeaderColumn } from "@/components/issues/issue-layouts";
|
||||
// constants
|
||||
|
|
@ -43,24 +44,26 @@ export const SpreadsheetHeader = observer((props: Props) => {
|
|||
<thead className="sticky top-0 left-0 z-[12] border-b-[0.5px] border-custom-border-100">
|
||||
<tr>
|
||||
<th
|
||||
className="group/list-header sticky left-0 z-[15] h-11 w-[28rem] flex items-center gap-1 bg-custom-background-90 text-sm font-medium before:absolute before:h-full before:right-0 before:border-[0.5px] before:border-custom-border-100 pl-2"
|
||||
className="group/list-header sticky left-0 z-[15] h-11 w-[28rem] flex items-center gap-1 bg-custom-background-90 text-sm font-medium before:absolute before:h-full before:right-0 before:border-custom-border-100"
|
||||
tabIndex={-1}
|
||||
>
|
||||
{canSelectIssues && (
|
||||
<div className="flex-shrink-0 flex items-center w-3.5 mr-1">
|
||||
<MultipleSelectGroupAction
|
||||
className={cn(
|
||||
"size-3.5 opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto !outline-none",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": !isGroupSelectionEmpty,
|
||||
}
|
||||
)}
|
||||
groupID={SPREADSHEET_SELECT_GROUP}
|
||||
selectionHelpers={selectionHelpers}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="flex h-full w-full flex-grow items-center py-2.5">Issues</span>
|
||||
<Row>
|
||||
{canSelectIssues && (
|
||||
<div className="flex-shrink-0 flex items-center w-3.5 mr-1 absolute left-1 py-[11px]">
|
||||
<MultipleSelectGroupAction
|
||||
className={cn(
|
||||
"size-3.5 opacity-0 pointer-events-none group-hover/list-header:opacity-100 group-hover/list-header:pointer-events-auto !outline-none",
|
||||
{
|
||||
"opacity-100 pointer-events-auto": !isGroupSelectionEmpty,
|
||||
}
|
||||
)}
|
||||
groupID={SPREADSHEET_SELECT_GROUP}
|
||||
selectionHelpers={selectionHelpers}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="flex h-full w-full flex-grow items-center py-2.5">Issues</span>
|
||||
</Row>
|
||||
</th>
|
||||
|
||||
{spreadsheetColumnsList.map((property) => (
|
||||
|
|
|
|||
|
|
@ -275,14 +275,14 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
|||
|
||||
let response: TIssue | undefined = undefined;
|
||||
|
||||
try{
|
||||
try {
|
||||
if (!data?.id) response = await handleCreateIssue(payload, is_draft_issue);
|
||||
else response = await handleUpdateIssue(payload);
|
||||
}catch(error){
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}finally{
|
||||
if (response != undefined && onSubmit) await onSubmit(response)
|
||||
}
|
||||
} finally {
|
||||
if (response != undefined && onSubmit) await onSubmit(response);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormChange = (formData: Partial<TIssue> | null) => setChangesMade(formData);
|
||||
|
|
|
|||
|
|
@ -158,7 +158,6 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
}, [data, projectId]);
|
||||
|
||||
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue = false) => {
|
||||
|
||||
// Check if the editor is ready to discard
|
||||
if (!editorRef.current?.isEditorReadyToDiscard()) {
|
||||
setToast({
|
||||
|
|
@ -190,9 +189,9 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
|
||||
// this condition helps to move the issues from draft to project issues
|
||||
if (formData.hasOwnProperty("is_draft")) submitData.is_draft = formData.is_draft;
|
||||
|
||||
|
||||
await onSubmit(submitData, is_draft_issue)
|
||||
.then(() =>{
|
||||
.then(() => {
|
||||
setGptAssistantModal(false);
|
||||
reset({
|
||||
...defaultValues,
|
||||
|
|
@ -203,8 +202,9 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||
});
|
||||
editorRef?.current?.clearEditor();
|
||||
})
|
||||
.catch((error) => {})
|
||||
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const condition =
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<div key={memberId} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs">
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} />
|
||||
<Avatar name={memberDetails.display_name} src={memberDetails.avatar} showTooltip={false} size={"sm"} />
|
||||
<span className="normal-case">{memberDetails.display_name}</span>
|
||||
{editable && (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { X } from "lucide-react";
|
||||
import { TModuleDisplayFilters, TModuleFilters } from "@plane/types";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant, Tag } from "@plane/ui";
|
||||
import { AppliedDateFilters, AppliedMembersFilters, AppliedStatusFilters } from "@/components/modules";
|
||||
// helpers
|
||||
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
||||
|
|
@ -37,8 +37,8 @@ export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
|||
const isEditingAllowed = alwaysAllowEditing;
|
||||
|
||||
return (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY} className="flex flex-wrap gap-2">
|
||||
<CustomHeader.LeftItem>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{Object.entries(appliedFilters).map(([key, value]) => {
|
||||
const filterKey = key as keyof TModuleFilters;
|
||||
|
||||
|
|
@ -46,10 +46,7 @@ export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
|||
if (Array.isArray(value) && value.length === 0) return;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={filterKey}
|
||||
className="flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 capitalize"
|
||||
>
|
||||
<Tag key={filterKey}>
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-xs text-custom-text-300">{replaceUnderscoreIfSnakeCase(filterKey)}</span>
|
||||
{filterKey === "status" && (
|
||||
|
|
@ -83,7 +80,7 @@ export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
|||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
{!isArchived && isFavoriteFilterApplied && (
|
||||
|
|
@ -114,16 +111,14 @@ export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
)}
|
||||
{isEditingAllowed && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClearAllFilters}
|
||||
className="flex items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 text-xs text-custom-text-300 hover:text-custom-text-200"
|
||||
>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
<button type="button" onClick={handleClearAllFilters}>
|
||||
<Tag>
|
||||
Clear all
|
||||
<X size={12} strokeWidth={2} />
|
||||
</Tag>
|
||||
</button>
|
||||
)}
|
||||
</CustomHeader.LeftItem>
|
||||
</CustomHeader>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,22 +57,20 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
|
|||
if (!filteredModuleIds) return null;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full overflow-y-auto">
|
||||
<GanttChartRoot
|
||||
title="Modules"
|
||||
loaderTitle="Modules"
|
||||
blockIds={filteredModuleIds}
|
||||
getBlockById={getBlockById}
|
||||
sidebarToRender={(props) => <ModuleGanttSidebar {...props} />}
|
||||
blockUpdateHandler={(block, payload) => handleModuleUpdate(block, payload)}
|
||||
blockToRender={(data: IModule) => <ModuleGanttBlock moduleId={data.id} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
enableReorder={isAllowed && displayFilters?.order_by === "sort_order"}
|
||||
enableAddBlock={isAllowed}
|
||||
showAllBlocks
|
||||
/>
|
||||
</div>
|
||||
<GanttChartRoot
|
||||
title="Modules"
|
||||
loaderTitle="Modules"
|
||||
blockIds={filteredModuleIds}
|
||||
getBlockById={getBlockById}
|
||||
sidebarToRender={(props) => <ModuleGanttSidebar {...props} />}
|
||||
blockUpdateHandler={(block, payload) => handleModuleUpdate(block, payload)}
|
||||
blockToRender={(data: IModule) => <ModuleGanttBlock moduleId={data.id} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
enableReorder={isAllowed && displayFilters?.order_by === "sort_order"}
|
||||
enableAddBlock={isAllowed}
|
||||
showAllBlocks
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useParams, usePathname, useSearchParams } from "next/navigation";
|
|||
import { CalendarCheck2, CalendarClock, Info, MoveRight, SquareUser } from "lucide-react";
|
||||
// ui
|
||||
import { IModule } from "@plane/types";
|
||||
import { FavoriteStar, LayersIcon, LinearProgressIndicator, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
import { Card, FavoriteStar, LayersIcon, LinearProgressIndicator, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
// components
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
|
|
@ -176,7 +176,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
return (
|
||||
<div className="relative">
|
||||
<Link ref={parentRef} href={`/${workspaceSlug}/projects/${moduleDetails.project_id}/modules/${moduleDetails.id}`}>
|
||||
<div className="flex h-44 w-full flex-col justify-between rounded border border-custom-border-100 bg-custom-background-100 p-4 text-sm hover:shadow-md">
|
||||
<Card>
|
||||
<div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Tooltip tooltipContent={moduleDetails.name} position="top" isMobile={isMobile}>
|
||||
|
|
@ -200,7 +200,6 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-1.5 text-custom-text-200">
|
||||
|
|
@ -232,7 +231,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<div className="absolute right-4 bottom-[18px] flex items-center gap-1.5">
|
||||
{isEditingAllowed && (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { observer } from "mobx-react";
|
|||
import Image from "next/image";
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
// components
|
||||
import { ContentWrapper, Row, ERowVariant } from "@plane/ui";
|
||||
import { ListLayout } from "@/components/core/list";
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "@/components/modules";
|
||||
|
|
@ -67,44 +68,34 @@ export const ModulesListView: React.FC = observer(() => {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContentWrapper variant={ERowVariant.HUGGING}>
|
||||
{displayFilters?.layout === "list" && (
|
||||
<div className="h-full overflow-y-auto">
|
||||
<div className="flex h-full w-full justify-between">
|
||||
<ListLayout>
|
||||
{filteredModuleIds.map((moduleId) => (
|
||||
<ModuleListItem key={moduleId} moduleId={moduleId} />
|
||||
))}
|
||||
</ListLayout>
|
||||
<ModulePeekOverview
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-full w-full justify-between">
|
||||
<ListLayout>
|
||||
{filteredModuleIds.map((moduleId) => (
|
||||
<ModuleListItem key={moduleId} moduleId={moduleId} />
|
||||
))}
|
||||
</ListLayout>
|
||||
<ModulePeekOverview projectId={projectId?.toString() ?? ""} workspaceSlug={workspaceSlug?.toString() ?? ""} />
|
||||
</div>
|
||||
)}
|
||||
{displayFilters?.layout === "board" && (
|
||||
<div className="h-full w-full">
|
||||
<div className="flex h-full w-full justify-between">
|
||||
<div
|
||||
className={`grid h-full w-full grid-cols-1 gap-6 overflow-y-auto p-8 ${
|
||||
peekModule
|
||||
? "lg:grid-cols-1 xl:grid-cols-2 3xl:grid-cols-3"
|
||||
: "lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4"
|
||||
} auto-rows-max transition-all vertical-scrollbar scrollbar-lg`}
|
||||
>
|
||||
{filteredModuleIds.map((moduleId) => (
|
||||
<ModuleCardItem key={moduleId} moduleId={moduleId} />
|
||||
))}
|
||||
</div>
|
||||
<ModulePeekOverview
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||
/>
|
||||
<Row className="flex h-full w-full justify-between py-page-y">
|
||||
<div
|
||||
className={`grid h-full w-full grid-cols-1 gap-6 overflow-y-auto ${
|
||||
peekModule
|
||||
? "lg:grid-cols-1 xl:grid-cols-2 3xl:grid-cols-3"
|
||||
: "lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4"
|
||||
} auto-rows-max transition-all vertical-scrollbar scrollbar-lg`}
|
||||
>
|
||||
{filteredModuleIds.map((moduleId) => (
|
||||
<ModuleCardItem key={moduleId} moduleId={moduleId} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ModulePeekOverview projectId={projectId?.toString() ?? ""} workspaceSlug={workspaceSlug?.toString() ?? ""} />
|
||||
</Row>
|
||||
)}
|
||||
{displayFilters?.layout === "gantt" && <ModulesListGanttChartView />}
|
||||
</>
|
||||
</ContentWrapper>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useEffect } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// components
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { DashboardWidgets } from "@/components/dashboard";
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { IssuePeekOverview } from "@/components/issues";
|
||||
|
|
@ -64,19 +65,16 @@ export const WorkspaceDashboardView = observer(() => {
|
|||
<>
|
||||
{joinedProjectIds.length > 0 || loader ? (
|
||||
<>
|
||||
<IssuePeekOverview shouldReplaceIssueOnFetch={false} />
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-7 md:p-7 p-3 bg-custom-background-90 h-full w-full flex flex-col overflow-y-auto",
|
||||
{
|
||||
"vertical-scrollbar scrollbar-lg": windowWidth >= 768,
|
||||
}
|
||||
)}
|
||||
<IssuePeekOverview />
|
||||
<ContentWrapper
|
||||
className={cn("gap-7", {
|
||||
"vertical-scrollbar scrollbar-lg": windowWidth >= 768,
|
||||
})}
|
||||
>
|
||||
{currentUser && <UserGreetingsView user={currentUser} />}
|
||||
|
||||
<DashboardWidgets />
|
||||
</div>
|
||||
</ContentWrapper>
|
||||
</>
|
||||
) : (
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
// types
|
||||
import { IUserLite } from "@plane/types";
|
||||
// components
|
||||
import { Row } from "@plane/ui";
|
||||
import { PageContentBrowser, PageContentLoader, PageEditorTitle } from "@/components/pages";
|
||||
// helpers
|
||||
import { cn, LIVE_URL } from "@/helpers/common.helper";
|
||||
|
|
@ -69,7 +70,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
project: { getProjectMemberIds },
|
||||
} = useMember();
|
||||
// derived values
|
||||
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
|
||||
const workspaceId = workspaceSlug ? (getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "") : "";
|
||||
const pageId = page?.id;
|
||||
const pageTitle = page?.name ?? "";
|
||||
const pageDescription = page?.description_html;
|
||||
|
|
@ -135,8 +136,8 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<div className="flex items-center h-full w-full overflow-y-auto">
|
||||
<div
|
||||
className={cn("sticky top-0 hidden h-full flex-shrink-0 -translate-x-full p-5 duration-200 md:block", {
|
||||
<Row
|
||||
className={cn("sticky top-0 hidden h-full flex-shrink-0 -translate-x-full py-5 duration-200 md:block", {
|
||||
"translate-x-0": sidePeekVisible,
|
||||
"w-[10rem] lg:w-[14rem]": !isFullWidth,
|
||||
"w-[5%]": isFullWidth,
|
||||
|
|
@ -148,7 +149,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||
markings={markings}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Row>
|
||||
<div
|
||||
className={cn("h-full w-full pt-5 duration-200", {
|
||||
"md:w-[calc(100%-10rem)] xl:w-[calc(100%-28rem)]": !isFullWidth,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||
// hooks
|
||||
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
|
|
@ -43,7 +43,7 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CustomHeader variant={EHeaderVariant.SECONDARY} className="flex justify-between">
|
||||
<Header variant={EHeaderVariant.SECONDARY}>
|
||||
<div className="flex-shrink-0 my-auto">
|
||||
<PageSummaryPopover
|
||||
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
|
||||
|
|
@ -60,12 +60,12 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
page={page}
|
||||
readOnlyEditorRef={readOnlyEditorRef}
|
||||
/>
|
||||
</CustomHeader>
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
</Header>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
{(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
|
||||
<PageToolbar editorRef={editorRef?.current} />
|
||||
)}
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
|
|
@ -45,19 +45,26 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CustomHeader variant={EHeaderVariant.SECONDARY} className="hidden md:flex justify-between">
|
||||
<div className={cn("flex-shrink-0 my-auto")}>
|
||||
<PageSummaryPopover
|
||||
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
|
||||
isFullWidth={isFullWidth}
|
||||
markings={markings}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
setSidePeekVisible={setSidePeekVisible}
|
||||
/>
|
||||
</div>
|
||||
{(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
|
||||
<PageToolbar editorRef={editorRef?.current} />
|
||||
)}
|
||||
<Header variant={EHeaderVariant.SECONDARY} showOnMobile={false}>
|
||||
<Header.LeftItem className="gap-0 w-full">
|
||||
<div
|
||||
className={cn("flex-shrink-0 my-auto", {
|
||||
"w-40 lg:w-56": !isFullWidth,
|
||||
"w-[5%]": isFullWidth,
|
||||
})}
|
||||
>
|
||||
<PageSummaryPopover
|
||||
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
|
||||
isFullWidth={isFullWidth}
|
||||
markings={markings}
|
||||
sidePeekVisible={sidePeekVisible}
|
||||
setSidePeekVisible={setSidePeekVisible}
|
||||
/>
|
||||
</div>
|
||||
{(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
|
||||
<PageToolbar editorRef={editorRef?.current} />
|
||||
)}
|
||||
</Header.LeftItem>
|
||||
<PageExtraOptions
|
||||
editorRef={editorRef}
|
||||
handleDuplicatePage={handleDuplicatePage}
|
||||
|
|
@ -65,7 +72,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
page={page}
|
||||
readOnlyEditorRef={readOnlyEditorRef}
|
||||
/>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
<div className="md:hidden">
|
||||
<PageEditorMobileHeaderRoot
|
||||
editorRef={editorRef}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { observer } from "mobx-react";
|
|||
import { ListFilter } from "lucide-react";
|
||||
import { TPageFilterProps, TPageNavigationTabs } from "@plane/types";
|
||||
// components
|
||||
import { CustomHeader, EHeaderVariant } from "@plane/ui";
|
||||
import { Header, EHeaderVariant } from "@plane/ui";
|
||||
import { FiltersDropdown } from "@/components/issues";
|
||||
import {
|
||||
PageAppliedFiltersList,
|
||||
|
|
@ -50,11 +50,11 @@ export const PagesListHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CustomHeader variant={EHeaderVariant.SECONDARY} className="flex">
|
||||
<CustomHeader.LeftItem>
|
||||
<Header variant={EHeaderVariant.SECONDARY}>
|
||||
<Header.LeftItem>
|
||||
<PageTabNavigation workspaceSlug={workspaceSlug} projectId={projectId} pageType={pageType} />
|
||||
</CustomHeader.LeftItem>
|
||||
<CustomHeader.RightItem>
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem className="items-center">
|
||||
<PageSearchInput
|
||||
searchQuery={filters.searchQuery}
|
||||
updateSearchQuery={(val) => updateFilters("searchQuery", val)}
|
||||
|
|
@ -79,17 +79,17 @@ export const PagesListHeaderRoot: React.FC<Props> = observer((props) => {
|
|||
memberIds={workspaceMemberIds ?? undefined}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</CustomHeader.RightItem>
|
||||
</CustomHeader>
|
||||
</Header.RightItem>
|
||||
</Header>
|
||||
{calculateTotalFilters(filters?.filters ?? {}) !== 0 && (
|
||||
<CustomHeader variant={EHeaderVariant.TERNARY}>
|
||||
<Header variant={EHeaderVariant.TERNARY}>
|
||||
<PageAppliedFiltersList
|
||||
appliedFilters={filters.filters ?? {}}
|
||||
handleClearAllFilters={clearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
alwaysAllowEditing
|
||||
/>
|
||||
</CustomHeader>
|
||||
</Header>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue