mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
change module and cycle types
This commit is contained in:
parent
199d50da0f
commit
701500df9f
8
packages/types/src/cycles.d.ts
vendored
8
packages/types/src/cycles.d.ts
vendored
@ -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 = {
|
||||
|
13
packages/types/src/modules.d.ts
vendored
13
packages/types/src/modules.d.ts
vendored
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
|
@ -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)[]) => {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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[]) =>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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() ?? ""}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user