mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: react-datepicker added (#210)
* fix: issue description form * fix: build errors * feat: react-datepicker added
This commit is contained in:
parent
fcf23b985b
commit
0ff5f363ee
@ -1,23 +1,26 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
// next
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// swr
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
// react-beautiful-dnd
|
// react-beautiful-dnd
|
||||||
import { DraggableStateSnapshot } from "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
|
// headless ui
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// constants
|
// constants
|
||||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||||
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
|
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
import stateService from "services/state.service";
|
import stateService from "services/state.service";
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
// components
|
// components
|
||||||
import { CustomSelect, AssigneesList } from "components/ui";
|
import { AssigneesList, CustomDatePicker } from "components/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
@ -256,7 +259,7 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
)} */}
|
)} */}
|
||||||
{properties.due_date && (
|
{properties.due_date && (
|
||||||
<div
|
<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 === null
|
||||||
? ""
|
? ""
|
||||||
: issue.target_date < new Date().toISOString()
|
: issue.target_date < new Date().toISOString()
|
||||||
@ -264,8 +267,37 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
|
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<CalendarDaysIcon className="h-4 w-4" />
|
<CustomDatePicker
|
||||||
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
|
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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{properties.sub_issue_count && (
|
{properties.sub_issue_count && (
|
||||||
|
@ -5,6 +5,9 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
|
// react-datepicker
|
||||||
|
import DatePicker from "react-datepicker";
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
@ -12,7 +15,7 @@ import stateService from "services/state.service";
|
|||||||
// headless ui
|
// headless ui
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, CustomSelect, AssigneesList, Avatar } from "components/ui";
|
import { CustomMenu, CustomSelect, AssigneesList, Avatar, CustomDatePicker } from "components/ui";
|
||||||
// components
|
// components
|
||||||
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
||||||
// icons
|
// icons
|
||||||
@ -241,7 +244,7 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
)} */}
|
)} */}
|
||||||
{properties.due_date && (
|
{properties.due_date && (
|
||||||
<div
|
<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 === null
|
||||||
? ""
|
? ""
|
||||||
: issue.target_date < new Date().toISOString()
|
: issue.target_date < new Date().toISOString()
|
||||||
@ -249,8 +252,37 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
|
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<CalendarDaysIcon className="h-4 w-4" />
|
<CustomDatePicker
|
||||||
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
|
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">
|
<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>
|
<h5 className="mb-1 font-medium text-gray-900">Due date</h5>
|
||||||
<div>{renderShortNumericDateFormat(issue.target_date ?? "")}</div>
|
<div>{renderShortNumericDateFormat(issue.target_date ?? "")}</div>
|
||||||
|
@ -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 CreateUpdateStateModal from "components/project/issues/BoardView/state/create-update-state-modal";
|
||||||
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
||||||
// ui
|
// ui
|
||||||
import { Button, CustomMenu, Input, Loader } from "components/ui";
|
import { Button, CustomDatePicker, CustomMenu, Input, Loader } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
// helpers
|
// helpers
|
||||||
@ -289,20 +289,25 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
<IssueLabelSelect value={value} onChange={onChange} projectId={projectId} />
|
<IssueLabelSelect value={value} onChange={onChange} projectId={projectId} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<div>
|
||||||
control={control}
|
<Controller
|
||||||
name="target_date"
|
control={control}
|
||||||
render={({ field: { value, onChange } }) => (
|
name="target_date"
|
||||||
<input
|
render={({ field: { value, onChange } }) => (
|
||||||
type="date"
|
<CustomDatePicker
|
||||||
value={value ?? ""}
|
value={value}
|
||||||
onChange={(e: any) => {
|
onChange={(val: Date) => {
|
||||||
onChange(e.target.value);
|
onChange(
|
||||||
}}
|
val
|
||||||
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"
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
/>
|
: null
|
||||||
)}
|
);
|
||||||
/>
|
}}
|
||||||
|
className="max-w-[7rem]"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<IssueParentSelect
|
<IssueParentSelect
|
||||||
control={control}
|
control={control}
|
||||||
isOpen={parentIssueListModalOpen}
|
isOpen={parentIssueListModalOpen}
|
||||||
|
@ -4,30 +4,30 @@ import Image from "next/image";
|
|||||||
import Module from "public/onboarding/module.png";
|
import Module from "public/onboarding/module.png";
|
||||||
|
|
||||||
const BreakIntoModules: React.FC = () => (
|
const BreakIntoModules: React.FC = () => (
|
||||||
<div className="h-full space-y-4">
|
<div className="h-full space-y-4">
|
||||||
<div className="relative h-1/2">
|
<div className="relative h-1/2">
|
||||||
<div
|
<div
|
||||||
className="absolute bottom-0 z-10 h-8 w-full bg-white"
|
className="absolute bottom-0 z-10 h-8 w-full bg-white"
|
||||||
style={{
|
style={{
|
||||||
background: "linear-gradient(0deg, #fff 84.2%, rgba(255, 255, 255, 0) 34.35%)",
|
background: "linear-gradient(0deg, #fff 84.2%, rgba(255, 255, 255, 0) 34.35%)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
src={Module}
|
src={Module}
|
||||||
className="h-full"
|
className="h-full"
|
||||||
objectFit="contain"
|
objectFit="contain"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
alt="Plane- Modules"
|
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>
|
</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;
|
export default BreakIntoModules;
|
||||||
|
@ -185,7 +185,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex gap-3">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<label htmlFor="icon" className="mb-2 text-gray-500">
|
<label htmlFor="icon" className="mb-2 text-gray-500">
|
||||||
Icon
|
Icon
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
// next
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// swr
|
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
// react hook form
|
// react hook form
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// headless
|
// headless ui
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// services
|
// services
|
||||||
import cycleService from "services/cycles.service";
|
import cycleService from "services/cycles.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input, TextArea, CustomSelect } from "components/ui";
|
import { Button, Input, TextArea, CustomSelect, CustomDatePicker } from "components/ui";
|
||||||
// common
|
// common
|
||||||
import { renderDateFormat } from "helpers/date-time.helper";
|
import { renderDateFormat } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
@ -31,8 +32,8 @@ const defaultValues: Partial<ICycle> = {
|
|||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
status: "draft",
|
status: "draft",
|
||||||
start_date: new Date().toString(),
|
start_date: null,
|
||||||
end_date: new Date().toString(),
|
end_date: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, projectId }) => {
|
const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, projectId }) => {
|
||||||
@ -202,32 +203,62 @@ const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, proj
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-2">
|
<div className="flex gap-x-2">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Input
|
<h6 className="text-gray-500">Start Date</h6>
|
||||||
id="start_date"
|
<div className="w-full">
|
||||||
label="Start Date"
|
<Controller
|
||||||
name="start_date"
|
control={control}
|
||||||
type="date"
|
name="start_date"
|
||||||
placeholder="Enter start date"
|
rules={{ required: "Start date is required" }}
|
||||||
error={errors.start_date}
|
render={({ field: { value, onChange } }) => (
|
||||||
register={register}
|
<CustomDatePicker
|
||||||
validations={{
|
renderAs="input"
|
||||||
required: "Start date is required",
|
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>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Input
|
<h6 className="text-gray-500">End Date</h6>
|
||||||
id="end_date"
|
<div className="w-full">
|
||||||
label="End Date"
|
<Controller
|
||||||
name="end_date"
|
control={control}
|
||||||
type="date"
|
name="end_date"
|
||||||
placeholder="Enter end date"
|
rules={{ required: "End date is required" }}
|
||||||
error={errors.end_date}
|
render={({ field: { value, onChange } }) => (
|
||||||
register={register}
|
<CustomDatePicker
|
||||||
validations={{
|
renderAs="input"
|
||||||
required: "End date is required",
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +13,7 @@ import cyclesService from "services/cycles.service";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "components/ui";
|
import { Loader, CustomDatePicker } from "components/ui";
|
||||||
//progress-bar
|
//progress-bar
|
||||||
import { CircularProgressbar } from "react-circular-progressbar";
|
import { CircularProgressbar } from "react-circular-progressbar";
|
||||||
import "react-circular-progressbar/dist/styles.css";
|
import "react-circular-progressbar/dist/styles.css";
|
||||||
@ -23,7 +23,7 @@ import { groupBy } from "helpers/array.helper";
|
|||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, ICycle } from "types";
|
import { CycleIssueResponse, ICycle } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLE_DETAIL } from "constants/fetch-keys";
|
import { CYCLE_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cycle: ICycle | undefined;
|
cycle: ICycle | undefined;
|
||||||
@ -38,9 +38,7 @@ const defaultValues: Partial<ICycle> = {
|
|||||||
|
|
||||||
const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) => {
|
const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
query: { workspaceSlug, projectId },
|
|
||||||
} = router;
|
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -60,11 +58,21 @@ const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) =>
|
|||||||
const submitChanges = (data: Partial<ICycle>) => {
|
const submitChanges = (data: Partial<ICycle>) => {
|
||||||
if (!workspaceSlug || !projectId || !module) return;
|
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
|
cyclesService
|
||||||
.patchCycle(workspaceSlug as string, projectId as string, cycle?.id ?? "", data)
|
.patchCycle(workspaceSlug as string, projectId as string, cycle?.id ?? "", data)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
mutate(CYCLE_DETAIL);
|
mutate(CYCLE_LIST(projectId as string));
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@ -160,16 +168,17 @@ const CycleDetailSidebar: React.FC<Props> = ({ cycle, isOpen, cycleIssues }) =>
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="start_date"
|
name="start_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value } }) => (
|
||||||
<input
|
<CustomDatePicker
|
||||||
type="date"
|
value={value}
|
||||||
id="cycleStartDate"
|
onChange={(val: Date) => {
|
||||||
value={value ?? ""}
|
submitChanges({
|
||||||
onChange={(e: any) => {
|
start_date: val
|
||||||
submitChanges({ start_date: e.target.value });
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
onChange(e.target.value);
|
: 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
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="end_date"
|
name="end_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value } }) => (
|
||||||
<input
|
<CustomDatePicker
|
||||||
type="date"
|
value={value}
|
||||||
id="moduleEndDate"
|
onChange={(val: Date) => {
|
||||||
value={value ?? ""}
|
submitChanges({
|
||||||
onChange={(e: any) => {
|
end_date: val
|
||||||
submitChanges({ end_date: e.target.value });
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
onChange(e.target.value);
|
: 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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -68,7 +68,8 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
|
|||||||
|
|
||||||
const handleOnDragEnd = useCallback(
|
const handleOnDragEnd = useCallback(
|
||||||
(result: DropResult) => {
|
(result: DropResult) => {
|
||||||
if (!result.destination) return;
|
if (!result.destination || !workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
const { source, destination, type } = result;
|
const { source, destination, type } = result;
|
||||||
|
|
||||||
if (destination.droppableId === "trashBox") {
|
if (destination.droppableId === "trashBox") {
|
||||||
@ -94,7 +95,7 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
|
|||||||
newStates[destination.index].sequence = sequenceNumber;
|
newStates[destination.index].sequence = sequenceNumber;
|
||||||
|
|
||||||
mutateState(newStates, false);
|
mutateState(newStates, false);
|
||||||
if (!workspaceSlug) return;
|
|
||||||
stateServices
|
stateServices
|
||||||
.patchState(
|
.patchState(
|
||||||
workspaceSlug as string,
|
workspaceSlug as string,
|
||||||
@ -140,18 +141,6 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
|
|||||||
draggedItem.state = destinationStateId;
|
draggedItem.state = destinationStateId;
|
||||||
draggedItem.state_detail = destinationState;
|
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>(
|
mutate<IssueResponse>(
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
||||||
(prevData) => {
|
(prevData) => {
|
||||||
@ -175,6 +164,15 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
|
|||||||
},
|
},
|
||||||
false
|
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));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,11 +76,6 @@ const activityDetails: {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueComment> = {
|
|
||||||
comment_html: "",
|
|
||||||
comment_json: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
const IssueActivitySection: React.FC<{
|
const IssueActivitySection: React.FC<{
|
||||||
issueActivities: IIssueActivity[];
|
issueActivities: IIssueActivity[];
|
||||||
mutate: KeyedMutator<IIssueActivity[]>;
|
mutate: KeyedMutator<IIssueActivity[]>;
|
||||||
@ -99,7 +94,7 @@ const IssueActivitySection: React.FC<{
|
|||||||
comment.id,
|
comment.id,
|
||||||
comment
|
comment
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((res) => {
|
||||||
mutate();
|
mutate();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -180,6 +175,10 @@ const IssueActivitySection: React.FC<{
|
|||||||
? activity.new_value !== ""
|
? activity.new_value !== ""
|
||||||
? "marked this issue being blocked by"
|
? "marked this issue being blocked by"
|
||||||
: "removed blocker"
|
: "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]
|
: activityDetails[activity.field as keyof typeof activityDetails]
|
||||||
?.message}{" "}
|
?.message}{" "}
|
||||||
</span>
|
</span>
|
||||||
@ -203,7 +202,9 @@ const IssueActivitySection: React.FC<{
|
|||||||
) : activity.field === "assignee" ? (
|
) : activity.field === "assignee" ? (
|
||||||
activity.old_value
|
activity.old_value
|
||||||
) : activity.field === "target_date" ? (
|
) : activity.field === "target_date" ? (
|
||||||
renderShortNumericDateFormat(activity.new_value as string)
|
activity.new_value ? (
|
||||||
|
renderShortNumericDateFormat(activity.new_value as string)
|
||||||
|
) : null
|
||||||
) : activity.field === "description" ? (
|
) : activity.field === "description" ? (
|
||||||
""
|
""
|
||||||
) : (
|
) : (
|
||||||
|
@ -4,20 +4,12 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
|
// react-hook-form
|
||||||
import { useForm, Controller, UseFormWatch, Control } from "react-hook-form";
|
import { useForm, Controller, UseFormWatch, Control } from "react-hook-form";
|
||||||
|
// react-color
|
||||||
import { TwitterPicker } from "react-color";
|
import { TwitterPicker } from "react-color";
|
||||||
// services
|
// headless ui
|
||||||
import { Popover, Listbox, Transition } from "@headlessui/react";
|
import { Popover, Listbox, Transition } from "@headlessui/react";
|
||||||
import {
|
|
||||||
TagIcon,
|
|
||||||
ChevronDownIcon,
|
|
||||||
LinkIcon,
|
|
||||||
CalendarDaysIcon,
|
|
||||||
TrashIcon,
|
|
||||||
PlusIcon,
|
|
||||||
XMarkIcon,
|
|
||||||
} from "@heroicons/react/24/outline";
|
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// services
|
// 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 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 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";
|
import SelectBlocked from "components/project/issues/issue-detail/issue-detail-sidebar/select-blocked";
|
||||||
// headless ui
|
|
||||||
// ui
|
// ui
|
||||||
import { Input, Button, Spinner } from "components/ui";
|
import { Input, Button, Spinner, CustomDatePicker } from "components/ui";
|
||||||
import DatePicker from "react-datepicker";
|
import DatePicker from "react-datepicker";
|
||||||
// icons
|
// icons
|
||||||
|
import {
|
||||||
|
TagIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
LinkIcon,
|
||||||
|
CalendarDaysIcon,
|
||||||
|
TrashIcon,
|
||||||
|
PlusIcon,
|
||||||
|
XMarkIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyTextToClipboard } from "helpers/string.helper";
|
import { copyTextToClipboard } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
@ -240,16 +240,16 @@ const IssueDetailSidebar: React.FC<Props> = ({
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="target_date"
|
name="target_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value } }) => (
|
||||||
<input
|
<CustomDatePicker
|
||||||
type="date"
|
value={value}
|
||||||
id="issueDate"
|
onChange={(val: Date) => {
|
||||||
value={value ?? ""}
|
submitChanges({
|
||||||
onChange={(e: any) => {
|
target_date: val
|
||||||
submitChanges({ target_date: e.target.value });
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
onChange(e.target.value);
|
: 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"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
|||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// components
|
// 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 SelectMembers from "components/project/modules/create-update-module-modal/select-members";
|
||||||
import SelectStatus from "components/project/modules/create-update-module-modal/select-status";
|
import SelectStatus from "components/project/modules/create-update-module-modal/select-status";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input, TextArea } from "components/ui";
|
import { Button, CustomDatePicker, Input, TextArea } from "components/ui";
|
||||||
// services
|
// services
|
||||||
import modulesService from "services/modules.service";
|
import modulesService from "services/modules.service";
|
||||||
// hooks
|
// hooks
|
||||||
@ -193,32 +193,62 @@ const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, pro
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-2">
|
<div className="flex gap-x-2">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Input
|
<h6 className="text-gray-500">Start Date</h6>
|
||||||
id="start_date"
|
<div className="w-full">
|
||||||
label="Start Date"
|
<Controller
|
||||||
name="start_date"
|
control={control}
|
||||||
type="date"
|
name="start_date"
|
||||||
placeholder="Enter start date"
|
rules={{ required: "Start date is required" }}
|
||||||
error={errors.start_date}
|
render={({ field: { value, onChange } }) => (
|
||||||
register={register}
|
<CustomDatePicker
|
||||||
validations={{
|
renderAs="input"
|
||||||
required: "Start date is required",
|
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>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Input
|
<h6 className="text-gray-500">Target Date</h6>
|
||||||
id="target_date"
|
<div className="w-full">
|
||||||
label="Target Date"
|
<Controller
|
||||||
name="target_date"
|
control={control}
|
||||||
type="date"
|
name="target_date"
|
||||||
placeholder="Enter target date"
|
rules={{ required: "Target date is required" }}
|
||||||
error={errors.target_date}
|
render={({ field: { value, onChange } }) => (
|
||||||
register={register}
|
<CustomDatePicker
|
||||||
validations={{
|
renderAs="input"
|
||||||
required: "Target date is required",
|
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>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
@ -26,7 +26,7 @@ import ModuleLinkModal from "components/project/modules/module-link-modal";
|
|||||||
import { CircularProgressbar } from "react-circular-progressbar";
|
import { CircularProgressbar } from "react-circular-progressbar";
|
||||||
import "react-circular-progressbar/dist/styles.css";
|
import "react-circular-progressbar/dist/styles.css";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "components/ui";
|
import { CustomDatePicker, Loader } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
// helpers
|
// helpers
|
||||||
import { timeAgo } from "helpers/date-time.helper";
|
import { timeAgo } from "helpers/date-time.helper";
|
||||||
@ -39,8 +39,8 @@ import { MODULE_LIST } from "constants/fetch-keys";
|
|||||||
|
|
||||||
const defaultValues: Partial<IModule> = {
|
const defaultValues: Partial<IModule> = {
|
||||||
members_list: [],
|
members_list: [],
|
||||||
start_date: new Date().toString(),
|
start_date: null,
|
||||||
target_date: new Date().toString(),
|
target_date: null,
|
||||||
status: null,
|
status: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,16 +88,21 @@ const ModuleDetailSidebar: React.FC<Props> = ({
|
|||||||
const submitChanges = (data: Partial<IModule>) => {
|
const submitChanges = (data: Partial<IModule>) => {
|
||||||
if (!workspaceSlug || !projectId || !module) return;
|
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
|
modulesService
|
||||||
.patchModule(workspaceSlug as string, projectId as string, module.id, data)
|
.patchModule(workspaceSlug as string, projectId as string, module.id, data)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
mutate<IModule[]>(projectId && MODULE_LIST(projectId as string), (prevData) =>
|
mutate(MODULE_LIST(projectId as string));
|
||||||
(prevData ?? []).map((module) => {
|
|
||||||
if (module.id === moduleId) return { ...module, ...data };
|
|
||||||
return module;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@ -186,16 +191,16 @@ const ModuleDetailSidebar: React.FC<Props> = ({
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="start_date"
|
name="start_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value } }) => (
|
||||||
<input
|
<CustomDatePicker
|
||||||
type="date"
|
value={value}
|
||||||
id="moduleStartDate"
|
onChange={(val: Date) => {
|
||||||
value={value ?? ""}
|
submitChanges({
|
||||||
onChange={(e: any) => {
|
start_date: val
|
||||||
submitChanges({ start_date: e.target.value });
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
onChange(e.target.value);
|
: 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
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="target_date"
|
name="target_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value } }) => (
|
||||||
<input
|
<CustomDatePicker
|
||||||
type="date"
|
value={value}
|
||||||
id="moduleTargetDate"
|
onChange={(val: Date) => {
|
||||||
value={value ?? ""}
|
submitChanges({
|
||||||
onChange={(e: any) => {
|
target_date: val
|
||||||
submitChanges({ target_date: e.target.value });
|
? `${val.getFullYear()}-${val.getMonth() + 1}-${val.getDate()}`
|
||||||
onChange(e.target.value);
|
: 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"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -12,9 +12,7 @@ import { UserGroupIcon } from "@heroicons/react/24/outline";
|
|||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// headless ui
|
// headless ui
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { AssigneesList, Spinner } from "components/ui";
|
||||||
// icons
|
|
||||||
import User from "public/user.png";
|
|
||||||
// types
|
// types
|
||||||
import { IModule } from "types";
|
import { IModule } from "types";
|
||||||
// constants
|
// constants
|
||||||
@ -64,52 +62,7 @@ const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
|
|||||||
>
|
>
|
||||||
<div className="flex cursor-pointer items-center gap-1 text-xs">
|
<div className="flex cursor-pointer items-center gap-1 text-xs">
|
||||||
{value && Array.isArray(value) ? (
|
{value && Array.isArray(value) ? (
|
||||||
<>
|
<AssigneesList userIds={value} length={10} />
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
40
apps/app/components/ui/datepicker.tsx
Normal file
40
apps/app/components/ui/datepicker.tsx
Normal 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}
|
||||||
|
/>
|
||||||
|
);
|
@ -13,3 +13,4 @@ export * from "./spinner";
|
|||||||
export * from "./text-area";
|
export * from "./text-area";
|
||||||
export * from "./tooltip";
|
export * from "./tooltip";
|
||||||
export * from "./avatar";
|
export * from "./avatar";
|
||||||
|
export * from "./datepicker";
|
||||||
|
@ -50,7 +50,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
|||||||
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
||||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||||
const [deleteIssue, setDeleteIssue] = useState<string | undefined>(undefined);
|
const [deleteIssue, setDeleteIssue] = useState<string | undefined>(undefined);
|
||||||
const [cycleSidebar, setCycleSidebar] = useState(false);
|
const [cycleSidebar, setCycleSidebar] = useState(true);
|
||||||
|
|
||||||
const [preloadedData, setPreloadedData] = useState<
|
const [preloadedData, setPreloadedData] = useState<
|
||||||
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | null
|
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | null
|
||||||
|
@ -71,3 +71,23 @@
|
|||||||
border: none;
|
border: none;
|
||||||
outline: 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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user