fix: module and cycle sidebar loading state (#2831)

This commit is contained in:
Anmol Singh Bhatia 2023-11-22 15:34:39 +05:30 committed by GitHub
parent 1dff6b63f8
commit e6a1f34713
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 441 additions and 445 deletions

View File

@ -270,7 +270,20 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
? Math.round((cycleDetails.completed_issues / cycleDetails.total_issues) * 100) ? Math.round((cycleDetails.completed_issues / cycleDetails.total_issues) * 100)
: null; : null;
if (!cycleDetails) return null; if (!cycleDetails)
return (
<Loader className="px-5">
<div className="space-y-2">
<Loader.Item height="15px" width="50%" />
<Loader.Item height="15px" width="30%" />
</div>
<div className="mt-8 space-y-3">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</div>
</Loader>
);
const endDate = new Date(cycleDetails.end_date ?? ""); const endDate = new Date(cycleDetails.end_date ?? "");
const startDate = new Date(cycleDetails.start_date ?? ""); const startDate = new Date(cycleDetails.start_date ?? "");
@ -300,57 +313,91 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
/> />
)} )}
{cycleDetails ? ( <>
<> <div className="flex items-center justify-between w-full">
<div className="flex items-center justify-between w-full"> <div>
<div> <button
<button className="flex items-center justify-center h-5 w-5 rounded-full bg-custom-border-300"
className="flex items-center justify-center h-5 w-5 rounded-full bg-custom-border-300" onClick={() => handleClose()}
onClick={() => handleClose()} >
> <ChevronRight className="h-3 w-3 text-white stroke-2" />
<ChevronRight className="h-3 w-3 text-white stroke-2" /> </button>
</button>
</div>
<div className="flex items-center gap-3.5">
<button onClick={handleCopyText}>
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
{!isCompleted && (
<CustomMenu width="lg" placement="bottom-end" ellipsis>
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-3 w-3" />
<span>Delete cycle</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>
)}
</div>
</div> </div>
<div className="flex items-center gap-3.5">
<button onClick={handleCopyText}>
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
{!isCompleted && (
<CustomMenu width="lg" placement="bottom-end" ellipsis>
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-3 w-3" />
<span>Delete cycle</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>
)}
</div>
</div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<h4 className="text-xl font-semibold break-words w-full text-custom-text-100">{cycleDetails.name}</h4> <h4 className="text-xl font-semibold break-words w-full text-custom-text-100">{cycleDetails.name}</h4>
<div className="flex items-center gap-5"> <div className="flex items-center gap-5">
{currentCycle && ( {currentCycle && (
<span <span
className="flex items-center justify-center text-xs text-center h-6 w-20 rounded-sm" className="flex items-center justify-center text-xs text-center h-6 w-20 rounded-sm"
style={{ style={{
color: currentCycle.color, color: currentCycle.color,
backgroundColor: `${currentCycle.color}20`, backgroundColor: `${currentCycle.color}20`,
}} }}
>
{currentCycle.value === "current"
? `${findHowManyDaysLeft(cycleDetails.end_date ?? new Date())} ${currentCycle.label}`
: `${currentCycle.label}`}
</span>
)}
<div className="relative flex h-full w-52 items-center gap-2.5">
<Popover className="flex h-full items-center justify-center rounded-lg">
<Popover.Button
disabled={isCompleted ?? false}
className="text-sm text-custom-text-300 font-medium cursor-default"
> >
{currentCycle.value === "current" {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")}
? `${findHowManyDaysLeft(cycleDetails.end_date ?? new Date())} ${currentCycle.label}` </Popover.Button>
: `${currentCycle.label}`}
</span> <Transition
)} as={React.Fragment}
<div className="relative flex h-full w-52 items-center gap-2.5"> enter="transition ease-out duration-200"
<Popover className="flex h-full items-center justify-center rounded-lg"> enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
<CustomRangeDatePicker
value={watch("start_date") ? watch("start_date") : cycleDetails?.start_date}
onChange={(val) => {
if (val) {
handleStartDateChange(val);
}
}}
startDate={watch("start_date") ? `${watch("start_date")}` : null}
endDate={watch("end_date") ? `${watch("end_date")}` : null}
maxDate={new Date(`${watch("end_date")}`)}
selectsStart
/>
</Popover.Panel>
</Transition>
</Popover>
<MoveRight className="h-4 w-4 text-custom-text-300" />
<Popover className="flex h-full items-center justify-center rounded-lg">
<>
<Popover.Button <Popover.Button
disabled={isCompleted ?? false} disabled={isCompleted ?? false}
className="text-sm text-custom-text-300 font-medium cursor-default" className="text-sm text-custom-text-300 font-medium cursor-default"
> >
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
</Popover.Button> </Popover.Button>
<Transition <Transition
@ -362,193 +409,145 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden"> <Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
<CustomRangeDatePicker <CustomRangeDatePicker
value={watch("start_date") ? watch("start_date") : cycleDetails?.start_date} value={watch("end_date") ? watch("end_date") : cycleDetails?.end_date}
onChange={(val) => { onChange={(val) => {
if (val) { if (val) {
handleStartDateChange(val); handleEndDateChange(val);
} }
}} }}
startDate={watch("start_date") ? `${watch("start_date")}` : null} startDate={watch("start_date") ? `${watch("start_date")}` : null}
endDate={watch("end_date") ? `${watch("end_date")}` : null} endDate={watch("end_date") ? `${watch("end_date")}` : null}
maxDate={new Date(`${watch("end_date")}`)} minDate={new Date(`${watch("start_date")}`)}
selectsStart selectsEnd
/> />
</Popover.Panel> </Popover.Panel>
</Transition> </Transition>
</Popover> </>
<MoveRight className="h-4 w-4 text-custom-text-300" /> </Popover>
<Popover className="flex h-full items-center justify-center rounded-lg"> </div>
<> </div>
<Popover.Button </div>
disabled={isCompleted ?? false}
className="text-sm text-custom-text-300 font-medium cursor-default"
>
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
</Popover.Button>
<Transition {cycleDetails.description && (
as={React.Fragment} <span className="whitespace-normal text-sm leading-5 py-2.5 text-custom-text-200 break-words w-full">
enter="transition ease-out duration-200" {cycleDetails.description}
enterFrom="opacity-0 translate-y-1" </span>
enterTo="opacity-100 translate-y-0" )}
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0" <div className="flex flex-col gap-5 pt-2.5 pb-6">
leaveTo="opacity-0 translate-y-1" <div className="flex items-center justify-start gap-1">
> <div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden"> <UserCircle2 className="h-4 w-4" />
<CustomRangeDatePicker <span className="text-base">Lead</span>
value={watch("end_date") ? watch("end_date") : cycleDetails?.end_date} </div>
onChange={(val) => { <div className="flex items-center w-1/2 rounded-sm">
if (val) { <div className="flex items-center gap-2.5">
handleEndDateChange(val); <Avatar name={cycleDetails.owned_by.display_name} src={cycleDetails.owned_by.avatar} />
} <span className="text-sm text-custom-text-200">{cycleDetails.owned_by.display_name}</span>
}}
startDate={watch("start_date") ? `${watch("start_date")}` : null}
endDate={watch("end_date") ? `${watch("end_date")}` : null}
minDate={new Date(`${watch("start_date")}`)}
selectsEnd
/>
</Popover.Panel>
</Transition>
</>
</Popover>
</div> </div>
</div> </div>
</div> </div>
{cycleDetails.description && ( <div className="flex items-center justify-start gap-1">
<span className="whitespace-normal text-sm leading-5 py-2.5 text-custom-text-200 break-words w-full"> <div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
{cycleDetails.description} <LayersIcon className="h-4 w-4" />
</span> <span className="text-base">Issues</span>
)}
<div className="flex flex-col gap-5 pt-2.5 pb-6">
<div className="flex items-center justify-start gap-1">
<div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
<UserCircle2 className="h-4 w-4" />
<span className="text-base">Lead</span>
</div>
<div className="flex items-center w-1/2 rounded-sm">
<div className="flex items-center gap-2.5">
<Avatar name={cycleDetails.owned_by.display_name} src={cycleDetails.owned_by.avatar} />
<span className="text-sm text-custom-text-200">{cycleDetails.owned_by.display_name}</span>
</div>
</div>
</div> </div>
<div className="flex items-center w-1/2">
<div className="flex items-center justify-start gap-1"> <span className="text-sm text-custom-text-300 px-1.5">{issueCount}</span>
<div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
<LayersIcon className="h-4 w-4" />
<span className="text-base">Issues</span>
</div>
<div className="flex items-center w-1/2">
<span className="text-sm text-custom-text-300 px-1.5">{issueCount}</span>
</div>
</div> </div>
</div> </div>
</div>
<div className="flex flex-col"> <div className="flex flex-col">
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5"> <div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5">
<Disclosure> <Disclosure>
{({ open }) => ( {({ open }) => (
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}> <div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
<Disclosure.Button <Disclosure.Button
className="flex w-full items-center justify-between gap-2 p-1.5" className="flex w-full items-center justify-between gap-2 p-1.5"
disabled={!isStartValid || !isEndValid} disabled={!isStartValid || !isEndValid}
> >
<div className="flex items-center justify-start gap-2 text-sm"> <div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-custom-text-200">Progress</span> <span className="font-medium text-custom-text-200">Progress</span>
</div> </div>
<div className="flex items-center gap-2.5"> <div className="flex items-center gap-2.5">
{progressPercentage ? ( {progressPercentage ? (
<span className="flex items-center justify-center h-5 w-9 rounded text-xs font-medium text-amber-500 bg-amber-50"> <span className="flex items-center justify-center h-5 w-9 rounded text-xs font-medium text-amber-500 bg-amber-50">
{progressPercentage ? `${progressPercentage}%` : ""} {progressPercentage ? `${progressPercentage}%` : ""}
</span>
) : (
""
)}
{isStartValid && isEndValid ? (
<ChevronDown className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} aria-hidden="true" />
) : (
<div className="flex items-center gap-1">
<AlertCircle height={14} width={14} className="text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
Invalid date. Please enter valid date.
</span> </span>
</div>
)}
</div>
</Disclosure.Button>
<Transition show={open}>
<Disclosure.Panel>
<div className="flex flex-col gap-3">
{isStartValid && isEndValid ? (
<div className="h-full w-full pt-4">
<div className="flex items-start gap-4 py-2 text-xs">
<div className="flex items-center gap-3 text-custom-text-100">
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
</div>
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#4C8FFF]" />
<span>Current</span>
</div>
</div>
</div>
<div className="relative h-40 w-80">
<ProgressChart
distribution={cycleDetails.distribution?.completion_chart ?? {}}
startDate={cycleDetails.start_date ?? ""}
endDate={cycleDetails.end_date ?? ""}
totalIssues={cycleDetails.total_issues}
/>
</div>
</div>
) : ( ) : (
"" ""
)} )}
{isStartValid && isEndValid ? ( {cycleDetails.total_issues > 0 && cycleDetails.distribution && (
<ChevronDown className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} aria-hidden="true" /> <div className="h-full w-full pt-5 border-t border-custom-border-200">
) : ( <SidebarProgressStats
<div className="flex items-center gap-1"> distribution={cycleDetails.distribution}
<AlertCircle height={14} width={14} className="text-custom-text-200" /> groupedIssues={{
<span className="text-xs italic text-custom-text-200"> backlog: cycleDetails.backlog_issues,
Invalid date. Please enter valid date. unstarted: cycleDetails.unstarted_issues,
</span> started: cycleDetails.started_issues,
completed: cycleDetails.completed_issues,
cancelled: cycleDetails.cancelled_issues,
}}
totalIssues={cycleDetails.total_issues}
isPeekView={Boolean(peekCycle)}
/>
</div> </div>
)} )}
</div> </div>
</Disclosure.Button> </Disclosure.Panel>
<Transition show={open}> </Transition>
<Disclosure.Panel> </div>
<div className="flex flex-col gap-3"> )}
{isStartValid && isEndValid ? ( </Disclosure>
<div className="h-full w-full pt-4">
<div className="flex items-start gap-4 py-2 text-xs">
<div className="flex items-center gap-3 text-custom-text-100">
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
</div>
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#4C8FFF]" />
<span>Current</span>
</div>
</div>
</div>
<div className="relative h-40 w-80">
<ProgressChart
distribution={cycleDetails.distribution?.completion_chart ?? {}}
startDate={cycleDetails.start_date ?? ""}
endDate={cycleDetails.end_date ?? ""}
totalIssues={cycleDetails.total_issues}
/>
</div>
</div>
) : (
""
)}
{cycleDetails.total_issues > 0 && cycleDetails.distribution && (
<div className="h-full w-full pt-5 border-t border-custom-border-200">
<SidebarProgressStats
distribution={cycleDetails.distribution}
groupedIssues={{
backlog: cycleDetails.backlog_issues,
unstarted: cycleDetails.unstarted_issues,
started: cycleDetails.started_issues,
completed: cycleDetails.completed_issues,
cancelled: cycleDetails.cancelled_issues,
}}
totalIssues={cycleDetails.total_issues}
isPeekView={Boolean(peekCycle)}
/>
</div>
)}
</div>
</Disclosure.Panel>
</Transition>
</div>
)}
</Disclosure>
</div>
</div> </div>
</> </div>
) : ( </>
<Loader className="px-5">
<div className="space-y-2">
<Loader.Item height="15px" width="50%" />
<Loader.Item height="15px" width="30%" />
</div>
<div className="mt-8 space-y-3">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</div>
</Loader>
)}
</> </>
); );
}); });

