forked from github/plane
Merge branch 'develop' of github.com:makeplane/plane into chore/breadcrumb_component_revamp
This commit is contained in:
commit
c8f5014c21
@ -38,7 +38,7 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
|||||||
<div className="">
|
<div className="">
|
||||||
<h4 className="text-sm font-medium">Auto-archive closed issues</h4>
|
<h4 className="text-sm font-medium">Auto-archive closed issues</h4>
|
||||||
<p className="text-sm text-custom-text-200 tracking-tight">
|
<p className="text-sm text-custom-text-200 tracking-tight">
|
||||||
Plane will auto archive issues that have been completed or canceled.
|
Plane will auto archive issues that have been completed or cancelled.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -82,7 +82,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
<div className="">
|
<div className="">
|
||||||
<h4 className="text-sm font-medium">Auto-close issues</h4>
|
<h4 className="text-sm font-medium">Auto-close issues</h4>
|
||||||
<p className="text-sm text-custom-text-200 tracking-tight">
|
<p className="text-sm text-custom-text-200 tracking-tight">
|
||||||
Plane will automatically close issue that haven’t been completed or canceled.
|
Plane will automatically close issue that haven’t been completed or cancelled.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles";
|
import { CyclesBoard, CyclesList, CyclesListGanttChartView } from "components/cycles";
|
||||||
// ui components
|
// ui components
|
||||||
import { Loader } from "components/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { TCycleLayout } from "types";
|
import { TCycleLayout } from "types";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import { DangerButton, SecondaryButton } from "components/ui";
|
import { Button } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// types
|
// types
|
||||||
@ -101,10 +101,13 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||||
<DangerButton onClick={formSubmit} loading={loader}>
|
Cancel
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button variant="danger" size="sm" onClick={formSubmit}>
|
||||||
{loader ? "Deleting..." : "Delete Cycle"}
|
{loader ? "Deleting..." : "Delete Cycle"}
|
||||||
</DangerButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
|
@ -3,7 +3,7 @@ import { usePopper } from "react-popper";
|
|||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search, Triangle } from "lucide-react";
|
import { Check, ChevronDown, Search, Triangle } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { Tooltip } from "components/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
import { Placement } from "@popperjs/core";
|
import { Placement } from "@popperjs/core";
|
||||||
// constants
|
// constants
|
||||||
import { IEstimatePoint } from "types";
|
import { IEstimatePoint } from "types";
|
||||||
|
@ -7,9 +7,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { CreateUpdateProjectViewModal } from "components/views";
|
import { CreateUpdateProjectViewModal } from "components/views";
|
||||||
// components
|
// components
|
||||||
import { Breadcrumbs, PhotoFilterIcon } from "@plane/ui";
|
import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui";
|
||||||
// ui
|
|
||||||
import { PrimaryButton } from "components/ui";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
||||||
@ -69,10 +67,14 @@ export const ProjectViewsHeader: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
<div>
|
<div>
|
||||||
<PrimaryButton type="button" className="flex items-center gap-2" onClick={() => setCreateViewModal(true)}>
|
<Button
|
||||||
<Plus size={14} strokeWidth={2} />
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
prependIcon={<Plus className="h-3.5 w-3.5 stroke-2" />}
|
||||||
|
onClick={() => setCreateViewModal(true)}
|
||||||
|
>
|
||||||
Create View
|
Create View
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { AppliedFiltersList } from "components/issues";
|
import { AppliedFiltersList } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { PrimaryButton } from "components/ui";
|
import { Button } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { areFiltersDifferent } from "helpers/filter.helper";
|
import { areFiltersDifferent } from "helpers/filter.helper";
|
||||||
// types
|
// types
|
||||||
@ -102,9 +102,9 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
states={projectStore.states?.[projectId?.toString() ?? ""]}
|
states={projectStore.states?.[projectId?.toString() ?? ""]}
|
||||||
/>
|
/>
|
||||||
{storedFilters && viewDetails && areFiltersDifferent(storedFilters, viewDetails.query_data ?? {}) && (
|
{storedFilters && viewDetails && areFiltersDifferent(storedFilters, viewDetails.query_data ?? {}) && (
|
||||||
<PrimaryButton className="whitespace-nowrap" onClick={handleUpdateView}>
|
<Button variant="primary" size="sm" onClick={handleUpdateView}>
|
||||||
Update view
|
Update view
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,6 @@ import { MoreHorizontal, Pencil, Trash2, ChevronRight, Link } from "lucide-react
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||||
@ -16,10 +15,16 @@ type Props = {
|
|||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
handleToggleExpand: (issueId: string) => void;
|
handleToggleExpand: (issueId: string) => void;
|
||||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
|
||||||
properties: IIssueDisplayProperties;
|
properties: IIssueDisplayProperties;
|
||||||
handleEditIssue: (issue: IIssue) => void;
|
handleEditIssue: (issue: IIssue) => void;
|
||||||
handleDeleteIssue: (issue: IIssue) => void;
|
handleDeleteIssue: (issue: IIssue) => void;
|
||||||
|
setIssuePeekOverView: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
issueId: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
disableUserActions: boolean;
|
disableUserActions: boolean;
|
||||||
nestingLevel: number;
|
nestingLevel: number;
|
||||||
};
|
};
|
||||||
@ -28,7 +33,7 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
issue,
|
issue,
|
||||||
expanded,
|
expanded,
|
||||||
handleToggleExpand,
|
handleToggleExpand,
|
||||||
handleUpdateIssue,
|
setIssuePeekOverView,
|
||||||
properties,
|
properties,
|
||||||
handleEditIssue,
|
handleEditIssue,
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
@ -53,105 +58,116 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleIssuePeekOverview = (issue: IIssue) => {
|
||||||
|
const { query } = router;
|
||||||
|
setIssuePeekOverView({
|
||||||
|
workspaceSlug: issue?.workspace_detail?.slug,
|
||||||
|
projectId: issue?.project_detail?.id,
|
||||||
|
issueId: issue?.id,
|
||||||
|
});
|
||||||
|
router.push({
|
||||||
|
pathname: router.pathname,
|
||||||
|
query: { ...query, peekIssueId: issue?.id },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const paddingLeft = `${nestingLevel * 54}px`;
|
const paddingLeft = `${nestingLevel * 54}px`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-custom-border-100">
|
<>
|
||||||
{properties.key && (
|
<div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-custom-border-100">
|
||||||
<div
|
{properties.key && (
|
||||||
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center min-w-[96px]"
|
<div
|
||||||
style={issue.parent && nestingLevel !== 0 ? { paddingLeft } : {}}
|
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center min-w-[96px]"
|
||||||
>
|
style={issue.parent && nestingLevel !== 0 ? { paddingLeft } : {}}
|
||||||
<div className="relative flex items-center cursor-pointer text-xs text-center hover:text-custom-text-100">
|
>
|
||||||
<span className="flex items-center justify-center font-medium opacity-100 group-hover:opacity-0 ">
|
<div className="relative flex items-center cursor-pointer text-xs text-center hover:text-custom-text-100">
|
||||||
{issue.project_detail?.identifier}-{issue.sequence_id}
|
<span className="flex items-center justify-center font-medium opacity-100 group-hover:opacity-0 ">
|
||||||
</span>
|
{issue.project_detail?.identifier}-{issue.sequence_id}
|
||||||
|
</span>
|
||||||
|
|
||||||
{!disableUserActions && (
|
{!disableUserActions && (
|
||||||
<div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">
|
<div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">
|
||||||
<Popover2
|
<Popover2
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
canEscapeKeyClose
|
canEscapeKeyClose
|
||||||
onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
|
onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
|
||||||
content={
|
content={
|
||||||
<div className="flex flex-col whitespace-nowrap rounded-md border border-custom-border-100 p-1 text-xs shadow-lg focus:outline-none min-w-full bg-custom-background-100 space-y-0.5">
|
<div className="flex flex-col whitespace-nowrap rounded-md border border-custom-border-100 p-1 text-xs shadow-lg focus:outline-none min-w-full bg-custom-background-100 space-y-0.5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleCopyText();
|
handleCopyText();
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Link className="h-3 w-3" />
|
<Link className="h-3 w-3" />
|
||||||
<span>Copy link</span>
|
<span>Copy link</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleEditIssue(issue);
|
handleEditIssue(issue);
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Pencil className="h-3 w-3" />
|
<Pencil className="h-3 w-3" />
|
||||||
<span>Edit issue</span>
|
<span>Edit issue</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="w-full select-none gap-2 rounded p-1 text-left text-red-500 hover:bg-custom-background-80"
|
className="w-full select-none gap-2 rounded p-1 text-left text-red-500 hover:bg-custom-background-80"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleDeleteIssue(issue);
|
handleDeleteIssue(issue);
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Trash2 className="h-3 w-3" />
|
<Trash2 className="h-3 w-3" />
|
||||||
<span>Delete issue</span>
|
<span>Delete issue</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
|
>
|
||||||
|
<MoreHorizontal className="h-5 w-5 text-custom-text-200" />
|
||||||
|
</Popover2>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{issue.sub_issues_count > 0 && (
|
||||||
|
<div className="h-6 w-6 flex justify-center items-center">
|
||||||
|
<button
|
||||||
|
className="h-5 w-5 hover:bg-custom-background-90 hover:text-custom-text-100 rounded-sm cursor-pointer"
|
||||||
|
onClick={() => handleToggleExpand(issue.id)}
|
||||||
>
|
>
|
||||||
<MoreHorizontal className="h-5 w-5 text-custom-text-200" />
|
<ChevronRight className={`h-3.5 w-3.5 ${expanded ? "rotate-90" : ""}`} />
|
||||||
</Popover2>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{issue.sub_issues_count > 0 && (
|
<div className="w-full overflow-hidden">
|
||||||
<div className="h-6 w-6 flex justify-center items-center">
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
<button
|
<div
|
||||||
className="h-5 w-5 hover:bg-custom-background-90 hover:text-custom-text-100 rounded-sm cursor-pointer"
|
className="px-4 py-2.5 h-full w-full truncate text-custom-text-100 text-left cursor-pointer text-[0.825rem]"
|
||||||
onClick={() => handleToggleExpand(issue.id)}
|
onClick={() => handleIssuePeekOverview(issue)}
|
||||||
>
|
>
|
||||||
<ChevronRight className={`h-3.5 w-3.5 ${expanded ? "rotate-90" : ""}`} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<IssuePeekOverview
|
|
||||||
workspaceSlug={issue?.workspace_detail?.slug}
|
|
||||||
projectId={issue?.project_detail?.id}
|
|
||||||
issueId={issue?.id}
|
|
||||||
handleIssue={(issueToUpdate) => handleUpdateIssue(issueToUpdate as IIssue, issueToUpdate)}
|
|
||||||
>
|
|
||||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
|
||||||
<span className="flex items-center px-4 py-2.5 h-full truncate flex-grow">
|
|
||||||
<div className="truncate text-custom-text-100 text-left cursor-pointer w-full text-[0.825rem]">
|
|
||||||
{issue.name}
|
{issue.name}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</Tooltip>
|
||||||
</Tooltip>
|
</div>
|
||||||
</IssuePeekOverview>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,9 +11,15 @@ type Props = {
|
|||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
expandedIssues: string[];
|
expandedIssues: string[];
|
||||||
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
|
||||||
properties: IIssueDisplayProperties;
|
properties: IIssueDisplayProperties;
|
||||||
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
||||||
|
setIssuePeekOverView: React.Dispatch<
|
||||||
|
React.SetStateAction<{
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
issueId: string;
|
||||||
|
} | null>
|
||||||
|
>;
|
||||||
disableUserActions: boolean;
|
disableUserActions: boolean;
|
||||||
nestingLevel?: number;
|
nestingLevel?: number;
|
||||||
};
|
};
|
||||||
@ -22,7 +28,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
issue,
|
issue,
|
||||||
expandedIssues,
|
expandedIssues,
|
||||||
setExpandedIssues,
|
setExpandedIssues,
|
||||||
handleUpdateIssue,
|
setIssuePeekOverView,
|
||||||
properties,
|
properties,
|
||||||
handleIssueAction,
|
handleIssueAction,
|
||||||
disableUserActions,
|
disableUserActions,
|
||||||
@ -51,9 +57,9 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
expanded={isExpanded}
|
expanded={isExpanded}
|
||||||
handleToggleExpand={handleToggleExpand}
|
handleToggleExpand={handleToggleExpand}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
|
||||||
handleEditIssue={() => handleIssueAction(issue, "edit")}
|
handleEditIssue={() => handleIssueAction(issue, "edit")}
|
||||||
handleDeleteIssue={() => handleIssueAction(issue, "delete")}
|
handleDeleteIssue={() => handleIssueAction(issue, "delete")}
|
||||||
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
nestingLevel={nestingLevel}
|
nestingLevel={nestingLevel}
|
||||||
/>
|
/>
|
||||||
@ -67,10 +73,10 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
key={subIssue.id}
|
key={subIssue.id}
|
||||||
issue={subIssue}
|
issue={subIssue}
|
||||||
expandedIssues={expandedIssues}
|
expandedIssues={expandedIssues}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
|
||||||
setExpandedIssues={setExpandedIssues}
|
setExpandedIssues={setExpandedIssues}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
handleIssueAction={handleIssueAction}
|
handleIssueAction={handleIssueAction}
|
||||||
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
nestingLevel={nestingLevel + 1}
|
nestingLevel={nestingLevel + 1}
|
||||||
/>
|
/>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { PlusIcon } from "lucide-react";
|
|
||||||
// components
|
// components
|
||||||
import { SpreadsheetColumnsList, SpreadsheetIssuesColumn, SpreadsheetInlineCreateIssueForm } from "components/issues";
|
import { SpreadsheetColumnsList, SpreadsheetIssuesColumn, SpreadsheetInlineCreateIssueForm } from "components/issues";
|
||||||
import { CustomMenu, Spinner } from "@plane/ui";
|
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
||||||
|
import { Spinner } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import {
|
import {
|
||||||
IIssue,
|
IIssue,
|
||||||
@ -47,6 +47,11 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [expandedIssues, setExpandedIssues] = useState<string[]>([]);
|
const [expandedIssues, setExpandedIssues] = useState<string[]>([]);
|
||||||
|
const [issuePeekOverview, setIssuePeekOverView] = useState<{
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
issueId: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
const [isInlineCreateIssueFormOpen, setIsInlineCreateIssueFormOpen] = useState(false);
|
const [isInlineCreateIssueFormOpen, setIsInlineCreateIssueFormOpen] = useState(false);
|
||||||
|
|
||||||
@ -104,11 +109,11 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
key={`${issue.id}_${index}`}
|
key={`${issue.id}_${index}`}
|
||||||
issue={issue}
|
issue={issue}
|
||||||
expandedIssues={expandedIssues}
|
expandedIssues={expandedIssues}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
|
||||||
setExpandedIssues={setExpandedIssues}
|
setExpandedIssues={setExpandedIssues}
|
||||||
properties={displayProperties}
|
properties={displayProperties}
|
||||||
handleIssueAction={handleIssueAction}
|
handleIssueAction={handleIssueAction}
|
||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -174,6 +179,14 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
))} */}
|
))} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{issuePeekOverview && (
|
||||||
|
<IssuePeekOverview
|
||||||
|
workspaceSlug={issuePeekOverview?.workspaceSlug}
|
||||||
|
projectId={issuePeekOverview?.projectId}
|
||||||
|
issueId={issuePeekOverview?.issueId}
|
||||||
|
handleIssue={(issueToUpdate: any) => handleUpdateIssue(issueToUpdate as IIssue, issueToUpdate)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ interface IIssuePeekOverview {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
issueId: string;
|
issueId: string;
|
||||||
handleIssue: (issue: Partial<IIssue>) => void;
|
handleIssue: (issue: Partial<IIssue>) => void;
|
||||||
children: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FC, ReactNode, useState } from "react";
|
import { FC, ReactNode, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { PanelRightOpen, Square, SquareCode, MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// components
|
// components
|
||||||
@ -165,9 +165,11 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="w-full !text-base">
|
<div className="w-full !text-base">
|
||||||
<div onClick={updateRoutePeekId} className="w-full cursor-pointer">
|
{children && (
|
||||||
{children}
|
<div onClick={updateRoutePeekId} className="w-full cursor-pointer">
|
||||||
</div>
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{issueId === peekIssueId && (
|
{issueId === peekIssueId && (
|
||||||
<div
|
<div
|
||||||
|
@ -4,7 +4,7 @@ import { Placement } from "@popperjs/core";
|
|||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
import { Check, ChevronDown, Search } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { Tooltip } from "components/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssueLabels } from "types";
|
import { IIssueLabels } from "types";
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { Disclosure, Transition } from "@headlessui/react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu } from "components/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ChevronDown, Component, Pencil, Plus, Trash2, X } from "lucide-react";
|
import { ChevronDown, Component, Pencil, Plus, Trash2, X } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
|
@ -3,7 +3,7 @@ import React, { useRef, useState } from "react";
|
|||||||
//hook
|
//hook
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu } from "components/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssueLabels } from "types";
|
import { IIssueLabels } from "types";
|
||||||
//icons
|
//icons
|
||||||
|
@ -11,9 +11,9 @@ import useToast from "hooks/use-toast";
|
|||||||
// components
|
// components
|
||||||
import { ConfirmProjectMemberRemove } from "components/project";
|
import { ConfirmProjectMemberRemove } from "components/project";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, CustomSelect } from "@plane/ui";
|
import { CustomSelect, Tooltip } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ChevronDown, X } from "lucide-react";
|
import { ChevronDown, XCircle } from "lucide-react";
|
||||||
// constants
|
// constants
|
||||||
import { ROLE } from "constants/workspace";
|
import { ROLE } from "constants/workspace";
|
||||||
import { TUserProjectRole } from "types";
|
import { TUserProjectRole } from "types";
|
||||||
@ -46,9 +46,8 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
);
|
);
|
||||||
// derived values
|
// derived values
|
||||||
const user = userStore.currentUser;
|
const user = userStore.currentUser;
|
||||||
const { currentProjectRole } = userStore;
|
const { currentProjectMemberInfo, currentProjectRole } = userStore;
|
||||||
const isAdmin = currentProjectRole === 20;
|
const isAdmin = currentProjectRole === 20;
|
||||||
const isOwner = currentProjectRole === 20;
|
|
||||||
const projectMembers = projectStore.members?.[projectId?.toString()!];
|
const projectMembers = projectStore.members?.[projectId?.toString()!];
|
||||||
const currentUser = projectMembers?.find((item) => item.member.id === user?.id);
|
const currentUser = projectMembers?.find((item) => item.member.id === user?.id);
|
||||||
|
|
||||||
@ -69,7 +68,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
await projectStore.removeMemberFromProject(
|
await projectStore.removeMemberFromProject(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
projectId.toString(),
|
projectId.toString(),
|
||||||
selectedRemoveMember
|
selectedRemoveMember.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// if the user is an invite
|
// if the user is an invite
|
||||||
@ -77,7 +76,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
await projectInvitationService.deleteProjectInvitation(
|
await projectInvitationService.deleteProjectInvitation(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
projectId.toString(),
|
projectId.toString(),
|
||||||
selectedInviteRemoveMember
|
selectedInviteRemoveMember.id
|
||||||
);
|
);
|
||||||
mutate(`PROJECT_INVITATIONS_${projectId.toString()}`);
|
mutate(`PROJECT_INVITATIONS_${projectId.toString()}`);
|
||||||
}
|
}
|
||||||
@ -89,59 +88,62 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<div className="group flex items-center justify-between px-3 py-4 hover:bg-custom-background-90">
|
||||||
<div key={member.id} className="flex items-center justify-between px-3.5 py-[18px]">
|
<div className="flex items-center gap-x-4 gap-y-2">
|
||||||
<div className="flex items-center gap-x-6 gap-y-2">
|
|
||||||
{member.avatar && member.avatar !== "" ? (
|
{member.avatar && member.avatar !== "" ? (
|
||||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg p-4 capitalize text-white">
|
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||||
<img
|
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize text-white">
|
||||||
src={member.avatar}
|
<img
|
||||||
alt={member.display_name}
|
src={member.avatar}
|
||||||
className="absolute top-0 left-0 h-full w-full object-cover rounded-lg"
|
alt={member.display_name || member.email}
|
||||||
/>
|
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||||
</div>
|
/>
|
||||||
) : member.display_name || member.email ? (
|
</a>
|
||||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
</Link>
|
||||||
{(member.display_name || member.email)?.charAt(0)}
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||||
?
|
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize bg-gray-700 text-white">
|
||||||
</div>
|
{(member.display_name ?? member.email ?? "?")[0]}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{member.member ? (
|
{member.member ? (
|
||||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||||
<a className="text-sm">
|
<a className="text-sm font-medium">
|
||||||
<span>
|
{member.first_name} {member.last_name}
|
||||||
{member.first_name} {member.last_name}
|
|
||||||
</span>
|
|
||||||
<span className="text-custom-text-300 text-sm ml-2">({member.display_name})</span>
|
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<h4 className="text-sm">{member.display_name || member.email}</h4>
|
<h4 className="text-sm cursor-default">{member.display_name || member.email}</h4>
|
||||||
)}
|
)}
|
||||||
{isOwner && <p className="mt-0.5 text-xs text-custom-sidebar-text-300">{member.email}</p>}
|
<p className="mt-0.5 text-xs text-custom-sidebar-text-300">{member.email ?? member.display_name}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 text-xs">
|
|
||||||
{!member.member && (
|
<div className="flex items-center gap-2 text-xs">
|
||||||
<div className="mr-2 flex items-center justify-center rounded-full bg-yellow-500/20 px-2 py-1 text-center text-xs text-yellow-500">
|
{!member?.status && (
|
||||||
Pending
|
<div className="flex items-center justify-center rounded bg-yellow-500/20 px-2.5 py-1 text-center text-xs text-yellow-500 font-medium">
|
||||||
|
<p>Pending</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
customButton={
|
customButton={
|
||||||
<div className="flex item-center gap-1">
|
<div className="flex item-center gap-1 px-2 py-0.5 rounded">
|
||||||
<span
|
<span
|
||||||
className={`flex items-center text-sm font-medium ${
|
className={`flex items-center text-xs font-medium rounded ${
|
||||||
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
|
member.memberId !== currentProjectMemberInfo?.id ? "" : "text-custom-sidebar-text-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{ROLE[member.role as keyof typeof ROLE]}
|
{ROLE[member.role as keyof typeof ROLE]}
|
||||||
</span>
|
</span>
|
||||||
{member.memberId !== user?.id && <ChevronDown className="h-4 w-4" />}
|
{member.memberId !== currentProjectMemberInfo?.id && (
|
||||||
|
<span className="grid place-items-center">
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
value={member.role}
|
value={member.role}
|
||||||
@ -168,31 +170,34 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||||||
!member.member ||
|
!member.member ||
|
||||||
(currentUser && currentUser.role !== 20 && currentUser.role < member.role)
|
(currentUser && currentUser.role !== 20 && currentUser.role < member.role)
|
||||||
}
|
}
|
||||||
|
placement="bottom-end"
|
||||||
>
|
>
|
||||||
{Object.keys(ROLE).map((key) => {
|
{Object.keys(ROLE).map((key) => {
|
||||||
if (currentUser && currentUser.role !== 20 && currentUser.role < parseInt(key)) return null;
|
if (currentProjectRole && currentProjectRole !== 20 && currentProjectRole < parseInt(key)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomSelect.Option key={key} value={key}>
|
<CustomSelect.Option key={key} value={parseInt(key, 10)}>
|
||||||
<>{ROLE[parseInt(key) as keyof typeof ROLE]}</>
|
<>{ROLE[parseInt(key) as keyof typeof ROLE]}</>
|
||||||
</CustomSelect.Option>
|
</CustomSelect.Option>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
<CustomMenu ellipsis disabled={!isAdmin}>
|
{isAdmin && (
|
||||||
<CustomMenu.MenuItem
|
<Tooltip
|
||||||
onClick={() => {
|
tooltipContent={member.memberId === currentProjectMemberInfo?.member ? "Leave project" : "Remove member"}
|
||||||
if (member.member) setSelectedRemoveMember(member.id);
|
|
||||||
else setSelectedInviteRemoveMember(member.id);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span className="flex items-center justify-start gap-2">
|
<button
|
||||||
<X className="h-4 w-4" />
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
<span> {member.memberId !== user?.id ? "Remove member" : "Leave project"}</span>
|
if (member.member) setSelectedRemoveMember(member);
|
||||||
</span>
|
else setSelectedInviteRemoveMember(member);
|
||||||
</CustomMenu.MenuItem>
|
}}
|
||||||
</CustomMenu>
|
className="opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto"
|
||||||
|
>
|
||||||
|
<XCircle className="h-3.5 w-3.5 text-custom-text-400" strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -3,10 +3,8 @@ import { usePopper } from "react-popper";
|
|||||||
import { Placement } from "@popperjs/core";
|
import { Placement } from "@popperjs/core";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search, User2 } from "lucide-react";
|
import { Check, ChevronDown, Search, User2 } from "lucide-react";
|
||||||
// components
|
|
||||||
import { Tooltip } from "components/ui";
|
|
||||||
// ui
|
// ui
|
||||||
import { Avatar, AvatarGroup } from "@plane/ui";
|
import { Avatar, AvatarGroup, Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IUserLite } from "types";
|
import { IUserLite } from "types";
|
||||||
|
|
||||||
|
@ -3,9 +3,7 @@ import { usePopper } from "react-popper";
|
|||||||
import { Placement } from "@popperjs/core";
|
import { Placement } from "@popperjs/core";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
import { Check, ChevronDown, Search } from "lucide-react";
|
||||||
import { PriorityIcon } from "@plane/ui";
|
import { PriorityIcon, Tooltip } from "@plane/ui";
|
||||||
// components
|
|
||||||
import { Tooltip } from "components/ui";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
import { capitalizeFirstLetter } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
|
@ -10,8 +10,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "components/ui";
|
import { Button, CustomSelect, Input, TextArea } from "@plane/ui";
|
||||||
import { Button, Input, TextArea } from "@plane/ui";
|
|
||||||
// icons
|
// icons
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
|
@ -11,8 +11,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "components/ui";
|
import { Button, CustomSelect, Input, Tooltip } from "@plane/ui";
|
||||||
import { Button, Input, Tooltip } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
import type { IState } from "types";
|
import type { IState } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
|
@ -4,8 +4,7 @@ import { Placement } from "@popperjs/core";
|
|||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
import { Check, ChevronDown, Search } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { StateGroupIcon } from "@plane/ui";
|
import { StateGroupIcon, Tooltip } from "@plane/ui";
|
||||||
import { Tooltip } from "components/ui";
|
|
||||||
// types
|
// types
|
||||||
import { IState } from "types";
|
import { IState } from "types";
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
// types
|
|
||||||
import { ButtonProps } from "./type";
|
|
||||||
|
|
||||||
export const DangerButton: React.FC<ButtonProps> = ({
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
onClick,
|
|
||||||
type = "button",
|
|
||||||
disabled = false,
|
|
||||||
loading = false,
|
|
||||||
size = "sm",
|
|
||||||
outline = false,
|
|
||||||
}) => (
|
|
||||||
<button
|
|
||||||
type={type}
|
|
||||||
className={`${className} border border-red-500 font-medium duration-300 ${
|
|
||||||
size === "sm"
|
|
||||||
? "rounded px-3 py-2 text-xs"
|
|
||||||
: size === "md"
|
|
||||||
? "rounded-md px-3.5 py-2 text-sm"
|
|
||||||
: "rounded-lg px-4 py-2 text-base"
|
|
||||||
} ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed bg-opacity-70 border-opacity-70 hover:bg-opacity-70 hover:border-opacity-70"
|
|
||||||
: ""
|
|
||||||
} ${
|
|
||||||
outline
|
|
||||||
? "bg-transparent text-red-500 hover:bg-red-500 hover:text-white"
|
|
||||||
: "text-white bg-red-500 hover:border-opacity-90 hover:bg-opacity-90"
|
|
||||||
} ${loading ? "cursor-wait" : ""}`}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled || loading}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
@ -1,3 +0,0 @@
|
|||||||
export * from "./danger-button";
|
|
||||||
export * from "./primary-button";
|
|
||||||
export * from "./secondary-button";
|
|
@ -1,32 +0,0 @@
|
|||||||
// types
|
|
||||||
import { ButtonProps } from "./type";
|
|
||||||
|
|
||||||
export const PrimaryButton: React.FC<ButtonProps> = ({
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
onClick,
|
|
||||||
type = "button",
|
|
||||||
disabled = false,
|
|
||||||
loading = false,
|
|
||||||
size = "sm",
|
|
||||||
outline = false,
|
|
||||||
}) => (
|
|
||||||
<button
|
|
||||||
type={type}
|
|
||||||
className={`${className} border border-custom-primary font-medium duration-300 ${
|
|
||||||
size === "sm"
|
|
||||||
? "rounded px-3 py-2 text-xs"
|
|
||||||
: size === "md"
|
|
||||||
? "rounded-md px-3.5 py-2 text-sm"
|
|
||||||
: "rounded-lg px-4 py-2 text-base"
|
|
||||||
} ${disabled ? "cursor-not-allowed opacity-70 hover:opacity-70" : ""} ${
|
|
||||||
outline
|
|
||||||
? "bg-transparent text-custom-primary hover:bg-custom-primary hover:text-white"
|
|
||||||
: "text-white bg-custom-primary hover:border-opacity-90 hover:bg-opacity-90"
|
|
||||||
} ${loading ? "cursor-wait" : ""}`}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled || loading}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
@ -1,32 +0,0 @@
|
|||||||
// types
|
|
||||||
import { ButtonProps } from "./type";
|
|
||||||
|
|
||||||
export const SecondaryButton: React.FC<ButtonProps> = ({
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
onClick,
|
|
||||||
type = "button",
|
|
||||||
disabled = false,
|
|
||||||
loading = false,
|
|
||||||
size = "sm",
|
|
||||||
outline = false,
|
|
||||||
}) => (
|
|
||||||
<button
|
|
||||||
type={type}
|
|
||||||
className={`${className} border border-custom-border-200 font-medium duration-300 ${
|
|
||||||
size === "sm"
|
|
||||||
? "rounded px-3 py-2 text-xs"
|
|
||||||
: size === "md"
|
|
||||||
? "rounded-md px-3.5 py-2 text-sm"
|
|
||||||
: "rounded-lg px-4 py-2 text-base"
|
|
||||||
} ${disabled ? "cursor-not-allowed border-custom-border-200 bg-custom-background-90" : ""} ${
|
|
||||||
outline
|
|
||||||
? "bg-transparent hover:bg-custom-background-80"
|
|
||||||
: "bg-custom-background-100 hover:border-opacity-70 hover:bg-opacity-70"
|
|
||||||
} ${loading ? "cursor-wait" : ""}`}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled || loading}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
10
web/components/ui/buttons/type.d.ts
vendored
10
web/components/ui/buttons/type.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export type ButtonProps = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
onClick?: (e: any) => void;
|
|
||||||
type?: "button" | "submit" | "reset";
|
|
||||||
disabled?: boolean;
|
|
||||||
loading?: boolean;
|
|
||||||
size?: "sm" | "md" | "lg";
|
|
||||||
outline?: boolean;
|
|
||||||
};
|
|
@ -1,4 +1,3 @@
|
|||||||
export * from "./buttons";
|
|
||||||
export * from "./dropdowns";
|
export * from "./dropdowns";
|
||||||
export * from "./graphs";
|
export * from "./graphs";
|
||||||
export * from "./input";
|
export * from "./input";
|
||||||
|
@ -3,7 +3,7 @@ import { Fragment, useState } from "react";
|
|||||||
// headless ui
|
// headless ui
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "components/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { Check, ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
|
import { Check, ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
// services
|
// services
|
||||||
import { WorkspaceService } from "services/workspace.service";
|
import { WorkspaceService } from "services/workspace.service";
|
||||||
// components
|
// components
|
||||||
import { Loader, MarkdownRenderer } from "components/ui";
|
import { MarkdownRenderer } from "components/ui";
|
||||||
|
import { Loader } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
// helpers
|
// helpers
|
||||||
|
@ -8,7 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { DangerButton, SecondaryButton } from "components/ui";
|
import { Button } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
@ -112,10 +112,12 @@ export const DeleteProjectViewModal: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end gap-2 p-4 sm:px-6">
|
<div className="flex justify-end gap-2 p-4 sm:px-6">
|
||||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||||
<DangerButton onClick={handleDeleteView} loading={isDeleteLoading}>
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="danger" size="sm" onClick={handleDeleteView}>
|
||||||
{isDeleteLoading ? "Deleting..." : "Delete"}
|
{isDeleteLoading ? "Deleting..." : "Delete"}
|
||||||
</DangerButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
@ -7,7 +7,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { AppliedFiltersList, FilterSelection, FiltersDropdown } from "components/issues";
|
import { AppliedFiltersList, FilterSelection, FiltersDropdown } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
import { Button, Input, TextArea } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IProjectView } from "types";
|
import { IProjectView } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -32,7 +32,6 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
|
|||||||
control,
|
control,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
register,
|
|
||||||
reset,
|
reset,
|
||||||
setValue,
|
setValue,
|
||||||
watch,
|
watch,
|
||||||
@ -70,32 +69,45 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
|
|||||||
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{data ? "Update" : "Create"} View</h3>
|
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{data ? "Update" : "Create"} View</h3>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Controller
|
||||||
id="name"
|
control={control}
|
||||||
name="name"
|
name="name"
|
||||||
type="name"
|
rules={{
|
||||||
placeholder="Title"
|
|
||||||
autoComplete="off"
|
|
||||||
className="resize-none text-xl"
|
|
||||||
error={errors.name}
|
|
||||||
register={register}
|
|
||||||
validations={{
|
|
||||||
required: "Title is required",
|
required: "Title is required",
|
||||||
maxLength: {
|
maxLength: {
|
||||||
value: 255,
|
value: 255,
|
||||||
message: "Title should be less than 255 characters",
|
message: "Title should be less than 255 characters",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
render={({ field: { value, onChange } }) => (
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
type="name"
|
||||||
|
name="name"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
hasError={Boolean(errors.name)}
|
||||||
|
placeholder="Title"
|
||||||
|
className="resize-none text-xl"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<TextArea
|
<Controller
|
||||||
id="description"
|
|
||||||
name="description"
|
name="description"
|
||||||
placeholder="Description"
|
control={control}
|
||||||
className="h-32 resize-none text-sm"
|
render={({ field: { value, onChange } }) => (
|
||||||
error={errors.description}
|
<TextArea
|
||||||
register={register}
|
id="description"
|
||||||
|
name="description"
|
||||||
|
placeholder="Description"
|
||||||
|
className="resize-none text-sm"
|
||||||
|
hasError={Boolean(errors?.description)}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -147,8 +159,10 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 flex justify-end gap-2">
|
<div className="mt-5 flex justify-end gap-2">
|
||||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" size="sm" type="submit">
|
||||||
{data
|
{data
|
||||||
? isSubmitting
|
? isSubmitting
|
||||||
? "Updating View..."
|
? "Updating View..."
|
||||||
@ -156,7 +170,7 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
|
|||||||
: isSubmitting
|
: isSubmitting
|
||||||
? "Creating View..."
|
? "Creating View..."
|
||||||
: "Create View"}
|
: "Create View"}
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { ProjectViewListItem } from "components/views";
|
import { ProjectViewListItem } from "components/views";
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// ui
|
// ui
|
||||||
import { Input, Loader } from "components/ui";
|
import { Input, Loader } from "@plane/ui";
|
||||||
// assets
|
// assets
|
||||||
import emptyView from "public/empty-state/view.svg";
|
import emptyView from "public/empty-state/view.svg";
|
||||||
// icons
|
// icons
|
||||||
@ -48,7 +48,7 @@ export const ProjectViewsList = observer(() => {
|
|||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
mode="trueTransparent"
|
mode="true-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { TourRoot } from "components/onboarding";
|
import { TourRoot } from "components/onboarding";
|
||||||
import { UserGreetingsView } from "components/user";
|
import { UserGreetingsView } from "components/user";
|
||||||
import { CompletedIssuesGraph, IssuesList, IssuesPieChart, IssuesStats } from "components/workspace";
|
import { CompletedIssuesGraph, IssuesList, IssuesPieChart, IssuesStats } from "components/workspace";
|
||||||
import { PrimaryButton } from "components/ui";
|
import { Button } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
import emptyDashboard from "public/empty-state/dashboard.svg";
|
import emptyDashboard from "public/empty-state/dashboard.svg";
|
||||||
|
|
||||||
@ -67,7 +67,9 @@ export const WorkspaceDashboardView = observer(() => {
|
|||||||
<div className="p-5 md:p-8 pr-0">
|
<div className="p-5 md:p-8 pr-0">
|
||||||
<h5 className="text-xl font-semibold">Create a project</h5>
|
<h5 className="text-xl font-semibold">Create a project</h5>
|
||||||
<p className="mt-2 mb-5">Manage your projects by creating issues, cycles, modules, views and pages.</p>
|
<p className="mt-2 mb-5">Manage your projects by creating issues, cycles, modules, views and pages.</p>
|
||||||
<PrimaryButton
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const e = new KeyboardEvent("keydown", {
|
const e = new KeyboardEvent("keydown", {
|
||||||
key: "p",
|
key: "p",
|
||||||
@ -76,7 +78,7 @@ export const WorkspaceDashboardView = observer(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Create Project
|
Create Project
|
||||||
</PrimaryButton>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden md:block self-end overflow-hidden pt-8">
|
<div className="hidden md:block self-end overflow-hidden pt-8">
|
||||||
<Image src={emptyDashboard} alt="Empty Dashboard" />
|
<Image src={emptyDashboard} alt="Empty Dashboard" />
|
||||||
|
@ -4,7 +4,7 @@ import Link from "next/link";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// icons
|
// icons
|
||||||
import { Spinner, PrimaryButton, SecondaryButton } from "components/ui";
|
import { Button, Spinner } from "@plane/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
|
||||||
@ -67,12 +67,16 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
<Link href="/invitations">
|
<Link href="/invitations">
|
||||||
<a>
|
<a>
|
||||||
<SecondaryButton>Check pending invites</SecondaryButton>
|
<Button variant="neutral-primary" size="sm">
|
||||||
|
Check pending invites
|
||||||
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/create-workspace">
|
<Link href="/create-workspace">
|
||||||
<a>
|
<a>
|
||||||
<PrimaryButton>Create new workspace</PrimaryButton>
|
<Button variant="primary" size="sm">
|
||||||
|
Create new workspace
|
||||||
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user