forked from github/plane
fix: ui and bugs (#2289)
* fix: 24 character limit on first & last name in onboarding page * fix: no option: 'Add Issue' in archive issue page * fix: in archive issue directly sending to issue detail page * fix: issue type showing in archive issue * fix: custom menu overflowing * fix: changing subscriber in filters has no effect * style: border in quick-add * fix: on onboarding member role overflowing * fix: inconsistent icons in issue detail * style: spacing, borders and shadows in quick-add * fix: custom menu truncate
This commit is contained in:
parent
60a69e28e3
commit
ec91a0d2e5
@ -8,9 +8,27 @@ import useEstimateOption from "hooks/use-estimate-option";
|
|||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
// icons
|
// icons
|
||||||
import { Icon, Tooltip } from "components/ui";
|
import { Icon, Tooltip } from "components/ui";
|
||||||
import { CopyPlus } from "lucide-react";
|
import {
|
||||||
import { Squares2X2Icon } from "@heroicons/react/24/outline";
|
TagIcon,
|
||||||
import { BlockedIcon, BlockerIcon, RelatedIcon } from "components/icons";
|
CopyPlus,
|
||||||
|
Calendar,
|
||||||
|
Link2Icon,
|
||||||
|
RocketIcon,
|
||||||
|
Users2Icon,
|
||||||
|
ArchiveIcon,
|
||||||
|
PaperclipIcon,
|
||||||
|
ContrastIcon,
|
||||||
|
TriangleIcon,
|
||||||
|
LayoutGridIcon,
|
||||||
|
SignalMediumIcon,
|
||||||
|
MessageSquareIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import {
|
||||||
|
BlockedIcon,
|
||||||
|
BlockerIcon,
|
||||||
|
RelatedIcon,
|
||||||
|
StackedLayersHorizontalIcon,
|
||||||
|
} from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
import { capitalizeFirstLetter } from "helpers/string.helper";
|
||||||
@ -38,7 +56,7 @@ const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
|||||||
{activity.issue_detail
|
{activity.issue_detail
|
||||||
? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}`
|
? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}`
|
||||||
: "Issue"}
|
: "Issue"}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@ -131,14 +149,14 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="group" className="!text-2xl" aria-hidden="true" />,
|
icon: <Users2Icon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
archived_at: {
|
archived_at: {
|
||||||
message: (activity) => {
|
message: (activity) => {
|
||||||
if (activity.new_value === "restore") return "restored the issue.";
|
if (activity.new_value === "restore") return "restored the issue.";
|
||||||
else return "archived the issue.";
|
else return "archived the issue.";
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="archive" className="!text-2xl" aria-hidden="true" />,
|
icon: <ArchiveIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
attachment: {
|
attachment: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -153,7 +171,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
attachment
|
attachment
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
{showIssue && (
|
{showIssue && (
|
||||||
<>
|
<>
|
||||||
@ -177,7 +195,7 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="attach_file" className="!text-2xl" aria-hidden="true" />,
|
icon: <PaperclipIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
blocking: {
|
blocking: {
|
||||||
message: (activity) => {
|
message: (activity) => {
|
||||||
@ -268,7 +286,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.new_value}
|
{activity.new_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -283,7 +301,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.new_value}
|
{activity.new_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -298,12 +316,12 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.old_value}
|
{activity.old_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="contrast" className="!text-2xl" aria-hidden="true" />,
|
icon: <ContrastIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
message: (activity, showIssue) => (
|
message: (activity, showIssue) => (
|
||||||
@ -318,7 +336,7 @@ const activityDetails: {
|
|||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
icon: <Icon iconName="chat" className="!text-2xl" aria-hidden="true" />,
|
icon: <MessageSquareIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
estimate_point: {
|
estimate_point: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -349,14 +367,14 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="change_history" className="!text-2xl" aria-hidden="true" />,
|
icon: <TriangleIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
issue: {
|
issue: {
|
||||||
message: (activity) => {
|
message: (activity) => {
|
||||||
if (activity.verb === "created") return "created the issue.";
|
if (activity.verb === "created") return "created the issue.";
|
||||||
else return "deleted an issue.";
|
else return "deleted an issue.";
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="stack" className="!text-2xl" aria-hidden="true" />,
|
icon: <StackedLayersHorizontalIcon width={12} height={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -393,7 +411,7 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="sell" className="!text-2xl" aria-hidden="true" />,
|
icon: <TagIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -408,7 +426,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
link
|
link
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
{showIssue && (
|
{showIssue && (
|
||||||
<>
|
<>
|
||||||
@ -430,7 +448,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
link
|
link
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
{showIssue && (
|
{showIssue && (
|
||||||
<>
|
<>
|
||||||
@ -452,7 +470,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
link
|
link
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
{showIssue && (
|
{showIssue && (
|
||||||
<>
|
<>
|
||||||
@ -464,7 +482,7 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="link" className="!text-2xl" aria-hidden="true" />,
|
icon: <Link2Icon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
modules: {
|
modules: {
|
||||||
message: (activity, showIssue, workspaceSlug) => {
|
message: (activity, showIssue, workspaceSlug) => {
|
||||||
@ -479,7 +497,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.new_value}
|
{activity.new_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -494,7 +512,7 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.new_value}
|
{activity.new_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -509,12 +527,12 @@ const activityDetails: {
|
|||||||
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
|
||||||
>
|
>
|
||||||
{activity.old_value}
|
{activity.old_value}
|
||||||
<Icon iconName="launch" className="!text-xs" />
|
<RocketIcon size={12} color="#6b7280" />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="dataset" className="!text-2xl" aria-hidden="true" />,
|
icon: <Icon iconName="dataset" className="!text-xs !text-[#6b7280]" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
message: (activity, showIssue) => (
|
message: (activity, showIssue) => (
|
||||||
@ -529,7 +547,7 @@ const activityDetails: {
|
|||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
icon: <Icon iconName="chat" className="!text-2xl" aria-hidden="true" />,
|
icon: <MessageSquareIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
parent: {
|
parent: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -562,7 +580,13 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="supervised_user_circle" className="!text-2xl" aria-hidden="true" />,
|
icon: (
|
||||||
|
<Icon
|
||||||
|
iconName="supervised_user_circle"
|
||||||
|
className="!text-xs !text-[#6b7280]"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
priority: {
|
priority: {
|
||||||
message: (activity, showIssue) => (
|
message: (activity, showIssue) => (
|
||||||
@ -580,7 +604,7 @@ const activityDetails: {
|
|||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
icon: <Icon iconName="signal_cellular_alt" className="!text-2xl" aria-hidden="true" />,
|
icon: <SignalMediumIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
start_date: {
|
start_date: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -614,7 +638,7 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="calendar_today" className="!text-2xl" aria-hidden="true" />,
|
icon: <Calendar size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
message: (activity, showIssue) => (
|
message: (activity, showIssue) => (
|
||||||
@ -630,7 +654,7 @@ const activityDetails: {
|
|||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
icon: <Squares2X2Icon className="h-6 w-6 text-custom-sidebar-200" aria-hidden="true" />,
|
icon: <LayoutGridIcon size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
target_date: {
|
target_date: {
|
||||||
message: (activity, showIssue) => {
|
message: (activity, showIssue) => {
|
||||||
@ -664,7 +688,7 @@ const activityDetails: {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: <Icon iconName="calendar_today" className="!text-2xl" aria-hidden="true" />,
|
icon: <Calendar size={12} color="#6b7280" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -200,7 +200,9 @@ export const FiltersList: React.FC<Props> = ({
|
|||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setFilters({
|
setFilters({
|
||||||
assignees: filters.assignees?.filter((p: any) => p !== memberId),
|
subscriber: filters.subscriber?.filter(
|
||||||
|
(p: any) => p !== memberId
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -277,6 +277,7 @@ export const IssuesFilterView: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!isArchivedIssues && (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h4 className="text-custom-text-200">Issue type</h4>
|
<h4 className="text-custom-text-200">Issue type</h4>
|
||||||
<div className="w-28">
|
<div className="w-28">
|
||||||
@ -304,6 +305,7 @@ export const IssuesFilterView: React.FC = () => {
|
|||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{displayFilters.layout !== "calendar" &&
|
{displayFilters.layout !== "calendar" &&
|
||||||
displayFilters.layout !== "spreadsheet" && (
|
displayFilters.layout !== "spreadsheet" && (
|
||||||
|
@ -48,7 +48,7 @@ const InlineInput = () => {
|
|||||||
export const BoardInlineCreateIssueForm: React.FC<Props> = (props) => (
|
export const BoardInlineCreateIssueForm: React.FC<Props> = (props) => (
|
||||||
<>
|
<>
|
||||||
<InlineCreateIssueFormWrapper
|
<InlineCreateIssueFormWrapper
|
||||||
className="flex flex-col justify-between gap-1.5 group/card relative select-none px-3.5 py-3 h-[118px] mb-3 rounded bg-custom-background-100 shadow-custom-shadow-sm"
|
className="flex flex-col border-[0.5px] border-custom-border-100 justify-between gap-1.5 group/card relative select-none px-3.5 py-3 h-[118px] mb-3 rounded bg-custom-background-100 shadow-custom-shadow-sm"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<InlineInput />
|
<InlineInput />
|
||||||
|
@ -67,7 +67,7 @@ const InlineInput = () => {
|
|||||||
{...register("name", {
|
{...register("name", {
|
||||||
required: "Issue title is required.",
|
required: "Issue title is required.",
|
||||||
})}
|
})}
|
||||||
className="w-full pr-2 py-1.5 rounded-md bg-transparent text-sm font-medium leading-5 text-custom-text-200 outline-none"
|
className="w-full pr-2 py-2.5 rounded-md bg-transparent text-sm font-medium leading-5 text-custom-text-200 outline-none"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -90,7 +90,7 @@ export const CalendarInlineCreateIssueForm: React.FC<Props> = (props) => {
|
|||||||
>
|
>
|
||||||
<InlineCreateIssueFormWrapper
|
<InlineCreateIssueFormWrapper
|
||||||
{...props}
|
{...props}
|
||||||
className="flex w-full p-1 px-1.5 rounded z-50 items-center gap-x-2 bg-custom-background-100 shadow-custom-shadow-sm transition-opacity"
|
className="flex w-full px-1.5 border-[0.5px] border-custom-border-100 rounded z-50 items-center gap-x-2 bg-custom-background-100 shadow-custom-shadow-sm transition-opacity"
|
||||||
>
|
>
|
||||||
<InlineInput />
|
<InlineInput />
|
||||||
</InlineCreateIssueFormWrapper>
|
</InlineCreateIssueFormWrapper>
|
||||||
|
@ -48,7 +48,7 @@ const InlineInput = () => {
|
|||||||
export const GanttInlineCreateIssueForm: React.FC<Props> = (props) => (
|
export const GanttInlineCreateIssueForm: React.FC<Props> = (props) => (
|
||||||
<>
|
<>
|
||||||
<InlineCreateIssueFormWrapper
|
<InlineCreateIssueFormWrapper
|
||||||
className="flex py-3 px-4 mr-2.5 items-center rounded gap-x-2 border bg-custom-background-100 shadow-custom-shadow-sm"
|
className="flex py-3 px-4 border-[0.5px] border-custom-border-100 mr-2.5 items-center rounded gap-x-2 bg-custom-background-100 shadow-custom-shadow-sm"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<InlineInput />
|
<InlineInput />
|
||||||
|
@ -81,7 +81,9 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||||
const isDraftIssues = router.asPath.includes("draft-issues");
|
|
||||||
|
const isDraftIssues = router.pathname?.split("/")?.[4] === "draft-issues";
|
||||||
|
const isArchivedIssues = router.pathname?.split("/")?.[4] === "archived-issues";
|
||||||
|
|
||||||
const { user } = useUserAuth();
|
const { user } = useUserAuth();
|
||||||
|
|
||||||
@ -625,6 +627,7 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
params,
|
params,
|
||||||
properties,
|
properties,
|
||||||
}}
|
}}
|
||||||
|
disableAddIssueOption={isArchivedIssues}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -39,7 +39,7 @@ const InlineInput = () => {
|
|||||||
{...register("name", {
|
{...register("name", {
|
||||||
required: "Issue title is required.",
|
required: "Issue title is required.",
|
||||||
})}
|
})}
|
||||||
className="w-full px-2 py-1.5 rounded-md bg-transparent text-sm font-medium leading-5 text-custom-text-200 outline-none"
|
className="w-full px-2 py-3 rounded-md bg-transparent text-sm font-medium leading-5 text-custom-text-200 outline-none"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -48,7 +48,7 @@ const InlineInput = () => {
|
|||||||
export const ListInlineCreateIssueForm: React.FC<Props> = (props) => (
|
export const ListInlineCreateIssueForm: React.FC<Props> = (props) => (
|
||||||
<>
|
<>
|
||||||
<InlineCreateIssueFormWrapper
|
<InlineCreateIssueFormWrapper
|
||||||
className="flex py-3 px-4 items-center gap-x-5 bg-custom-background-100 shadow-custom-shadow-sm z-10"
|
className="flex border-[0.5px] border-t-0 border-custom-border-100 px-4 items-center gap-x-5 bg-custom-background-100 shadow-custom-shadow-sm z-10"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<InlineInput />
|
<InlineInput />
|
||||||
|
@ -328,7 +328,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between px-4 py-2.5 gap-10 border-b border-custom-border-200 bg-custom-background-100 last:border-b-0"
|
className="flex items-center justify-between px-4 py-2.5 gap-10 border-b-[0.5px] border-custom-border-100 bg-custom-background-100 last:border-b-0"
|
||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setContextMenu(true);
|
setContextMenu(true);
|
||||||
@ -352,6 +352,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
|||||||
type="button"
|
type="button"
|
||||||
className="truncate text-[0.825rem] text-custom-text-100"
|
className="truncate text-[0.825rem] text-custom-text-100"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (isArchivedIssues) return router.push(issuePath);
|
||||||
if (!isDraftIssues) openPeekOverview(issue);
|
if (!isDraftIssues) openPeekOverview(issue);
|
||||||
if (isDraftIssues && handleDraftIssueSelect) handleDraftIssueSelect(issue);
|
if (isDraftIssues && handleDraftIssueSelect) handleDraftIssueSelect(issue);
|
||||||
}}
|
}}
|
||||||
|
@ -336,7 +336,8 @@ export const SingleList: React.FC<Props> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{!disableAddIssueOption && !isCreateIssueFormOpen && (
|
{!disableAddIssueOption && !isCreateIssueFormOpen && (
|
||||||
<div className="w-full bg-custom-background-100 px-6 py-3">
|
// TODO: add border here
|
||||||
|
<div className="w-full bg-custom-background-100 px-6 py-3 border-b border-custom-border-100">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -54,7 +54,7 @@ export const AssigneeColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.assignee && (
|
{properties.assignee && (
|
||||||
<MembersSelect
|
<MembersSelect
|
||||||
value={issue.assignees}
|
value={issue.assignees}
|
||||||
|
@ -23,7 +23,7 @@ export const CreatedOnColumn: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.created_on && (
|
{properties.created_on && (
|
||||||
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
|
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
|
||||||
{renderLongDetailDateFormat(issue.created_at)}
|
{renderLongDetailDateFormat(issue.created_at)}
|
||||||
|
@ -23,7 +23,7 @@ export const DueDateColumn: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.due_date && (
|
{properties.due_date && (
|
||||||
<ViewDueDateSelect
|
<ViewDueDateSelect
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
@ -23,7 +23,7 @@ export const EstimateColumn: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.estimate && (
|
{properties.estimate && (
|
||||||
<ViewEstimateSelect
|
<ViewEstimateSelect
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
@ -82,7 +82,7 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||||
|
|
||||||
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-r border-custom-border-200 ">
|
<div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-r border-custom-border-100">
|
||||||
<div
|
<div
|
||||||
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center"
|
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center"
|
||||||
style={issue.parent ? { paddingLeft } : {}}
|
style={issue.parent ? { paddingLeft } : {}}
|
||||||
|
@ -28,7 +28,7 @@ export const LabelColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.labels && (
|
{properties.labels && (
|
||||||
<LabelSelect
|
<LabelSelect
|
||||||
value={issue.labels}
|
value={issue.labels}
|
||||||
|
@ -48,7 +48,7 @@ export const PriorityColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.priority && (
|
{properties.priority && (
|
||||||
<PrioritySelect
|
<PrioritySelect
|
||||||
value={issue.priority}
|
value={issue.priority}
|
||||||
|
@ -287,7 +287,7 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="border-t border-custom-border-100">
|
||||||
<div className="mb-3 z-50 sticky bottom-0 left-0">
|
<div className="mb-3 z-50 sticky bottom-0 left-0">
|
||||||
<ListInlineCreateIssueForm
|
<ListInlineCreateIssueForm
|
||||||
isOpen={isInlineCreateIssueFormOpen}
|
isOpen={isInlineCreateIssueFormOpen}
|
||||||
@ -303,11 +303,11 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
? !disableUserActions &&
|
? !disableUserActions &&
|
||||||
!isInlineCreateIssueFormOpen && (
|
!isInlineCreateIssueFormOpen && (
|
||||||
<button
|
<button
|
||||||
className="flex gap-1.5 items-center text-custom-primary-100 pl-7 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full"
|
className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] w-full"
|
||||||
onClick={() => setIsInlineCreateIssueFormOpen(true)}
|
onClick={() => setIsInlineCreateIssueFormOpen(true)}
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
Add Issue
|
New Issue
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
: !disableUserActions &&
|
: !disableUserActions &&
|
||||||
@ -316,11 +316,11 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
className="sticky left-0 z-10"
|
className="sticky left-0 z-10"
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<button
|
||||||
className="flex gap-1.5 items-center text-custom-primary-100 pl-7 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full"
|
className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
Add Issue
|
New Issue
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
position="left"
|
position="left"
|
||||||
|
@ -23,7 +23,7 @@ export const StartDateColumn: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.due_date && (
|
{properties.due_date && (
|
||||||
<ViewStartDateSelect
|
<ViewStartDateSelect
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
@ -70,7 +70,7 @@ export const StateColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.state && (
|
{properties.state && (
|
||||||
<StateSelect
|
<StateSelect
|
||||||
value={issue.state_detail}
|
value={issue.state_detail}
|
||||||
|
@ -23,7 +23,7 @@ export const UpdatedOnColumn: React.FC<Props> = ({
|
|||||||
isNotAllowed,
|
isNotAllowed,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
<div className="flex items-center text-sm h-11 w-full bg-custom-background-100">
|
||||||
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-200">
|
<span className="flex items-center px-4 py-2.5 h-full w-full flex-shrink-0 border-r border-b border-custom-border-100">
|
||||||
{properties.updated_on && (
|
{properties.updated_on && (
|
||||||
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
|
<div className="flex items-center text-xs cursor-default text-custom-text-200 text-center p-2 group-hover:bg-custom-background-80 border-custom-border-200">
|
||||||
{renderLongDetailDateFormat(issue.updated_at)}
|
{renderLongDetailDateFormat(issue.updated_at)}
|
||||||
|
@ -347,7 +347,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsCreateIssueFormOpen(true)}
|
onClick={() => setIsCreateIssueFormOpen(true)}
|
||||||
className="flex items-center gap-x-[6px] text-custom-primary-100 px-2 py-1 rounded-md"
|
className="flex items-center gap-x-[6px] text-custom-primary-100 px-2 pl-[1.875rem] py-1 rounded-md"
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
<span className="text-sm font-medium text-custom-primary-100">New Issue</span>
|
<span className="text-sm font-medium text-custom-primary-100">New Issue</span>
|
||||||
|
@ -1,15 +1,27 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
// headless ui
|
||||||
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import {
|
||||||
|
Control,
|
||||||
|
Controller,
|
||||||
|
FieldArrayWithId,
|
||||||
|
UseFieldArrayRemove,
|
||||||
|
useFieldArray,
|
||||||
|
useForm,
|
||||||
|
} from "react-hook-form";
|
||||||
// services
|
// services
|
||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui";
|
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
|
||||||
|
// hooks
|
||||||
|
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
||||||
// icons
|
// icons
|
||||||
import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||||
|
import { PlusIcon, XMarkIcon, CheckIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { ICurrentUserResponse, IWorkspace, TOnboardingSteps } from "types";
|
import { ICurrentUserResponse, IWorkspace, TOnboardingSteps } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -31,12 +43,136 @@ type FormValues = {
|
|||||||
emails: EmailRole[];
|
emails: EmailRole[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InviteMembers: React.FC<Props> = ({
|
type InviteMemberFormProps = {
|
||||||
finishOnboarding,
|
index: number;
|
||||||
stepChange,
|
remove: UseFieldArrayRemove;
|
||||||
user,
|
control: Control<FormValues, any>;
|
||||||
workspace,
|
field: FieldArrayWithId<FormValues, "emails", "id">;
|
||||||
}) => {
|
fields: FieldArrayWithId<FormValues, "emails", "id">[];
|
||||||
|
errors: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const InviteMemberForm: React.FC<InviteMemberFormProps> = (props) => {
|
||||||
|
const { control, index, fields, remove, errors } = props;
|
||||||
|
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
|
||||||
|
useDynamicDropdownPosition(
|
||||||
|
isDropdownOpen,
|
||||||
|
() => setIsDropdownOpen(false),
|
||||||
|
buttonRef,
|
||||||
|
dropdownRef
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="group relative grid grid-cols-11 gap-4">
|
||||||
|
<div className="col-span-7">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`emails.${index}.email`}
|
||||||
|
rules={{
|
||||||
|
required: "Email ID is required",
|
||||||
|
pattern: {
|
||||||
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
|
message: "Invalid Email ID",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
render={({ field }) => (
|
||||||
|
<>
|
||||||
|
<Input {...field} className="text-xs sm:text-sm" placeholder="Enter their email..." />
|
||||||
|
{errors.emails?.[index]?.email && (
|
||||||
|
<span className="text-red-500 text-xs">
|
||||||
|
{errors.emails?.[index]?.email?.message}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-3">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`emails.${index}.role`}
|
||||||
|
rules={{ required: true }}
|
||||||
|
render={({ field: { value, onChange } }) => (
|
||||||
|
<Listbox
|
||||||
|
as="div"
|
||||||
|
value={value}
|
||||||
|
onChange={(val) => {
|
||||||
|
onChange(val);
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
}}
|
||||||
|
className="flex-shrink-0 text-left w-full"
|
||||||
|
>
|
||||||
|
<Listbox.Button
|
||||||
|
type="button"
|
||||||
|
ref={buttonRef}
|
||||||
|
onClick={() => setIsDropdownOpen((prev) => !prev)}
|
||||||
|
className="flex items-center px-2.5 py-2 text-xs justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none"
|
||||||
|
>
|
||||||
|
<span className="text-xs sm:text-sm">{ROLE[value]}</span>
|
||||||
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
</Listbox.Button>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
show={isDropdownOpen}
|
||||||
|
as={React.Fragment}
|
||||||
|
enter="transition ease-out duration-100"
|
||||||
|
enterFrom="transform opacity-0 scale-95"
|
||||||
|
enterTo="transform opacity-100 scale-100"
|
||||||
|
leave="transition ease-in duration-75"
|
||||||
|
leaveFrom="transform opacity-100 scale-100"
|
||||||
|
leaveTo="transform opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<Listbox.Options
|
||||||
|
ref={dropdownRef}
|
||||||
|
className="fixed w-36 z-10 border border-custom-border-300 mt-1 overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none max-h-48"
|
||||||
|
>
|
||||||
|
<div className="space-y-1 p-2">
|
||||||
|
{Object.entries(ROLE).map(([key, value]) => (
|
||||||
|
<Listbox.Option
|
||||||
|
key={key}
|
||||||
|
value={parseInt(key)}
|
||||||
|
className={({ active, selected }) =>
|
||||||
|
`cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
||||||
|
active || selected ? "bg-custom-background-80" : ""
|
||||||
|
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{({ selected }) => (
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2">{value}</div>
|
||||||
|
{selected && <CheckIcon className="h-4 w-4 flex-shrink-0" />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Listbox.Option>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Listbox.Options>
|
||||||
|
</Transition>
|
||||||
|
</Listbox>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{fields.length > 1 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="hidden group-hover:grid self-center place-items-center rounded -ml-3"
|
||||||
|
onClick={() => remove(index)}
|
||||||
|
>
|
||||||
|
<XMarkIcon className="h-3.5 w-3.5 text-custom-text-200" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InviteMembers: React.FC<Props> = (props) => {
|
||||||
|
const { finishOnboarding, stepChange, user, workspace } = props;
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -109,66 +245,15 @@ export const InviteMembers: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-3 sm:space-y-4 mb-3 h-full overflow-y-auto">
|
<div className="space-y-3 sm:space-y-4 mb-3 h-full overflow-y-auto">
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<div key={field.id} className="group relative grid grid-cols-11 gap-4">
|
<InviteMemberForm
|
||||||
<div className="col-span-7">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
control={control}
|
||||||
name={`emails.${index}.email`}
|
errors={errors}
|
||||||
rules={{
|
field={field}
|
||||||
required: "Email ID is required",
|
fields={fields}
|
||||||
pattern: {
|
index={index}
|
||||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
remove={remove}
|
||||||
message: "Invalid Email ID",
|
key={field.id}
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
className="text-xs sm:text-sm"
|
|
||||||
placeholder="Enter their email..."
|
|
||||||
/>
|
/>
|
||||||
{errors.emails?.[index]?.email && (
|
|
||||||
<span className="text-red-500 text-xs">
|
|
||||||
{errors.emails?.[index]?.email?.message}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-3">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`emails.${index}.role`}
|
|
||||||
rules={{ required: true }}
|
|
||||||
render={({ field: { value, onChange } }) => (
|
|
||||||
<CustomSelect
|
|
||||||
value={value}
|
|
||||||
label={<span className="text-xs sm:text-sm">{ROLE[value]}</span>}
|
|
||||||
onChange={onChange}
|
|
||||||
width="w-full"
|
|
||||||
input
|
|
||||||
>
|
|
||||||
{Object.entries(ROLE).map(([key, value]) => (
|
|
||||||
<CustomSelect.Option key={key} value={parseInt(key)}>
|
|
||||||
{value}
|
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{fields.length > 1 && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="hidden group-hover:grid self-center place-items-center rounded -ml-3"
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
>
|
|
||||||
<XMarkIcon className="h-3.5 w-3.5 text-custom-text-200" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -121,6 +121,10 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
|
|||||||
register={register}
|
register={register}
|
||||||
validations={{
|
validations={{
|
||||||
required: "First name is required",
|
required: "First name is required",
|
||||||
|
maxLength: {
|
||||||
|
value: 24,
|
||||||
|
message: "First name cannot exceed the limit of 24 characters",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
error={errors.first_name}
|
error={errors.first_name}
|
||||||
/>
|
/>
|
||||||
@ -135,6 +139,10 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
|
|||||||
placeholder="Enter your last name..."
|
placeholder="Enter your last name..."
|
||||||
validations={{
|
validations={{
|
||||||
required: "Last name is required",
|
required: "Last name is required",
|
||||||
|
maxLength: {
|
||||||
|
value: 24,
|
||||||
|
message: "Last name cannot exceed the limit of 24 characters",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
error={errors.last_name}
|
error={errors.last_name}
|
||||||
/>
|
/>
|
||||||
|
@ -239,9 +239,11 @@ export const ProfileIssuesViewOptions: React.FC = () => {
|
|||||||
<div className="w-28">
|
<div className="w-28">
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
label={
|
label={
|
||||||
FILTER_ISSUE_OPTIONS.find(
|
<span className="truncate">
|
||||||
|
{FILTER_ISSUE_OPTIONS.find(
|
||||||
(option) => option.key === displayFilters?.type
|
(option) => option.key === displayFilters?.type
|
||||||
)?.name ?? "Select"
|
)?.name ?? "Select"}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
className="!w-full"
|
className="!w-full"
|
||||||
buttonClassName="w-full"
|
buttonClassName="w-full"
|
||||||
|
@ -14,6 +14,7 @@ const paramsToKey = (params: any) => {
|
|||||||
sub_issue,
|
sub_issue,
|
||||||
start_target_date,
|
start_target_date,
|
||||||
project,
|
project,
|
||||||
|
subscriber,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let projectKey = project ? project.split(",") : [];
|
let projectKey = project ? project.split(",") : [];
|
||||||
@ -23,6 +24,7 @@ const paramsToKey = (params: any) => {
|
|||||||
let assigneesKey = assignees ? assignees.split(",") : [];
|
let assigneesKey = assignees ? assignees.split(",") : [];
|
||||||
let createdByKey = created_by ? created_by.split(",") : [];
|
let createdByKey = created_by ? created_by.split(",") : [];
|
||||||
let labelsKey = labels ? labels.split(",") : [];
|
let labelsKey = labels ? labels.split(",") : [];
|
||||||
|
let subscriberKey = subscriber ? subscriber.split(",") : [];
|
||||||
const startTargetDate = start_target_date ? `${start_target_date}`.toUpperCase() : "FALSE";
|
const startTargetDate = start_target_date ? `${start_target_date}`.toUpperCase() : "FALSE";
|
||||||
const startDateKey = start_date ?? "";
|
const startDateKey = start_date ?? "";
|
||||||
const targetDateKey = target_date ?? "";
|
const targetDateKey = target_date ?? "";
|
||||||
@ -38,8 +40,9 @@ const paramsToKey = (params: any) => {
|
|||||||
assigneesKey = assigneesKey.sort().join("_");
|
assigneesKey = assigneesKey.sort().join("_");
|
||||||
createdByKey = createdByKey.sort().join("_");
|
createdByKey = createdByKey.sort().join("_");
|
||||||
labelsKey = labelsKey.sort().join("_");
|
labelsKey = labelsKey.sort().join("_");
|
||||||
|
subscriberKey = subscriberKey.sort().join("_");
|
||||||
|
|
||||||
return `${projectKey}_${stateGroupKey}_${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}_${sub_issue}_${startTargetDate}`;
|
return `${projectKey}_${stateGroupKey}_${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}_${sub_issue}_${startTargetDate}_${subscriberKey}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const inboxParamsToKey = (params: any) => {
|
const inboxParamsToKey = (params: any) => {
|
||||||
|
@ -47,7 +47,7 @@ const useIssuesView = () => {
|
|||||||
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
||||||
state: filters?.state ? filters?.state.join(",") : undefined,
|
state: filters?.state ? filters?.state.join(",") : undefined,
|
||||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||||
type: displayFilters?.type ? displayFilters?.type : undefined,
|
type: !isArchivedIssues ? (displayFilters?.type ? displayFilters?.type : undefined) : undefined,
|
||||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||||
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||||
|
Loading…
Reference in New Issue
Block a user