View File

@ -198,7 +198,20 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
setModuleLinkModal(true); setModuleLinkModal(true);
}; };
if (!moduleDetails) return null; if (!moduleDetails)
return (
<Loader className="px-5">
<div className="space-y-2">
<Loader.Item height="15px" width="50%" />
<Loader.Item height="15px" width="30%" />
</div>
<div className="mt-8 space-y-3">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</div>
</Loader>
);
const startDate = new Date(moduleDetails.start_date ?? ""); const startDate = new Date(moduleDetails.start_date ?? "");
const endDate = new Date(moduleDetails.target_date ?? ""); const endDate = new Date(moduleDetails.target_date ?? "");
@ -230,200 +243,200 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
updateIssueLink={handleUpdateLink} updateIssueLink={handleUpdateLink}
/> />
<DeleteModuleModal isOpen={moduleDeleteModal} onClose={() => setModuleDeleteModal(false)} data={moduleDetails} /> <DeleteModuleModal isOpen={moduleDeleteModal} onClose={() => setModuleDeleteModal(false)} data={moduleDetails} />
{module ? (
<> <>
<div className="flex items-center justify-between w-full"> <div className="flex items-center justify-between w-full">
<div> <div>
<button <button
className="flex items-center justify-center h-5 w-5 rounded-full bg-custom-border-300" className="flex items-center justify-center h-5 w-5 rounded-full bg-custom-border-300"
onClick={() => handleClose()} onClick={() => handleClose()}
> >
<ChevronRight className="h-3 w-3 text-white stroke-2" /> <ChevronRight className="h-3 w-3 text-white stroke-2" />
</button> </button>
</div>
<div className="flex items-center gap-3.5">
<button onClick={handleCopyText}>
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
<CustomMenu width="lg" placement="bottom-end" ellipsis>
<CustomMenu.MenuItem onClick={() => setModuleDeleteModal(true)}>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-3 w-3" />
<span>Delete module</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>
</div>
</div> </div>
<div className="flex items-center gap-3.5">
<div className="flex flex-col gap-3"> <button onClick={handleCopyText}>
<h4 className="text-xl font-semibold break-words w-full text-custom-text-100">{moduleDetails.name}</h4> <LinkIcon className="h-3 w-3 text-custom-text-300" />
<div className="flex items-center gap-5"> </button>
<Controller <CustomMenu width="lg" placement="bottom-end" ellipsis>
control={control} <CustomMenu.MenuItem onClick={() => setModuleDeleteModal(true)}>
name="status" <span className="flex items-center justify-start gap-2">
render={({ field: { value } }) => ( <Trash2 className="h-3 w-3" />
<CustomSelect <span>Delete module</span>
customButton={ </span>
<span </CustomMenu.MenuItem>
className={`flex items-center cursor-default justify-center text-sm h-6 w-20 rounded-sm ${moduleStatus?.textColor} ${moduleStatus?.bgColor}`} </CustomMenu>
>
{moduleStatus?.label ?? "Backlog"}
</span>
}
value={value}
onChange={(value: any) => {
submitChanges({ status: value });
}}
>
{MODULE_STATUS.map((status) => (
<CustomSelect.Option key={status.value} value={status.value}>
<div className="flex items-center gap-2">
<ModuleStatusIcon status={status.value} />
{status.label}
</div>
</CustomSelect.Option>
))}
</CustomSelect>
)}
/>
<span className="text-sm text-custom-text-300 font-mediu cursor-default">
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "}
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
</span>
</div>
</div> </div>
</div>
{moduleDetails.description && ( <div className="flex flex-col gap-3">
<span className="whitespace-normal text-sm leading-5 py-2.5 text-custom-text-200 break-words w-full"> <h4 className="text-xl font-semibold break-words w-full text-custom-text-100">{moduleDetails.name}</h4>
{moduleDetails.description} <div className="flex items-center gap-5">
</span>
)}
<div className="flex flex-col gap-5 pt-2.5 pb-6">
<Controller <Controller
control={control} control={control}
name="lead" name="status"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<SidebarLeadSelect <CustomSelect
value={value} customButton={
onChange={(val: string) => { <span
submitChanges({ lead: val }); className={`flex items-center cursor-default justify-center text-sm h-6 w-20 rounded-sm ${moduleStatus?.textColor} ${moduleStatus?.bgColor}`}
}}
/>
)}
/>
<Controller
control={control}
name="members"
render={({ field: { value } }) => (
<SidebarMembersSelect
value={value}
onChange={(val: string[]) => {
submitChanges({ members: val });
}}
/>
)}
/>
<div className="flex items-center justify-start gap-1">
<div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
<LayersIcon className="h-4 w-4" />
<span className="text-base">Issues</span>
</div>
<div className="flex items-center w-1/2">
<span className="text-sm text-custom-text-300 px-1.5">{issueCount}</span>
</div>
</div>
</div>
<div className="flex flex-col">
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5">
<Disclosure>
{({ open }) => (
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
<Disclosure.Button
className="flex w-full items-center justify-between gap-2 p-1.5"
disabled={!isStartValid || !isEndValid}
> >
<div className="flex items-center justify-start gap-2 text-sm"> {moduleStatus?.label ?? "Backlog"}
<span className="font-medium text-custom-text-200">Progress</span> </span>
}
value={value}
onChange={(value: any) => {
submitChanges({ status: value });
}}
>
{MODULE_STATUS.map((status) => (
<CustomSelect.Option key={status.value} value={status.value}>
<div className="flex items-center gap-2">
<ModuleStatusIcon status={status.value} />
{status.label}
</div> </div>
</CustomSelect.Option>
))}
</CustomSelect>
)}
/>
<div className="flex items-center gap-2.5"> <span className="text-sm text-custom-text-300 font-mediu cursor-default">
{progressPercentage ? ( {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "}
<span className="flex items-center justify-center h-5 w-9 rounded text-xs font-medium text-amber-500 bg-amber-50"> {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
{progressPercentage ? `${progressPercentage}%` : ""} </span>
</div>
</div>
{moduleDetails.description && (
<span className="whitespace-normal text-sm leading-5 py-2.5 text-custom-text-200 break-words w-full">
{moduleDetails.description}
</span>
)}
<div className="flex flex-col gap-5 pt-2.5 pb-6">
<Controller
control={control}
name="lead"
render={({ field: { value } }) => (
<SidebarLeadSelect
value={value}
onChange={(val: string) => {
submitChanges({ lead: val });
}}
/>
)}
/>
<Controller
control={control}
name="members"
render={({ field: { value } }) => (
<SidebarMembersSelect
value={value}
onChange={(val: string[]) => {
submitChanges({ members: val });
}}
/>
)}
/>
<div className="flex items-center justify-start gap-1">
<div className="flex w-1/2 items-center justify-start gap-2 text-custom-text-300">
<LayersIcon className="h-4 w-4" />
<span className="text-base">Issues</span>
</div>
<div className="flex items-center w-1/2">
<span className="text-sm text-custom-text-300 px-1.5">{issueCount}</span>
</div>
</div>
</div>
<div className="flex flex-col">
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5">
<Disclosure>
{({ open }) => (
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
<Disclosure.Button
className="flex w-full items-center justify-between gap-2 p-1.5"
disabled={!isStartValid || !isEndValid}
>
<div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-custom-text-200">Progress</span>
</div>
<div className="flex items-center gap-2.5">
{progressPercentage ? (
<span className="flex items-center justify-center h-5 w-9 rounded text-xs font-medium text-amber-500 bg-amber-50">
{progressPercentage ? `${progressPercentage}%` : ""}
</span>
) : (
""
)}
{isStartValid && isEndValid ? (
<ChevronDown className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} aria-hidden="true" />
) : (
<div className="flex items-center gap-1">
<AlertCircle height={14} width={14} className="text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
Invalid date. Please enter valid date.
</span> </span>
</div>
)}
</div>
</Disclosure.Button>
<Transition show={open}>
<Disclosure.Panel>
<div className="flex flex-col gap-3">
{isStartValid && isEndValid ? (
<div className=" h-full w-full pt-4">
<div className="flex items-start gap-4 py-2 text-xs">
<div className="flex items-center gap-3 text-custom-text-100">
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
</div>
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#4C8FFF]" />
<span>Current</span>
</div>
</div>
</div>
<div className="relative h-40 w-80">
<ProgressChart
distribution={moduleDetails.distribution.completion_chart}
startDate={moduleDetails.start_date ?? ""}
endDate={moduleDetails.target_date ?? ""}
totalIssues={moduleDetails.total_issues}
/>
</div>
</div>
) : ( ) : (
"" ""
)} )}
{isStartValid && isEndValid ? ( {moduleDetails.total_issues > 0 && (
<ChevronDown className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} aria-hidden="true" /> <div className="h-full w-full pt-5 border-t border-custom-border-200">
) : ( <SidebarProgressStats
<div className="flex items-center gap-1"> distribution={moduleDetails.distribution}
<AlertCircle height={14} width={14} className="text-custom-text-200" /> groupedIssues={{
<span className="text-xs italic text-custom-text-200"> backlog: moduleDetails.backlog_issues,
Invalid date. Please enter valid date. unstarted: moduleDetails.unstarted_issues,
</span> started: moduleDetails.started_issues,
completed: moduleDetails.completed_issues,
cancelled: moduleDetails.cancelled_issues,
}}
totalIssues={moduleDetails.total_issues}
module={moduleDetails}
isPeekView={Boolean(peekModule)}
/>
</div> </div>
)} )}
</div> </div>
</Disclosure.Button> </Disclosure.Panel>
<Transition show={open}> </Transition>
<Disclosure.Panel> </div>
<div className="flex flex-col gap-3"> )}
{isStartValid && isEndValid ? ( </Disclosure>
<div className=" h-full w-full pt-4"> </div>
<div className="flex items-start gap-4 py-2 text-xs">
<div className="flex items-center gap-3 text-custom-text-100">
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
</div>
<div className="flex items-center justify-center gap-1">
<span className="h-2.5 w-2.5 rounded-full bg-[#4C8FFF]" />
<span>Current</span>
</div>
</div>
</div>
<div className="relative h-40 w-80">
<ProgressChart
distribution={moduleDetails.distribution.completion_chart}
startDate={moduleDetails.start_date ?? ""}
endDate={moduleDetails.target_date ?? ""}
totalIssues={moduleDetails.total_issues}
/>
</div>
</div>
) : (
""
)}
{moduleDetails.total_issues > 0 && (
<div className="h-full w-full pt-5 border-t border-custom-border-200">
<SidebarProgressStats
distribution={moduleDetails.distribution}
groupedIssues={{
backlog: moduleDetails.backlog_issues,
unstarted: moduleDetails.unstarted_issues,
started: moduleDetails.started_issues,
completed: moduleDetails.completed_issues,
cancelled: moduleDetails.cancelled_issues,
}}
totalIssues={moduleDetails.total_issues}
module={moduleDetails}
isPeekView={Boolean(peekModule)}
/>
</div>
)}
</div>
</Disclosure.Panel>
</Transition>
</div>
)}
</Disclosure>
</div>
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5"> <div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 py-5 px-1.5">
<Disclosure> <Disclosure>
@ -434,46 +447,16 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<span className="font-medium text-custom-text-200">Links</span> <span className="font-medium text-custom-text-200">Links</span>
</div> </div>
<div className="flex items-center gap-2.5"> <div className="flex items-center gap-2.5">
<ChevronDown <ChevronDown className={`h-3.5 w-3.5 ${open ? "rotate-180 transform" : ""}`} aria-hidden="true" />
className={`h-3.5 w-3.5 ${open ? "rotate-180 transform" : ""}`} </div>
aria-hidden="true" </Disclosure.Button>
/> <Transition show={open}>
</div> <Disclosure.Panel>
</Disclosure.Button> <div className="flex flex-col w-full mt-2 space-y-3 h-72 overflow-y-auto">
<Transition show={open}> {userRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
<Disclosure.Panel> <>
<div className="flex flex-col w-full mt-2 space-y-3 h-72 overflow-y-auto"> <div className="flex items-center justify-end w-full">
{userRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
<>
<div className="flex items-center justify-end w-full">
<button
className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100"
onClick={() => setModuleLinkModal(true)}
>
<Plus className="h-3 w-3" />
Add link
</button>
</div>
<LinksList
links={moduleDetails.link_module}
handleEditLink={handleEditLink}
handleDeleteLink={handleDeleteLink}
userAuth={{
isGuest: userRole === 5,
isViewer: userRole === 10,
isMember: userRole === 15,
isOwner: userRole === 20,
}}
/>
</>
) : (
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Info className="h-3.5 w-3.5 text-custom-text-300 stroke-[1.5]" />
<span className="text-xs text-custom-text-300 p-0.5">No links added yet</span>
</div>
<button <button
className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100" className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100"
onClick={() => setModuleLinkModal(true)} onClick={() => setModuleLinkModal(true)}
@ -482,29 +465,43 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
Add link Add link
</button> </button>
</div> </div>
)}
</div> <LinksList
</Disclosure.Panel> links={moduleDetails.link_module}
</Transition> handleEditLink={handleEditLink}
</div> handleDeleteLink={handleDeleteLink}
)} userAuth={{
</Disclosure> isGuest: userRole === 5,
</div> isViewer: userRole === 10,
isMember: userRole === 15,
isOwner: userRole === 20,
}}
/>
</>
) : (
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Info className="h-3.5 w-3.5 text-custom-text-300 stroke-[1.5]" />
<span className="text-xs text-custom-text-300 p-0.5">No links added yet</span>
</div>
<button
className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100"
onClick={() => setModuleLinkModal(true)}
>
<Plus className="h-3 w-3" />
Add link
</button>
</div>
)}
</div>
</Disclosure.Panel>
</Transition>
</div>
)}
</Disclosure>
</div> </div>
</> </div>
) : ( </>
<Loader className="px-5">
<div className="space-y-2">
<Loader.Item height="15px" width="50%" />
<Loader.Item height="15px" width="30%" />
</div>
<div className="mt-8 space-y-3">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</div>
</Loader>
)}
</> </>
); );
}); });