change module and cycle types

This commit is contained in:
rahulramesha 2024-02-19 18:00:07 +05:30
parent 199d50da0f
commit 701500df9f
21 changed files with 132 additions and 111 deletions

View File

@ -32,8 +32,7 @@ export interface ICycle {
name: string;
owned_by: string;
progress_snapshot: TProgressSnapshot;
project: string;
project_detail: IProjectLite;
project_id: string;
status: TCycleGroups;
sort_order: number;
start_date: string | null;
@ -42,12 +41,11 @@ export interface ICycle {
unstarted_issues: number;
updated_at: Date;
updated_by: string;
assignees: IUserLite[];
assignee_ids: string[];
view_props: {
filters: IIssueFilterOptions;
};
workspace: string;
workspace_detail: IWorkspaceLite;
workspace_id: string;
}
export type TProgressSnapshot = {

View File

@ -27,16 +27,12 @@ export interface IModule {
labels: TLabelsDistribution[];
};
id: string;
lead: string | null;
lead_detail: IUserLite | null;
lead_id: string | null;
link_module: ILinkDetails[];
links_list: ModuleLink[];
members: string[];
members_detail: IUserLite[];
member_ids: string[];
is_favorite: boolean;
name: string;
project: string;
project_detail: IProjectLite;
project_id: string;
sort_order: number;
start_date: string | null;
started_issues: number;
@ -49,8 +45,7 @@ export interface IModule {
view_props: {
filters: IIssueFilterOptions;
};
workspace: string;
workspace_detail: IWorkspaceLite;
workspace_id: string;
}
export interface ModuleIssueResponse {

View File

@ -21,6 +21,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
const projectDetails = projectId ? getProjectById(projectId.toString()) : undefined;
const cycleOwnerDetails = cycleDetails ? getUserDetails(cycleDetails.owned_by) : undefined;
const moduleLeadDetails = moduleDetails && moduleDetails.lead_id ? getUserDetails(moduleDetails.lead_id) : undefined;
return (
<>
@ -57,7 +58,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
<div className="mt-4 space-y-4">
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Lead</h6>
<span>{moduleDetails.lead_detail?.display_name}</span>
{moduleLeadDetails && <span>{moduleLeadDetails?.display_name}</span>}
</div>
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Start Date</h6>

View File

@ -5,7 +5,7 @@ import { mutate } from "swr";
// services
import { AnalyticsService } from "services/analytics.service";
// hooks
import { useCycle, useModule, useProject, useUser } from "hooks/store";
import { useCycle, useModule, useProject, useUser, useWorkspace } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CustomAnalyticsSidebarHeader, CustomAnalyticsSidebarProjectsList } from "components/analytics";
@ -39,6 +39,8 @@ export const CustomAnalyticsSidebar: React.FC<Props> = observer((props) => {
// store hooks
const { currentUser } = useUser();
const { workspaceProjectIds, getProjectById } = useProject();
const { getWorkspaceById } = useWorkspace();
const { fetchCycleDetails, getCycleById } = useCycle();
const { fetchModuleDetails, getModuleById } = useModule();
@ -70,11 +72,14 @@ export const CustomAnalyticsSidebar: React.FC<Props> = observer((props) => {
if (cycleDetails || moduleDetails) {
const details = cycleDetails || moduleDetails;
eventPayload.workspaceId = details?.workspace_detail?.id;
eventPayload.workspaceName = details?.workspace_detail?.name;
eventPayload.projectId = details?.project_detail.id;
eventPayload.projectIdentifier = details?.project_detail.identifier;
eventPayload.projectName = details?.project_detail.name;
const currentProjectDetails = getProjectById(details?.project_id || "");
const currentWorkspaceDetails = getWorkspaceById(details?.workspace_id || "");
eventPayload.workspaceId = details?.workspace_id;
eventPayload.workspaceName = currentWorkspaceDetails?.name;
eventPayload.projectId = details?.project_id;
eventPayload.projectIdentifier = currentProjectDetails?.identifier;
eventPayload.projectName = currentProjectDetails?.name;
}
if (cycleDetails) {
@ -138,14 +143,18 @@ export const CustomAnalyticsSidebar: React.FC<Props> = observer((props) => {
const selectedProjects = params.project && params.project.length > 0 ? params.project : workspaceProjectIds;
return (
<div className={cn("relative h-full flex w-full gap-2 justify-between items-start px-5 py-4 bg-custom-sidebar-background-100", !isProjectLevel ? "flex-col" : "")}
<div
className={cn(
"relative h-full flex w-full gap-2 justify-between items-start px-5 py-4 bg-custom-sidebar-background-100",
!isProjectLevel ? "flex-col" : ""
)}
>
<div className="flex flex-wrap items-center gap-2">
<div className="flex items-center gap-1 rounded-md bg-custom-background-80 px-3 py-1 text-xs text-custom-text-200">
<LayersIcon height={14} width={14} />
{analytics ? analytics.total : "..."} <div className={cn(isProjectLevel ? "hidden md:block" : "")}>Issues</div>
{analytics ? analytics.total : "..."}
<div className={cn(isProjectLevel ? "hidden md:block" : "")}>Issues</div>
</div>
{isProjectLevel && (
<div className="flex items-center gap-1 rounded-md bg-custom-background-80 px-3 py-1 text-xs text-custom-text-200">

View File

@ -222,12 +222,13 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
<span className="text-custom-text-200">{cycleOwnerDetails?.display_name}</span>
</div>
{activeCycle.assignees.length > 0 && (
{activeCycle.assignee_ids.length > 0 && (
<div className="flex items-center gap-1 text-custom-text-200">
<AvatarGroup>
{activeCycle.assignees.map((assignee) => (
<Avatar key={assignee.id} name={assignee.display_name} src={assignee.avatar} />
))}
{activeCycle.assignee_ids.map((assigne_id) => {
const member = getUserDetails(assigne_id);
return <Avatar key={member?.id} name={member?.display_name} src={member?.avatar} />;
})}
</AvatarGroup>
</div>
)}

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
import Link from "next/link";
import { observer } from "mobx-react";
// hooks
import { useEventTracker, useCycle, useUser } from "hooks/store";
import { useEventTracker, useCycle, useUser, useMember } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles";
@ -40,6 +40,7 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
membership: { currentProjectRole },
} = useUser();
const { addCycleToFavorites, removeCycleFromFavorites, getCycleById } = useCycle();
const { getUserDetails } = useMember();
// toast alert
const { setToastAlert } = useToast();
// computed
@ -212,13 +213,14 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
<LayersIcon className="h-4 w-4 text-custom-text-300" />
<span className="text-xs text-custom-text-300">{issueCount}</span>
</div>
{cycleDetails.assignees.length > 0 && (
<Tooltip tooltipContent={`${cycleDetails.assignees.length} Members`}>
{cycleDetails.assignee_ids.length > 0 && (
<Tooltip tooltipContent={`${cycleDetails.assignee_ids.length} Members`}>
<div className="flex cursor-default items-center gap-1">
<AvatarGroup showTooltip={false}>
{cycleDetails.assignees.map((assignee) => (
<Avatar key={assignee.id} name={assignee.display_name} src={assignee.avatar} />
))}
{cycleDetails.assignee_ids.map((assigne_id) => {
const member = getUserDetails(assigne_id);
return <Avatar key={member?.id} name={member?.display_name} src={member?.avatar} />;
})}
</AvatarGroup>
</div>
</Tooltip>

View File

@ -3,7 +3,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react";
// hooks
import { useEventTracker, useCycle, useUser } from "hooks/store";
import { useEventTracker, useCycle, useUser, useMember } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles";
@ -44,6 +44,7 @@ export const CyclesListItem: FC<TCyclesListItem> = observer((props) => {
membership: { currentProjectRole },
} = useUser();
const { getCycleById, addCycleToFavorites, removeCycleFromFavorites } = useCycle();
const { getUserDetails } = useMember();
// toast alert
const { setToastAlert } = useToast();
@ -230,13 +231,14 @@ export const CyclesListItem: FC<TCyclesListItem> = observer((props) => {
</div>
<div className="relative flex flex-shrink-0 items-center gap-3">
<Tooltip tooltipContent={`${cycleDetails.assignees.length} Members`}>
<Tooltip tooltipContent={`${cycleDetails.assignee_ids?.length} Members`}>
<div className="flex w-10 cursor-default items-center justify-center">
{cycleDetails.assignees.length > 0 ? (
{cycleDetails.assignee_ids?.length > 0 ? (
<AvatarGroup showTooltip={false}>
{cycleDetails.assignees.map((assignee) => (
<Avatar key={assignee.id} name={assignee.display_name} src={assignee.avatar} />
))}
{cycleDetails.assignee_ids?.map((assigne_id) => {
const member = getUserDetails(assigne_id);
return <Avatar key={member?.id} name={member?.display_name} src={member?.avatar} />;
})}
</AvatarGroup>
) : (
<span className="flex h-5 w-5 items-end justify-center rounded-full border border-dashed border-custom-text-400 bg-custom-background-80">

View File

@ -36,7 +36,7 @@ export const CycleForm: React.FC<Props> = (props) => {
reset,
} = useForm<ICycle>({
defaultValues: {
project: projectId,
project_id: projectId,
name: data?.name || "",
description: data?.description || "",
start_date: data?.start_date || null,
@ -61,13 +61,13 @@ export const CycleForm: React.FC<Props> = (props) => {
maxDate?.setDate(maxDate.getDate() - 1);
return (
<form onSubmit={handleSubmit((formData)=>handleFormSubmit(formData,dirtyFields))}>
<form onSubmit={handleSubmit((formData) => handleFormSubmit(formData, dirtyFields))}>
<div className="space-y-5">
<div className="flex items-center gap-x-3">
{!status && (
<Controller
control={control}
name="project"
name="project_id"
render={({ field: { value, onChange } }) => (
<ProjectDropdown
value={value}

View File

@ -40,7 +40,7 @@ export const CycleGanttBlock: React.FC<Props> = observer((props) => {
? "rgb(var(--color-text-200))"
: "",
}}
onClick={() => router.push(`/${workspaceSlug}/projects/${cycleDetails?.project}/cycles/${cycleDetails?.id}`)}
onClick={() => router.push(`/${workspaceSlug}/projects/${cycleDetails?.project_id}/cycles/${cycleDetails?.id}`)}
>
<div className="absolute left-0 top-0 h-full w-full bg-custom-background-100/50" />
<Tooltip
@ -78,7 +78,7 @@ export const CycleGanttSidebarBlock: React.FC<Props> = observer((props) => {
return (
<div
className="relative flex h-full w-full items-center gap-2"
onClick={() => router.push(`/${workspaceSlug}/projects/${cycleDetails?.project}/cycles/${cycleDetails?.id}`)}
onClick={() => router.push(`/${workspaceSlug}/projects/${cycleDetails?.project_id}/cycles/${cycleDetails?.id}`)}
>
<ContrastIcon
className="h-5 w-5 flex-shrink-0"

View File

@ -33,7 +33,7 @@ export const CyclesListGanttChartView: FC<Props> = observer((props) => {
const payload: any = { ...data };
if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
await updateCycleDetails(workspaceSlug.toString(), cycle.project, cycle.id, payload);
await updateCycleDetails(workspaceSlug.toString(), cycle.project_id, cycle.id, payload);
};
const blockFormat = (blocks: (ICycle | null)[]) => {

View File

@ -40,7 +40,7 @@ export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
const handleCreateCycle = async (payload: Partial<ICycle>) => {
if (!workspaceSlug || !projectId) return;
const selectedProjectId = payload.project ?? projectId.toString();
const selectedProjectId = payload.project_id ?? projectId.toString();
await createCycle(workspaceSlug, selectedProjectId, payload)
.then((res) => {
setToastAlert({
@ -69,7 +69,7 @@ export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
const handleUpdateCycle = async (cycleId: string, payload: Partial<ICycle>, dirtyFields: any) => {
if (!workspaceSlug || !projectId) return;
const selectedProjectId = payload.project ?? projectId.toString();
const selectedProjectId = payload.project_id ?? projectId.toString();
await updateCycleDetails(workspaceSlug, selectedProjectId, cycleId, payload)
.then((res) => {
const changed_properties = Object.keys(dirtyFields);
@ -155,8 +155,8 @@ export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
// if data is present, set active project to the project of the
// issue. This has more priority than the project in the url.
if (data && data.project) {
setActiveProject(data.project);
if (data && data.project_id) {
setActiveProject(data.project_id);
return;
}

View File

@ -45,7 +45,7 @@ export const DeleteModuleModal: React.FC<Props> = observer((props) => {
await deleteModule(workspaceSlug.toString(), projectId.toString(), data.id)
.then(() => {
if (moduleId || peekModule) router.push(`/${workspaceSlug}/projects/${data.project}/modules`);
if (moduleId || peekModule) router.push(`/${workspaceSlug}/projects/${data.project_id}/modules`);
handleClose();
setToastAlert({
type: "success",

View File

@ -23,8 +23,8 @@ const defaultValues: Partial<IModule> = {
name: "",
description: "",
status: "backlog",
lead: null,
members: [],
lead_id: null,
member_ids: [],
};
export const ModuleForm: React.FC<Props> = ({
@ -43,12 +43,12 @@ export const ModuleForm: React.FC<Props> = ({
reset,
} = useForm<IModule>({
defaultValues: {
project: projectId,
project_id: projectId,
name: data?.name || "",
description: data?.description || "",
status: data?.status || "backlog",
lead: data?.lead || null,
members: data?.members || [],
lead_id: data?.lead_id || null,
member_ids: data?.member_ids || [],
},
});
@ -83,7 +83,7 @@ export const ModuleForm: React.FC<Props> = ({
{!status && (
<Controller
control={control}
name="project"
name="project_id"
render={({ field: { value, onChange } }) => (
<div className="h-7">
<ProjectDropdown
@ -184,7 +184,7 @@ export const ModuleForm: React.FC<Props> = ({
<ModuleStatusSelect control={control} error={errors.status} tabIndex={5} />
<Controller
control={control}
name="lead"
name="lead_id"
render={({ field: { value, onChange } }) => (
<div className="h-7">
<ProjectMemberDropdown
@ -201,7 +201,7 @@ export const ModuleForm: React.FC<Props> = ({
/>
<Controller
control={control}
name="members"
name="member_ids"
render={({ field: { value, onChange } }) => (
<div className="h-7">
<ProjectMemberDropdown

View File

@ -29,7 +29,9 @@ export const ModuleGanttBlock: React.FC<Props> = observer((props) => {
<div
className="relative flex h-full w-full items-center rounded"
style={{ backgroundColor: MODULE_STATUS.find((s) => s.value === moduleDetails?.status)?.color }}
onClick={() => router.push(`/${workspaceSlug}/projects/${moduleDetails?.project}/modules/${moduleDetails?.id}`)}
onClick={() =>
router.push(`/${workspaceSlug}/projects/${moduleDetails?.project_id}/modules/${moduleDetails?.id}`)
}
>
<div className="absolute left-0 top-0 h-full w-full bg-custom-background-100/50" />
<Tooltip
@ -65,7 +67,9 @@ export const ModuleGanttSidebarBlock: React.FC<Props> = observer((props) => {
return (
<div
className="relative flex h-full w-full items-center gap-2"
onClick={() => router.push(`/${workspaceSlug}/projects/${moduleDetails?.project}/modules/${moduleDetails?.id}`)}
onClick={() =>
router.push(`/${workspaceSlug}/projects/${moduleDetails?.project_id}/modules/${moduleDetails?.id}`)
}
>
<ModuleStatusIcon status={moduleDetails?.status ?? "backlog"} height="16px" width="16px" />
<h6 className="flex-grow truncate text-sm font-medium">{moduleDetails?.name}</h6>

View File

@ -22,7 +22,7 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
const payload: any = { ...data };
if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
await updateModuleDetails(workspaceSlug.toString(), module.project, module.id, payload);
await updateModuleDetails(workspaceSlug.toString(), module.project_id, module.id, payload);
};
const blockFormat = (blocks: string[]) =>

View File

@ -24,8 +24,8 @@ const defaultValues: Partial<IModule> = {
name: "",
description: "",
status: "backlog",
lead: null,
members: [],
lead_id: null,
member_ids: [],
};
export const CreateUpdateModuleModal: React.FC<Props> = observer((props) => {
@ -51,7 +51,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = observer((props) => {
const handleCreateModule = async (payload: Partial<IModule>) => {
if (!workspaceSlug || !projectId) return;
const selectedProjectId = payload.project ?? projectId.toString();
const selectedProjectId = payload.project_id ?? projectId.toString();
await createModule(workspaceSlug.toString(), selectedProjectId, payload)
.then((res) => {
handleClose();
@ -81,7 +81,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = observer((props) => {
const handleUpdateModule = async (payload: Partial<IModule>, dirtyFields: any) => {
if (!workspaceSlug || !projectId || !data) return;
const selectedProjectId = payload.project ?? projectId.toString();
const selectedProjectId = payload.project_id ?? projectId.toString();
await updateModuleDetails(workspaceSlug.toString(), selectedProjectId, data.id, payload)
.then((res) => {
handleClose();
@ -129,8 +129,8 @@ export const CreateUpdateModuleModal: React.FC<Props> = observer((props) => {
// if data is present, set active project to the project of the
// issue. This has more priority than the project in the url.
if (data && data.project) {
setActiveProject(data.project);
if (data && data.project_id) {
setActiveProject(data.project_id);
return;
}

View File

@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react";
// hooks
import { useEventTracker, useModule, useUser } from "hooks/store";
import { useEventTracker, useMember, useModule, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules";
@ -37,6 +37,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
membership: { currentProjectRole },
} = useUser();
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites } = useModule();
const { getUserDetails } = useMember();
const { setTrackElement, captureEvent } = useEventTracker();
// derived values
const moduleDetails = getModuleById(moduleId);
@ -163,7 +164,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
/>
)}
<DeleteModuleModal data={moduleDetails} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
<Link href={`/${workspaceSlug}/projects/${moduleDetails.project}/modules/${moduleDetails.id}`}>
<Link href={`/${workspaceSlug}/projects/${moduleDetails.project_id}/modules/${moduleDetails.id}`}>
<div className="flex h-44 w-full flex-col justify-between rounded border border-custom-border-100 bg-custom-background-100 p-4 text-sm hover:shadow-md">
<div>
<div className="flex items-center justify-between gap-2">
@ -195,13 +196,14 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
<LayersIcon className="h-4 w-4 text-custom-text-300" />
<span className="text-xs text-custom-text-300">{issueCount ?? "0 Issue"}</span>
</div>
{moduleDetails.members_detail.length > 0 && (
<Tooltip tooltipContent={`${moduleDetails.members_detail.length} Members`}>
{moduleDetails.member_ids?.length > 0 && (
<Tooltip tooltipContent={`${moduleDetails.member_ids.length} Members`}>
<div className="flex cursor-default items-center gap-1">
<AvatarGroup showTooltip={false}>
{moduleDetails.members_detail.map((member) => (
<Avatar key={member.id} name={member.display_name} src={member.avatar} />
))}
{moduleDetails.member_ids.map((member_id) => {
const member = getUserDetails(member_id);
return <Avatar key={member?.id} name={member?.display_name} src={member?.avatar} />;
})}
</AvatarGroup>
</div>
</Tooltip>

View File

@ -4,7 +4,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react";
// hooks
import { useModule, useUser, useEventTracker } from "hooks/store";
import { useModule, useUser, useEventTracker, useMember } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules";
@ -37,6 +37,7 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
membership: { currentProjectRole },
} = useUser();
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites } = useModule();
const { getUserDetails } = useMember();
const { setTrackElement, captureEvent } = useEventTracker();
// derived values
const moduleDetails = getModuleById(moduleId);
@ -153,7 +154,7 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
/>
)}
<DeleteModuleModal data={moduleDetails} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
<Link href={`/${workspaceSlug}/projects/${moduleDetails.project}/modules/${moduleDetails.id}`}>
<Link href={`/${workspaceSlug}/projects/${moduleDetails.project_id}/modules/${moduleDetails.id}`}>
<div className="group flex w-full items-center justify-between gap-5 border-b border-custom-border-100 bg-custom-background-100 flex-col sm:flex-row px-5 py-6 text-sm hover:bg-custom-background-90">
<div className="relative flex w-full items-center gap-3 justify-between overflow-hidden">
<div className="relative w-full flex items-center gap-3 overflow-hidden">
@ -206,13 +207,14 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
</div>
<div className="flex-shrink-0 relative flex items-center gap-3">
<Tooltip tooltipContent={`${moduleDetails.members_detail.length} Members`}>
<Tooltip tooltipContent={`${moduleDetails.member_ids.length} Members`}>
<div className="flex w-10 cursor-default items-center justify-center gap-1">
{moduleDetails.members_detail.length > 0 ? (
{moduleDetails.member_ids.length > 0 ? (
<AvatarGroup showTooltip={false}>
{moduleDetails.members_detail.map((member) => (
<Avatar key={member.id} name={member.display_name} src={member.avatar} />
))}
{moduleDetails.member_ids.map((member_id) => {
const member = getUserDetails(member_id);
return <Avatar key={member?.id} name={member?.display_name} src={member?.avatar} />;
})}
</AvatarGroup>
) : (
<span className="flex h-5 w-5 items-end justify-center rounded-full border border-dashed border-custom-text-400 bg-custom-background-80">

View File

@ -37,8 +37,8 @@ import { EUserProjectRoles } from "constants/project";
import { MODULE_LINK_CREATED, MODULE_LINK_DELETED, MODULE_LINK_UPDATED, MODULE_UPDATED } from "constants/event-tracker";
const defaultValues: Partial<IModule> = {
lead: "",
members: [],
lead_id: "",
member_ids: [],
start_date: null,
target_date: null,
status: "backlog",
@ -323,7 +323,8 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<CustomSelect
customButton={
<span
className={`flex h-6 w-20 items-center justify-center rounded-sm text-center text-xs ${isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
className={`flex h-6 w-20 items-center justify-center rounded-sm text-center text-xs ${
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
}`}
style={{
color: moduleStatus ? moduleStatus.color : "#a3a3a2",
@ -373,12 +374,14 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<>
<Popover.Button
ref={startDateButtonRef}
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
}`}
disabled={!isEditingAllowed}
>
<span
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${watch("start_date") ? "" : "text-custom-text-400"
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
watch("start_date") ? "" : "text-custom-text-400"
}`}
>
{renderFormattedDate(startDate) ?? "No date selected"}
@ -427,12 +430,14 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<>
<Popover.Button
ref={endDateButtonRef}
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
}`}
disabled={!isEditingAllowed}
>
<span
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${watch("target_date") ? "" : "text-custom-text-400"
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
watch("target_date") ? "" : "text-custom-text-400"
}`}
>
{renderFormattedDate(endDate) ?? "No date selected"}
@ -485,13 +490,13 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
</div>
<Controller
control={control}
name="lead"
name="lead_id"
render={({ field: { value } }) => (
<div className="w-1/2">
<ProjectMemberDropdown
value={value ?? null}
onChange={(val) => {
submitChanges({ lead: val });
submitChanges({ lead_id: val });
}}
projectId={projectId?.toString() ?? ""}
multiple={false}
@ -509,13 +514,13 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
</div>
<Controller
control={control}
name="members"
name="member_ids"
render={({ field: { value } }) => (
<div className="w-1/2">
<ProjectMemberDropdown
value={value ?? []}
onChange={(val: string[]) => {
submitChanges({ members: val });
submitChanges({ member_ids: val });
}}
multiple
projectId={projectId?.toString() ?? ""}

View File

@ -102,7 +102,7 @@ export class CycleStore implements ICycleStore {
get currentProjectCycleIds() {
const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.fetchedMap[projectId]) return null;
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId);
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project_id === projectId);
allCycles = sortBy(allCycles, [(c) => c.sort_order]);
const allCycleIds = allCycles.map((c) => c.id);
return allCycleIds;
@ -116,7 +116,7 @@ export class CycleStore implements ICycleStore {
if (!projectId || !this.fetchedMap[projectId]) return null;
let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && hasEndDatePassed;
return c.project_id === projectId && hasEndDatePassed;
});
completedCycles = sortBy(completedCycles, [(c) => c.sort_order]);
const completedCycleIds = completedCycles.map((c) => c.id);
@ -131,7 +131,7 @@ export class CycleStore implements ICycleStore {
if (!projectId || !this.fetchedMap[projectId]) return null;
let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const isStartDateUpcoming = isFuture(new Date(c.start_date ?? ""));
return c.project === projectId && isStartDateUpcoming;
return c.project_id === projectId && isStartDateUpcoming;
});
upcomingCycles = sortBy(upcomingCycles, [(c) => c.sort_order]);
const upcomingCycleIds = upcomingCycles.map((c) => c.id);
@ -146,7 +146,7 @@ export class CycleStore implements ICycleStore {
if (!projectId || !this.fetchedMap[projectId]) return null;
let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && !hasEndDatePassed;
return c.project_id === projectId && !hasEndDatePassed;
});
incompleteCycles = sortBy(incompleteCycles, [(c) => c.sort_order]);
const incompleteCycleIds = incompleteCycles.map((c) => c.id);
@ -160,7 +160,7 @@ export class CycleStore implements ICycleStore {
const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.fetchedMap[projectId]) return null;
let draftCycles = Object.values(this.cycleMap ?? {}).filter(
(c) => c.project === projectId && !c.start_date && !c.end_date
(c) => c.project_id === projectId && !c.start_date && !c.end_date
);
draftCycles = sortBy(draftCycles, [(c) => c.sort_order]);
const draftCycleIds = draftCycles.map((c) => c.id);
@ -174,7 +174,7 @@ export class CycleStore implements ICycleStore {
const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null;
const activeCycle = Object.keys(this.activeCycleIdMap ?? {}).find(
(cycleId) => this.cycleMap?.[cycleId]?.project === projectId
(cycleId) => this.cycleMap?.[cycleId]?.project_id === projectId
);
return activeCycle || null;
}
@ -202,7 +202,7 @@ export class CycleStore implements ICycleStore {
getProjectCycleIds = computedFn((projectId: string): string[] | null => {
if (!this.fetchedMap[projectId]) return null;
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project_id === projectId);
cycles = sortBy(cycles, [(c) => c.sort_order]);
const cycleIds = cycles.map((c) => c.id);
return cycleIds || null;

View File

@ -99,7 +99,7 @@ export class ModulesStore implements IModuleStore {
get projectModuleIds() {
const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId);
projectModules = sortBy(projectModules, [(m) => m.sort_order]);
const projectModuleIds = projectModules.map((m) => m.id);
return projectModuleIds || null;
@ -119,7 +119,7 @@ export class ModulesStore implements IModuleStore {
getProjectModuleIds = computedFn((projectId: string) => {
if (!this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId);
projectModules = sortBy(projectModules, [(m) => m.sort_order]);
const projectModuleIds = projectModules.map((m) => m.id);
return projectModuleIds;