chore: select start date option for issue (#1813)

This commit is contained in:
Aaryan Khandelwal 2023-08-09 15:17:32 +05:30 committed by GitHub
parent 5f1209f1db
commit 4fcd081d27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 243 additions and 30 deletions

View File

@ -428,6 +428,40 @@ const activityDetails: {
),
icon: <Icon iconName="signal_cellular_alt" className="!text-sm" aria-hidden="true" />,
},
start_date: {
message: (activity, showIssue) => {
if (!activity.new_value)
return (
<>
removed the start date
{showIssue && (
<>
{" "}
from <IssueLink activity={activity} />
</>
)}
.
</>
);
else
return (
<>
set the start date to{" "}
<span className="font-medium text-custom-text-100">
{renderShortDateWithYearFormat(activity.new_value)}
</span>
{showIssue && (
<>
{" "}
for <IssueLink activity={activity} />
</>
)}
.
</>
);
},
icon: <Icon iconName="calendar_today" className="!text-sm" aria-hidden="true" />,
},
state: {
message: (activity, showIssue) => (
<>

View File

@ -24,6 +24,7 @@ import {
ViewEstimateSelect,
ViewIssueLabel,
ViewPrioritySelect,
ViewStartDateSelect,
ViewStateSelect,
} from "components/issues";
// ui
@ -322,6 +323,14 @@ export const SingleBoardIssue: React.FC<Props> = ({
selfPositioned
/>
)}
{properties.start_date && issue.start_date && (
<ViewStartDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}

View File

@ -21,6 +21,7 @@ import {
ViewEstimateSelect,
ViewLabelSelect,
ViewPrioritySelect,
ViewStartDateSelect,
ViewStateSelect,
} from "components/issues";
// icons
@ -230,7 +231,14 @@ export const SingleCalendarIssue: React.FC<Props> = ({
user={user}
/>
)}
{properties.start_date && issue.start_date && (
<ViewStartDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}

View File

@ -16,6 +16,7 @@ import {
ViewEstimateSelect,
ViewIssueLabel,
ViewPrioritySelect,
ViewStartDateSelect,
ViewStateSelect,
} from "components/issues";
// ui
@ -244,6 +245,14 @@ export const SingleListIssue: React.FC<Props> = ({
isNotAllowed={isNotAllowed}
/>
)}
{properties.start_date && issue.start_date && (
<ViewStartDateSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
user={user}
isNotAllowed={isNotAllowed}
/>
)}
{properties.due_date && issue.target_date && (
<ViewDueDateSelect
issue={issue}

View File

@ -75,6 +75,7 @@ const defaultValues: Partial<IIssue> = {
assignees_list: [],
labels: [],
labels_list: [],
start_date: null,
target_date: null,
};
@ -96,6 +97,7 @@ export interface IssueFormProps {
| "priority"
| "assignee"
| "label"
| "startDate"
| "dueDate"
| "estimate"
| "parent"
@ -239,6 +241,15 @@ export const IssueForm: FC<IssueFormProps> = ({
});
}, [getValues, projectId, reset]);
const startDate = watch("start_date");
const targetDate = watch("target_date");
const minDate = startDate ? new Date(startDate) : null;
minDate?.setDate(minDate.getDate());
const maxDate = targetDate ? new Date(targetDate) : null;
maxDate?.setDate(maxDate.getDate());
return (
<>
{projectId && (
@ -447,13 +458,34 @@ export const IssueForm: FC<IssueFormProps> = ({
)}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("startDate")) && (
<div>
<Controller
control={control}
name="start_date"
render={({ field: { value, onChange } }) => (
<IssueDateSelect
label="Start date"
maxDate={maxDate ?? undefined}
onChange={onChange}
value={value}
/>
)}
/>
</div>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("dueDate")) && (
<div>
<Controller
control={control}
name="target_date"
render={({ field: { value, onChange } }) => (
<IssueDateSelect value={value} onChange={onChange} />
<IssueDateSelect
label="Due date"
minDate={minDate ?? undefined}
onChange={onChange}
value={value}
/>
)}
/>
</div>

View File

@ -53,6 +53,7 @@ export interface IssuesModalProps {
| "priority"
| "assignee"
| "label"
| "startDate"
| "dueDate"
| "estimate"
| "parent"

View File

@ -8,11 +8,14 @@ import DatePicker from "react-datepicker";
import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper";
type Props = {
value: string | null;
label: string;
maxDate?: Date;
minDate?: Date;
onChange: (val: string | null) => void;
value: string | null;
};
export const IssueDateSelect: React.FC<Props> = ({ value, onChange }) => (
export const IssueDateSelect: React.FC<Props> = ({ label, maxDate, minDate, onChange, value }) => (
<Popover className="relative flex items-center justify-center rounded-lg">
{({ open }) => (
<>
@ -28,7 +31,7 @@ export const IssueDateSelect: React.FC<Props> = ({ value, onChange }) => (
) : (
<>
<CalendarDaysIcon className="h-3.5 w-3.5 flex-shrink-0" />
<span>Due Date</span>
<span>{label}</span>
</>
)}
</span>
@ -51,6 +54,8 @@ export const IssueDateSelect: React.FC<Props> = ({ value, onChange }) => (
else onChange(renderDateFormat(val));
}}
dateFormat="dd-MM-yyyy"
minDate={minDate}
maxDate={maxDate}
inline
/>
</Popover.Panel>

View File

@ -54,6 +54,7 @@ type Props = {
| "parent"
| "blocker"
| "blocked"
| "startDate"
| "dueDate"
| "cycle"
| "module"
@ -210,6 +211,15 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
fieldsToShow.includes("cycle") ||
fieldsToShow.includes("module");
const startDate = watchIssue("start_date");
const targetDate = watchIssue("target_date");
const minDate = startDate ? new Date(startDate) : null;
minDate?.setDate(minDate.getDate());
const maxDate = targetDate ? new Date(targetDate) : null;
maxDate?.setDate(maxDate.getDate());
const isNotAllowed = memberRole.isGuest || memberRole.isViewer;
return (
@ -367,6 +377,34 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
disabled={uneditable}
/>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("startDate")) && (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
<CalendarDaysIcon className="h-4 w-4 flex-shrink-0" />
<p>Start date</p>
</div>
<div className="sm:basis-1/2">
<Controller
control={control}
name="start_date"
render={({ field: { value } }) => (
<CustomDatePicker
placeholder="Start date"
value={value}
onChange={(val) =>
submitChanges({
start_date: val,
})
}
className="bg-custom-background-90"
maxDate={maxDate ?? undefined}
disabled={isNotAllowed || uneditable}
/>
)}
/>
</div>
</div>
)}
{(fieldsToShow.includes("all") || fieldsToShow.includes("dueDate")) && (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
@ -387,6 +425,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
})
}
className="bg-custom-background-90"
minDate={minDate ?? undefined}
disabled={isNotAllowed || uneditable}
/>
)}

View File

@ -32,9 +32,12 @@ export const ViewDueDateSelect: React.FC<Props> = ({
const { issueView } = useIssuesView();
const minDate = issue.start_date ? new Date(issue.start_date) : null;
minDate?.setDate(minDate.getDate());
return (
<Tooltip
tooltipHeading="Due Date"
tooltipHeading="Due date"
tooltipContent={
issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"
}
@ -56,8 +59,6 @@ export const ViewDueDateSelect: React.FC<Props> = ({
partialUpdateIssue(
{
target_date: val,
priority: issue.priority,
state: issue.state,
},
issue
);
@ -77,6 +78,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
issueView === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
}`}
minDate={minDate ?? undefined}
noBorder={noBorder}
disabled={isNotAllowed}
/>

View File

@ -1,6 +1,7 @@
export * from "./assignee";
export * from "./due-date";
export * from "./estimate";
export * from "./priority";
export * from "./state";
export * from "./label";
export * from "./priority";
export * from "./start-date";
export * from "./state";

View File

@ -0,0 +1,80 @@
import { useRouter } from "next/router";
// ui
import { CustomDatePicker, Tooltip } from "components/ui";
// helpers
import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper";
// services
import trackEventServices from "services/track-event.service";
// types
import { ICurrentUserResponse, IIssue } from "types";
import useIssuesView from "hooks/use-issues-view";
type Props = {
issue: IIssue;
partialUpdateIssue: (formData: Partial<IIssue>, issue: IIssue) => void;
tooltipPosition?: "top" | "bottom";
noBorder?: boolean;
user: ICurrentUserResponse | undefined;
isNotAllowed: boolean;
};
export const ViewStartDateSelect: React.FC<Props> = ({
issue,
partialUpdateIssue,
tooltipPosition = "top",
noBorder = false,
user,
isNotAllowed,
}) => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { issueView } = useIssuesView();
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
maxDate?.setDate(maxDate.getDate());
return (
<Tooltip
tooltipHeading="Start date"
tooltipContent={
issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"
}
position={tooltipPosition}
>
<div className="group flex-shrink-0 relative max-w-[6.5rem]">
<CustomDatePicker
placeholder="Due date"
value={issue?.start_date}
onChange={(val) => {
partialUpdateIssue(
{
start_date: val,
},
issue
);
trackEventServices.trackIssuePartialPropertyUpdateEvent(
{
workspaceSlug,
workspaceId: issue.workspace,
projectId: issue.project_detail.id,
projectIdentifier: issue.project_detail.identifier,
projectName: issue.project_detail.name,
issueId: issue.id,
},
"ISSUE_PROPERTY_UPDATE_DUE_DATE",
user
);
}}
className={`${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${
issueView === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100"
}`}
maxDate={maxDate ?? undefined}
noBorder={noBorder}
disabled={isNotAllowed}
/>
</div>
</Tooltip>
);
};

View File

@ -15,6 +15,7 @@ type Props = {
className?: string;
isClearable?: boolean;
disabled?: boolean;
maxDate?: Date;
minDate?: Date;
};
@ -29,6 +30,7 @@ export const CustomDatePicker: React.FC<Props> = ({
className = "",
isClearable = true,
disabled = false,
maxDate,
minDate,
}) => (
<DatePicker
@ -54,6 +56,7 @@ export const CustomDatePicker: React.FC<Props> = ({
dateFormat="MMM dd, yyyy"
isClearable={isClearable}
disabled={disabled}
maxDate={maxDate}
minDate={minDate}
/>
);

View File

@ -11,6 +11,7 @@ import { IssuePriorities, Properties } from "types";
const initialValues: Properties = {
assignee: true,
start_date: true,
due_date: true,
key: true,
labels: true,
@ -91,6 +92,7 @@ const useIssuesProperties = (workspaceSlug?: string, projectId?: string) => {
const newProperties: Properties = {
assignee: properties.assignee,
start_date: properties.start_date,
due_date: properties.due_date,
key: properties.key,
labels: properties.labels,

View File

@ -28,17 +28,18 @@ import { PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS } from "constants/fetch-keys";
import { truncateText } from "helpers/string.helper";
const defaultValues = {
name: "",
assignees_list: [],
description: "",
description_html: "",
estimate_point: null,
state: "",
assignees_list: [],
priority: "low",
target_date: new Date().toString(),
issue_cycle: null,
issue_module: null,
labels_list: [],
name: "",
priority: "low",
start_date: null,
state: "",
target_date: null,
};
const IssueDetailsPage: NextPage = () => {

View File

@ -10,6 +10,7 @@ import type {
IWorkspaceLite,
IStateLite,
TStateGroups,
Properties,
} from "types";
export interface IIssueCycle {
@ -147,21 +148,6 @@ export type IssuePriorities = {
user: string;
};
export type Properties = {
assignee: boolean;
due_date: boolean;
labels: boolean;
key: boolean;
priority: boolean;
state: boolean;
sub_issue_count: boolean;
link: boolean;
attachment_count: boolean;
estimate: boolean;
created_on: boolean;
updated_on: boolean;
};
export interface IIssueLabels {
id: string;
created_at: Date;

View File

@ -49,6 +49,7 @@ export interface IWorkspaceBulkInviteFormData {
export type Properties = {
assignee: boolean;
start_date: boolean;
due_date: boolean;
labels: boolean;
key: boolean;