feat: react-datepicker added (#210)

* fix: issue description form

* fix: build errors

* feat: react-datepicker added
This commit is contained in:
Aaryan Khandelwal 2023-01-30 23:16:02 +05:30 committed by GitHub
parent fcf23b985b
commit 0ff5f363ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 409 additions and 251 deletions

View File

@ -1,23 +1,26 @@
import React from "react";
// next
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
// swr
import useSWR, { mutate } from "swr";
// react-beautiful-dnd
import { DraggableStateSnapshot } from "react-beautiful-dnd";
// react-datepicker
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// constants
import { TrashIcon } from "@heroicons/react/24/outline";
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
// services
import issuesService from "services/issues.service";
import stateService from "services/state.service";
import projectService from "services/project.service";
// components
import { CustomSelect, AssigneesList } from "components/ui";
import { AssigneesList, CustomDatePicker } from "components/ui";
// helpers
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
import { addSpaceIfCamelCase } from "helpers/string.helper";
@ -256,7 +259,7 @@ const SingleBoardIssue: React.FC<Props> = ({
)} */}
{properties.due_date && (
<div
className={`group flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
className={`group relative ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
@ -264,8 +267,37 @@ const SingleBoardIssue: React.FC<Props> = ({
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CalendarDaysIcon className="h-4 w-4" />
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
<CustomDatePicker
placeholder="N/A"
value={issue?.target_date}
onChange={(val: Date) => {
partialUpdateIssue({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"}
/>
{/* <DatePicker
placeholderText="N/A"
value={
issue?.target_date ? `${renderShortNumericDateFormat(issue.target_date)}` : "N/A"
}
selected={issue?.target_date ? new Date(issue.target_date) : null}
onChange={(val: Date) => {
partialUpdateIssue({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
dateFormat="dd-MM-yyyy"
className={`cursor-pointer rounded-md border px-2 py-[3px] text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue?.target_date ? "w-[4.5rem]" : "w-[3rem] text-center"
}`}
isClearable
/> */}
</div>
)}
{properties.sub_issue_count && (

View File

@ -5,6 +5,9 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// react-datepicker
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
// services
import issuesService from "services/issues.service";
import workspaceService from "services/workspace.service";
@ -12,7 +15,7 @@ import stateService from "services/state.service";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// ui
import { CustomMenu, CustomSelect, AssigneesList, Avatar } from "components/ui";
import { CustomMenu, CustomSelect, AssigneesList, Avatar, CustomDatePicker } from "components/ui";
// components
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
// icons
@ -241,7 +244,7 @@ const SingleListIssue: React.FC<Props> = ({
)} */}
{properties.due_date && (
<div
className={`group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
className={`group relative ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
@ -249,8 +252,37 @@ const SingleListIssue: React.FC<Props> = ({
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CalendarDaysIcon className="h-4 w-4" />
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
<CustomDatePicker
placeholder="N/A"
value={issue?.target_date}
onChange={(val: Date) => {
partialUpdateIssue({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"}
/>
{/* <DatePicker
placeholderText="N/A"
value={
issue?.target_date ? `${renderShortNumericDateFormat(issue.target_date)}` : "N/A"
}
selected={issue?.target_date ? new Date(issue.target_date) : null}
onChange={(val: Date) => {
partialUpdateIssue({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
dateFormat="dd-MM-yyyy"
className={`cursor-pointer rounded-md border px-2 py-[3px] text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue?.target_date ? "w-[4.5rem]" : "w-[3rem] text-center"
}`}
isClearable
/> */}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium text-gray-900">Due date</h5>
<div>{renderShortNumericDateFormat(issue.target_date ?? "")}</div>

View File

@ -19,7 +19,7 @@ import { CycleSelect as IssueCycleSelect } from "components/cycles/select";
import CreateUpdateStateModal from "components/project/issues/BoardView/state/create-update-state-modal";
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
// ui
import { Button, CustomMenu, Input, Loader } from "components/ui";
import { Button, CustomDatePicker, CustomMenu, Input, Loader } from "components/ui";
// icons
import { XMarkIcon } from "@heroicons/react/24/outline";
// helpers
@ -289,20 +289,25 @@ export const IssueForm: FC<IssueFormProps> = ({
<IssueLabelSelect value={value} onChange={onChange} projectId={projectId} />
)}
/>
<Controller
control={control}
name="target_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
value={value ?? ""}
onChange={(e: any) => {
onChange(e.target.value);
}}
className="cursor-pointer rounded-md border px-2 py-[3px] text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
)}
/>
<div>
<Controller
control={control}
name="target_date"
render={({ field: { value, onChange } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
onChange(
val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null
);
}}
className="max-w-[7rem]"
/>
)}
/>
</div>
<IssueParentSelect
control={control}
isOpen={parentIssueListModalOpen}

View File

@ -4,30 +4,30 @@ import Image from "next/image";
import Module from "public/onboarding/module.png";
const BreakIntoModules: React.FC = () => (
<div className="h-full space-y-4">
<div className="relative h-1/2">
<div
className="absolute bottom-0 z-10 h-8 w-full bg-white"
style={{
background: "linear-gradient(0deg, #fff 84.2%, rgba(255, 255, 255, 0) 34.35%)",
}}
/>
<Image
src={Module}
className="h-full"
objectFit="contain"
layout="fill"
alt="Plane- Modules"
/>
</div>
<div className="mx-auto h-1/2 space-y-4 lg:w-1/2">
<h2 className="text-2xl font-medium">Break into Modules</h2>
<p className="text-sm text-gray-400">
Modules break your big think into Projects or Features, to help you organize better.
</p>
<p className="text-sm text-gray-400">4/5</p>
</div>
<div className="h-full space-y-4">
<div className="relative h-1/2">
<div
className="absolute bottom-0 z-10 h-8 w-full bg-white"
style={{
background: "linear-gradient(0deg, #fff 84.2%, rgba(255, 255, 255, 0) 34.35%)",
}}
/>
<Image
src={Module}
className="h-full"
objectFit="contain"
layout="fill"
alt="Plane- Modules"
/>
</div>
);
<div className="mx-auto h-1/2 space-y-4 lg:w-1/2">
<h2 className="text-2xl font-medium">Break into Modules</h2>
<p className="text-sm text-gray-400">
Modules break your big thing into Projects or Features, to help you organize better.
</p>
<p className="text-sm text-gray-400">4/5</p>
</div>
</div>
);
export default BreakIntoModules;

View File

@ -185,7 +185,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
</p>
</div>
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="flex gap-3">
<div className="flex-shrink-0">
<label htmlFor="icon" className="mb-2 text-gray-500">
Icon

View File

@ -1,18 +1,19 @@
import React, { useEffect } from "react";
// next
import { useRouter } from "next/router";
// swr
import { mutate } from "swr";
// react hook form
import { Controller, useForm } from "react-hook-form";
// headless
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// services
import cycleService from "services/cycles.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { Button, Input, TextArea, CustomSelect } from "components/ui";
import { Button, Input, TextArea, CustomSelect, CustomDatePicker } from "components/ui";
// common
import { renderDateFormat } from "helpers/date-time.helper";
// types
@ -31,8 +32,8 @@ const defaultValues: Partial<ICycle> = {
name: "",
description: "",
status: "draft",
start_date: new Date().toString(),
end_date: new Date().toString(),
start_date: null,
end_date: null,
};
const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, projectId }) => {
@ -202,32 +203,62 @@ const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, proj
</div>
<div className="flex gap-x-2">
<div className="w-full">
<Input
id="start_date"
label="Start Date"
name="start_date"
type="date"
placeholder="Enter start date"
error={errors.start_date}
register={register}
validations={{
required: "Start date is required",
}}
/>
<h6 className="text-gray-500">Start Date</h6>
<div className="w-full">
<Controller
control={control}
name="start_date"
rules={{ required: "Start date is required" }}
render={({ field: { value, onChange } }) => (
<CustomDatePicker
renderAs="input"
value={value}
onChange={(val: Date) => {
onChange(
val
? `${val.getFullYear()}-${
val.getMonth() + 1
}-${val.getDate()}`
: null
);
}}
error={errors.start_date ? true : false}
/>
)}
/>
{errors.start_date && (
<h6 className="text-sm text-red-500">{errors.start_date.message}</h6>
)}
</div>
</div>
<div className="w-full">
<Input
id="end_date"
label="End Date"
name="end_date"
type="date"
placeholder="Enter end date"
error={errors.end_date}
register={register}
validations={{
required: "End date is required",
}}
/>
<h6 className="text-gray-500">End Date</h6>
<div className="w-full">
<Controller
control={control}
name="end_date"
rules={{ required: "End date is required" }}
render={({ field: { value, onChange } }) => (
<CustomDatePicker
renderAs="input"
value={value}
onChange={(val: Date) => {
onChange(
val
? `${val.getFullYear()}-${
val.getMonth() + 1
}-${val.getDate()}`
: null
);
}}
error={errors.end_date ? true : false}
/>
)}
/>
{errors.end_date && (
<h6 className="text-sm text-red-500">{errors.end_date.message}</h6>
)}
</div>
</div>
</div>
</div>

View File

@ -13,7 +13,7 @@ import cyclesService from "services/cycles.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { Loader } from "components/ui";
import { Loader, CustomDatePicker } from "components/ui";
//progress-bar
import { CircularProgressbar } from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
@ -23,7 +23,7 @@ import { groupBy } from "helpers/array.helper";
// types
import { CycleIssueResponse, ICycle } from "types";
// fetch-keys
import { CYCLE_DETAIL } from "constants/fetch-keys";
import { CYCLE_LIST } from "constants/fetch-keys";
type Props = {
cycle: ICycle | undefined;
@ -38,9 +38,7 @@ const defaultValues: Partial<ICycle> = {
const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) => {
const router = useRouter();
const {
query: { workspaceSlug, projectId },
} = router;
const { workspaceSlug, projectId, cycleId } = router.query;
const { setToastAlert } = useToast();
@ -60,11 +58,21 @@ const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) =>
const submitChanges = (data: Partial<ICycle>) => {
if (!workspaceSlug || !projectId || !module) return;
mutate<ICycle[]>(
projectId && CYCLE_LIST(projectId as string),
(prevData) =>
(prevData ?? []).map((tempCycle) => {
if (tempCycle.id === cycleId) return { ...tempCycle, ...data };
return tempCycle;
}),
false
);
cyclesService
.patchCycle(workspaceSlug as string, projectId as string, cycle?.id ?? "", data)
.then((res) => {
console.log(res);
mutate(CYCLE_DETAIL);
mutate(CYCLE_LIST(projectId as string));
})
.catch((e) => {
console.log(e);
@ -160,16 +168,17 @@ const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) =>
<Controller
control={control}
name="start_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
id="cycleStartDate"
value={value ?? ""}
onChange={(e: any) => {
submitChanges({ start_date: e.target.value });
onChange(e.target.value);
render={({ field: { value } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
submitChanges({
start_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className="w-full cursor-pointer rounded-md border bg-transparent px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
isClearable={false}
/>
)}
/>
@ -184,16 +193,17 @@ const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) =>
<Controller
control={control}
name="end_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
id="moduleEndDate"
value={value ?? ""}
onChange={(e: any) => {
submitChanges({ end_date: e.target.value });
onChange(e.target.value);
render={({ field: { value } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
submitChanges({
end_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className="w-full cursor-pointer rounded-md border bg-transparent px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
isClearable={false}
/>
)}
/>

View File

@ -68,7 +68,8 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
const handleOnDragEnd = useCallback(
(result: DropResult) => {
if (!result.destination) return;
if (!result.destination || !workspaceSlug || !projectId) return;
const { source, destination, type } = result;
if (destination.droppableId === "trashBox") {
@ -94,7 +95,7 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
newStates[destination.index].sequence = sequenceNumber;
mutateState(newStates, false);
if (!workspaceSlug) return;
stateServices
.patchState(
workspaceSlug as string,
@ -140,18 +141,6 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
draggedItem.state = destinationStateId;
draggedItem.state_detail = destinationState;
// patch request
issuesServices.patchIssue(
workspaceSlug as string,
projectId as string,
draggedItem.id,
{
state: destinationStateId,
}
);
// mutate the issues
if (!workspaceSlug || !projectId) return;
mutate<IssueResponse>(
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
(prevData) => {
@ -175,6 +164,15 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
},
false
);
// patch request
issuesServices
.patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, {
state: destinationStateId,
})
.then((res) => {
mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string));
});
}
}
}

View File

@ -76,11 +76,6 @@ const activityDetails: {
},
};
const defaultValues: Partial<IIssueComment> = {
comment_html: "",
comment_json: "",
};
const IssueActivitySection: React.FC<{
issueActivities: IIssueActivity[];
mutate: KeyedMutator<IIssueActivity[]>;
@ -99,7 +94,7 @@ const IssueActivitySection: React.FC<{
comment.id,
comment
)
.then((response) => {
.then((res) => {
mutate();
});
};
@ -180,6 +175,10 @@ const IssueActivitySection: React.FC<{
? activity.new_value !== ""
? "marked this issue being blocked by"
: "removed blocker"
: activity.field === "target_date"
? activity.new_value && activity.new_value !== ""
? "set the due date to"
: "removed the due date"
: activityDetails[activity.field as keyof typeof activityDetails]
?.message}{" "}
</span>
@ -203,7 +202,9 @@ const IssueActivitySection: React.FC<{
) : activity.field === "assignee" ? (
activity.old_value
) : activity.field === "target_date" ? (
renderShortNumericDateFormat(activity.new_value as string)
activity.new_value ? (
renderShortNumericDateFormat(activity.new_value as string)
) : null
) : activity.field === "description" ? (
""
) : (

View File

@ -4,20 +4,12 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// react-hook-form
import { useForm, Controller, UseFormWatch, Control } from "react-hook-form";
// react-color
import { TwitterPicker } from "react-color";
// services
// headless ui
import { Popover, Listbox, Transition } from "@headlessui/react";
import {
TagIcon,
ChevronDownIcon,
LinkIcon,
CalendarDaysIcon,
TrashIcon,
PlusIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
// hooks
import useToast from "hooks/use-toast";
// services
@ -31,11 +23,19 @@ import SelectCycle from "components/project/issues/issue-detail/issue-detail-sid
import SelectAssignee from "components/project/issues/issue-detail/issue-detail-sidebar/select-assignee";
import SelectBlocker from "components/project/issues/issue-detail/issue-detail-sidebar/select-blocker";
import SelectBlocked from "components/project/issues/issue-detail/issue-detail-sidebar/select-blocked";
// headless ui
// ui
import { Input, Button, Spinner } from "components/ui";
import { Input, Button, Spinner, CustomDatePicker } from "components/ui";
import DatePicker from "react-datepicker";
// icons
import {
TagIcon,
ChevronDownIcon,
LinkIcon,
CalendarDaysIcon,
TrashIcon,
PlusIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
@ -240,16 +240,16 @@ const IssueDetailSidebar: React.FC<Props> = ({
<Controller
control={control}
name="target_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
id="issueDate"
value={value ?? ""}
onChange={(e: any) => {
submitChanges({ target_date: e.target.value });
onChange(e.target.value);
render={({ field: { value } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
submitChanges({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className="w-full cursor-pointer rounded-md border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
)}
/>

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import { mutate } from "swr";
// react-hook-form
import { useForm } from "react-hook-form";
import { Controller, useForm } from "react-hook-form";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// components
@ -13,7 +13,7 @@ import SelectLead from "components/project/modules/create-update-module-modal/se
import SelectMembers from "components/project/modules/create-update-module-modal/select-members";
import SelectStatus from "components/project/modules/create-update-module-modal/select-status";
// ui
import { Button, Input, TextArea } from "components/ui";
import { Button, CustomDatePicker, Input, TextArea } from "components/ui";
// services
import modulesService from "services/modules.service";
// hooks
@ -193,32 +193,62 @@ const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, pro
</div>
<div className="flex gap-x-2">
<div className="w-full">
<Input
id="start_date"
label="Start Date"
name="start_date"
type="date"
placeholder="Enter start date"
error={errors.start_date}
register={register}
validations={{
required: "Start date is required",
}}
/>
<h6 className="text-gray-500">Start Date</h6>
<div className="w-full">
<Controller
control={control}
name="start_date"
rules={{ required: "Start date is required" }}
render={({ field: { value, onChange } }) => (
<CustomDatePicker
renderAs="input"
value={value}
onChange={(val: Date) => {
onChange(
val
? `${val.getFullYear()}-${
val.getMonth() + 1
}-${val.getDate()}`
: null
);
}}
error={errors.start_date ? true : false}
/>
)}
/>
{errors.start_date && (
<h6 className="text-sm text-red-500">{errors.start_date.message}</h6>
)}
</div>
</div>
<div className="w-full">
<Input
id="target_date"
label="Target Date"
name="target_date"
type="date"
placeholder="Enter target date"
error={errors.target_date}
register={register}
validations={{
required: "Target date is required",
}}
/>
<h6 className="text-gray-500">Target Date</h6>
<div className="w-full">
<Controller
control={control}
name="target_date"
rules={{ required: "Target date is required" }}
render={({ field: { value, onChange } }) => (
<CustomDatePicker
renderAs="input"
value={value}
onChange={(val: Date) => {
onChange(
val
? `${val.getFullYear()}-${
val.getMonth() + 1
}-${val.getDate()}`
: null
);
}}
error={errors.target_date ? true : false}
/>
)}
/>
{errors.target_date && (
<h6 className="text-sm text-red-500">{errors.target_date.message}</h6>
)}
</div>
</div>
</div>
<div className="flex flex-wrap items-center gap-2">

View File

@ -26,7 +26,7 @@ import ModuleLinkModal from "components/project/modules/module-link-modal";
import { CircularProgressbar } from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
// ui
import { Loader } from "components/ui";
import { CustomDatePicker, Loader } from "components/ui";
// icons
// helpers
import { timeAgo } from "helpers/date-time.helper";
@ -39,8 +39,8 @@ import { MODULE_LIST } from "constants/fetch-keys";
const defaultValues: Partial<IModule> = {
members_list: [],
start_date: new Date().toString(),
target_date: new Date().toString(),
start_date: null,
target_date: null,
status: null,
};
@ -88,16 +88,21 @@ const ModuleDetailSidebar: React.FC<Props> = ({
const submitChanges = (data: Partial<IModule>) => {
if (!workspaceSlug || !projectId || !module) return;
mutate<IModule[]>(
projectId && MODULE_LIST(projectId as string),
(prevData) =>
(prevData ?? []).map((module) => {
if (module.id === moduleId) return { ...module, ...data };
return module;
}),
false
);
modulesService
.patchModule(workspaceSlug as string, projectId as string, module.id, data)
.then((res) => {
console.log(res);
mutate<IModule[]>(projectId && MODULE_LIST(projectId as string), (prevData) =>
(prevData ?? []).map((module) => {
if (module.id === moduleId) return { ...module, ...data };
return module;
})
);
mutate(MODULE_LIST(projectId as string));
})
.catch((e) => {
console.log(e);
@ -186,16 +191,16 @@ const ModuleDetailSidebar: React.FC<Props> = ({
<Controller
control={control}
name="start_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
id="moduleStartDate"
value={value ?? ""}
onChange={(e: any) => {
submitChanges({ start_date: e.target.value });
onChange(e.target.value);
render={({ field: { value } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
submitChanges({
start_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className="w-full cursor-pointer rounded-md border bg-transparent px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
)}
/>
@ -210,16 +215,16 @@ const ModuleDetailSidebar: React.FC<Props> = ({
<Controller
control={control}
name="target_date"
render={({ field: { value, onChange } }) => (
<input
type="date"
id="moduleTargetDate"
value={value ?? ""}
onChange={(e: any) => {
submitChanges({ target_date: e.target.value });
onChange(e.target.value);
render={({ field: { value } }) => (
<CustomDatePicker
value={value}
onChange={(val: Date) => {
submitChanges({
target_date: val
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
: null,
});
}}
className="w-full cursor-pointer rounded-md border bg-transparent px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
)}
/>

View File

@ -12,9 +12,7 @@ import { UserGroupIcon } from "@heroicons/react/24/outline";
import workspaceService from "services/workspace.service";
// headless ui
// ui
import { Spinner } from "components/ui";
// icons
import User from "public/user.png";
import { AssigneesList, Spinner } from "components/ui";
// types
import { IModule } from "types";
// constants
@ -64,52 +62,7 @@ const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
>
<div className="flex cursor-pointer items-center gap-1 text-xs">
{value && Array.isArray(value) ? (
<>
{value.length > 0 ? (
value.map((member, index: number) => {
const person = people?.find((p) => p.member.id === member)?.member;
return (
<div
key={index}
className={`relative z-[1] h-5 w-5 rounded-full ${
index !== 0 ? "-ml-2.5" : ""
}`}
>
{person && person.avatar && person.avatar !== "" ? (
<div className="h-5 w-5 rounded-full border-2 border-white bg-white">
<Image
src={person.avatar}
height="100%"
width="100%"
className="rounded-full"
alt={person.first_name}
/>
</div>
) : (
<div
className={`grid h-5 w-5 place-items-center rounded-full border-2 border-white bg-gray-700 capitalize text-white`}
>
{person?.first_name && person.first_name !== ""
? person.first_name.charAt(0)
: person?.email.charAt(0)}
</div>
)}
</div>
);
})
) : (
<div className="h-5 w-5 rounded-full border-2 border-white bg-white">
<Image
src={User}
height="100%"
width="100%"
className="rounded-full"
alt="No user"
/>
</div>
)}
</>
<AssigneesList userIds={value} length={10} />
) : null}
</div>
</span>

View File

@ -0,0 +1,40 @@
// react-datepicker
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
type Props = {
renderAs?: "input" | "button";
value: Date | string | null | undefined;
onChange: (arg: Date) => void;
placeholder?: string;
displayShortForm?: boolean;
error?: boolean;
className?: string;
isClearable?: boolean;
};
export const CustomDatePicker: React.FC<Props> = ({
renderAs = "button",
value,
onChange,
placeholder = "Select date",
displayShortForm = false,
error = false,
className = "",
isClearable = true,
}) => (
<DatePicker
placeholderText={placeholder}
selected={value ? new Date(value) : null}
onChange={onChange}
dateFormat="dd-MM-yyyy"
className={`${className} ${
renderAs === "input"
? "block bg-transparent text-sm focus:outline-none rounded-md border border-gray-300 px-3 py-2 w-full cursor-pointer"
: renderAs === "button"
? "w-full cursor-pointer rounded-md border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
: ""
} ${error ? "border-red-500 bg-red-200" : ""} bg-transparent caret-transparent`}
isClearable={isClearable}
/>
);

View File

@ -13,3 +13,4 @@ export * from "./spinner";
export * from "./text-area";
export * from "./tooltip";
export * from "./avatar";
export * from "./datepicker";

View File

@ -50,7 +50,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
const [deleteIssue, setDeleteIssue] = useState<string | undefined>(undefined);
const [cycleSidebar, setCycleSidebar] = useState(false);
const [cycleSidebar, setCycleSidebar] = useState(true);
const [preloadedData, setPreloadedData] = useState<
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | null

View File

@ -71,3 +71,23 @@
border: none;
outline: none;
}
/* react datepicker styling */
.react-datepicker-wrapper input::placeholder {
color: #000;
opacity: 1;
}
.react-datepicker-wrapper input:-ms-input-placeholder {
color: #000;
}
.react-datepicker-wrapper .react-datepicker__close-icon::after {
background: transparent;
color: #000;
}
.react-datepicker-popper {
z-index: 30 !important;
}
/* end react datepicker styling */