forked from github/plane
chore: removed unused hooks and components (#2611)
* chore: remove unused hooks * chore: removed useProjectMembers hook * chore: removed issue hooks * fix: build errors
This commit is contained in:
parent
c987c6f308
commit
d5fd69354e
@ -1,12 +1,13 @@
|
||||
import React, { FC, Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { Command } from "cmdk";
|
||||
import { THEME_OPTIONS } from "constants/themes";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Settings } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// constants
|
||||
import { THEME_OPTIONS } from "constants/themes";
|
||||
|
||||
type Props = {
|
||||
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { Dispatch, SetStateAction, useCallback, FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { mutate } from "swr";
|
||||
import { Command } from "cmdk";
|
||||
import { Check } from "lucide-react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
// hooks
|
||||
import useProjectMembers from "hooks/use-project-members";
|
||||
// constants
|
||||
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||
// ui
|
||||
import { Avatar } from "@plane/ui";
|
||||
// icons
|
||||
import { Check } from "lucide-react";
|
||||
// types
|
||||
import { IUser, IIssue } from "types";
|
||||
// constants
|
||||
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
||||
@ -24,11 +24,14 @@ type Props = {
|
||||
// services
|
||||
const issueService = new IssueService();
|
||||
|
||||
export const ChangeIssueAssignee: FC<Props> = ({ setIsPaletteOpen, issue, user }) => {
|
||||
export const ChangeIssueAssignee: FC<Props> = observer((props) => {
|
||||
const { setIsPaletteOpen, issue, user } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { members } = useProjectMembers(workspaceSlug as string, projectId as string);
|
||||
const { project: projectStore } = useMobxStore();
|
||||
const members = projectId ? projectStore.members?.[projectId.toString()] : undefined;
|
||||
|
||||
const options =
|
||||
members?.map(({ member }) => ({
|
||||
@ -104,4 +107,4 @@ export const ChangeIssueAssignee: FC<Props> = ({ setIsPaletteOpen, issue, user }
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -89,6 +89,7 @@ const LabelPill = ({ labelId }: { labelId: string }) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const EstimatePoint = ({ point }: { point: string }) => {
|
||||
const { estimateValue, isEstimateActive } = useEstimateOption(Number(point));
|
||||
const currentPoint = Number(point) + 1;
|
||||
|
@ -1,349 +0,0 @@
|
||||
import React from "react";
|
||||
import { X } from "lucide-react";
|
||||
// ui
|
||||
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IIssueFilterOptions, IIssueLabels, IState, IUserLite, TStateGroups } from "types";
|
||||
// constants
|
||||
import { STATE_GROUP_COLORS } from "constants/state";
|
||||
|
||||
type Props = {
|
||||
filters: Partial<IIssueFilterOptions>;
|
||||
setFilters: (updatedFilter: Partial<IIssueFilterOptions>) => void;
|
||||
clearAllFilters: (...args: any) => void;
|
||||
labels: IIssueLabels[] | undefined;
|
||||
members: IUserLite[] | undefined;
|
||||
states: IState[] | undefined;
|
||||
};
|
||||
|
||||
export const FiltersList: React.FC<Props> = ({ filters, setFilters, clearAllFilters, labels, members, states }) => {
|
||||
if (!filters) return <></>;
|
||||
|
||||
const nullFilters = Object.keys(filters).filter((key) => filters[key as keyof IIssueFilterOptions] === null);
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-wrap items-center gap-2 text-xs">
|
||||
{Object.keys(filters).map((filterKey) => {
|
||||
const key = filterKey as keyof typeof filters;
|
||||
|
||||
if (filters[key] === null) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
className="flex items-center gap-x-2 rounded-full border border-custom-border-200 bg-custom-background-80 px-2 py-1"
|
||||
>
|
||||
<span className="capitalize text-custom-text-200">
|
||||
{key === "target_date" ? "Due Date" : replaceUnderscoreIfSnakeCase(key)}:
|
||||
</span>
|
||||
{filters[key] === null || (filters[key]?.length ?? 0) <= 0 ? (
|
||||
<span className="inline-flex items-center px-2 py-0.5 font-medium">None</span>
|
||||
) : Array.isArray(filters[key]) ? (
|
||||
<div className="space-x-2">
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
{key === "state"
|
||||
? filters.state?.map((stateId: string) => {
|
||||
const state = states?.find((s) => s.id === stateId);
|
||||
|
||||
return (
|
||||
<p
|
||||
key={state?.id}
|
||||
className="inline-flex items-center gap-x-1 rounded-full px-2 py-0.5 font-medium"
|
||||
style={{
|
||||
color: state?.color,
|
||||
backgroundColor: `${state?.color}20`,
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} />
|
||||
</span>
|
||||
<span>{state?.name ?? ""}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
state: filters.state?.filter((s: any) => s !== stateId),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
})
|
||||
: key === "state_group"
|
||||
? filters.state_group?.map((stateGroup) => {
|
||||
const group = stateGroup as TStateGroups;
|
||||
|
||||
return (
|
||||
<p
|
||||
key={group}
|
||||
className="inline-flex items-center gap-x-1 rounded-full px-2 py-0.5 capitalize"
|
||||
style={{
|
||||
color: STATE_GROUP_COLORS[group],
|
||||
backgroundColor: `${STATE_GROUP_COLORS[group]}20`,
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<StateGroupIcon stateGroup={group} color={undefined} />
|
||||
</span>
|
||||
<span>{group}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
state_group: filters.state_group?.filter((g) => g !== group),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
})
|
||||
: key === "priority"
|
||||
? filters.priority?.map((priority: any) => (
|
||||
<p
|
||||
key={priority}
|
||||
className={`inline-flex items-center gap-x-1 rounded-full px-2 py-0.5 capitalize ${
|
||||
priority === "urgent"
|
||||
? "bg-red-500/20 text-red-500"
|
||||
: priority === "high"
|
||||
? "bg-orange-500/20 text-orange-500"
|
||||
: priority === "medium"
|
||||
? "bg-yellow-500/20 text-yellow-500"
|
||||
: priority === "low"
|
||||
? "bg-green-500/20 text-green-500"
|
||||
: "bg-custom-background-90 text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
<span>
|
||||
<PriorityIcon priority={priority} className="h-3 w-3" />
|
||||
</span>
|
||||
<span>{priority === "null" ? "None" : priority}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
priority: filters.priority?.filter((p: any) => p !== priority),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</p>
|
||||
))
|
||||
: key === "mentions"
|
||||
? filters.mentions?.map((mentionId: string) => {
|
||||
const member = members?.find((m) => m.id === mentionId);
|
||||
return (
|
||||
<div
|
||||
key={mentionId}
|
||||
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1"
|
||||
>
|
||||
<Avatar name={member?.display_name} src={member?.avatar} showTooltip={false} />
|
||||
<span>{member?.display_name}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
mentions: filters.mentions?.filter((p: any) => p !== mentionId),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: key === "assignees"
|
||||
? filters.assignees?.map((memberId: string) => {
|
||||
const member = members?.find((m) => m.id === memberId);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={memberId}
|
||||
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1"
|
||||
>
|
||||
<Avatar name={member?.display_name} src={member?.avatar} showTooltip={false} />
|
||||
<span>{member?.display_name}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
subscriber: filters.subscriber?.filter((p: any) => p !== memberId),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: key === "created_by"
|
||||
? filters.created_by?.map((memberId: string) => {
|
||||
const member = members?.find((m) => m.id === memberId);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${memberId}-${key}`}
|
||||
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1 capitalize"
|
||||
>
|
||||
<Avatar name={member?.display_name} src={member?.avatar} showTooltip={false} />
|
||||
<span>{member?.display_name}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
created_by: filters.created_by?.filter((p: any) => p !== memberId),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: key === "labels"
|
||||
? filters.labels?.map((labelId: string) => {
|
||||
const label = labels?.find((l) => l.id === labelId);
|
||||
|
||||
if (!label) return null;
|
||||
const color = label.color !== "" ? label.color : "#0f172a";
|
||||
return (
|
||||
<div
|
||||
className="inline-flex items-center gap-x-1 rounded-full px-2 py-0.5"
|
||||
style={{
|
||||
color: color,
|
||||
backgroundColor: `${color}20`, // add 20% opacity
|
||||
}}
|
||||
key={labelId}
|
||||
>
|
||||
<div
|
||||
className="h-1.5 w-1.5 rounded-full"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
<span>{label.name}</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
labels: filters.labels?.filter((l: any) => l !== labelId),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X
|
||||
className="h-3 w-3"
|
||||
style={{
|
||||
color: color,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: key === "start_date"
|
||||
? filters.start_date?.map((date: string) => {
|
||||
if (filters.start_date && filters.start_date.length <= 0) return null;
|
||||
|
||||
const splitDate = date.split(";");
|
||||
|
||||
return (
|
||||
<div
|
||||
key={date}
|
||||
className="inline-flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-100 px-1 py-0.5"
|
||||
>
|
||||
<div className="h-1.5 w-1.5 rounded-full" />
|
||||
<span className="capitalize">
|
||||
{splitDate[1]} {renderShortDateWithYearFormat(splitDate[0])}
|
||||
</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
start_date: filters.start_date?.filter((d: any) => d !== date),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: key === "target_date"
|
||||
? filters.target_date?.map((date: string) => {
|
||||
if (filters.target_date && filters.target_date.length <= 0) return null;
|
||||
|
||||
const splitDate = date.split(";");
|
||||
|
||||
return (
|
||||
<div
|
||||
key={date}
|
||||
className="inline-flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-100 px-1 py-0.5"
|
||||
>
|
||||
<div className="h-1.5 w-1.5 rounded-full" />
|
||||
<span className="capitalize">
|
||||
{splitDate[1]} {renderShortDateWithYearFormat(splitDate[0])}
|
||||
</span>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
target_date: filters.target_date?.filter((d: any) => d !== date),
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: (filters[key] as any)?.join(", ")}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
[key]: null,
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-x-1 capitalize">
|
||||
{filters[key as keyof typeof filters]}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setFilters({
|
||||
[key]: null,
|
||||
})
|
||||
}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={clearAllFilters}
|
||||
className="flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-80 px-3 py-1.5 text-xs"
|
||||
>
|
||||
<span>Clear all filters</span>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,3 +1,2 @@
|
||||
export * from "./date-filter-modal";
|
||||
export * from "./date-filter-select";
|
||||
export * from "./filters-list";
|
||||
|
@ -2,7 +2,6 @@ export * from "./filters";
|
||||
export * from "./modals";
|
||||
export * from "./sidebar";
|
||||
export * from "./theme";
|
||||
export * from "./views";
|
||||
export * from "./activity";
|
||||
export * from "./reaction-selector";
|
||||
export * from "./image-picker-popover";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import useSWR from "swr";
|
||||
// react hook form
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
@ -9,7 +9,6 @@ import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
import { IssueService } from "services/issue";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
// ui
|
||||
import { Button, LayersIcon } from "@plane/ui";
|
||||
// icons
|
||||
@ -17,15 +16,7 @@ import { Search } from "lucide-react";
|
||||
// types
|
||||
import { IUser, IIssue } from "types";
|
||||
// fetch keys
|
||||
import {
|
||||
CYCLE_DETAILS,
|
||||
CYCLE_ISSUES_WITH_PARAMS,
|
||||
MODULE_DETAILS,
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
PROJECT_ISSUES_LIST,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
VIEW_ISSUES,
|
||||
} from "constants/fetch-keys";
|
||||
import { PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||
|
||||
type FormInput = {
|
||||
delete_issue_ids: string[];
|
||||
@ -43,7 +34,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, user } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
// fetching project issues.
|
||||
@ -53,9 +44,6 @@ export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
|
||||
);
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
const { displayFilters, params } = useIssuesView();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { order_by, group_by, ...viewGanttParams } = params;
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
@ -89,14 +77,6 @@ export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
|
||||
|
||||
if (!Array.isArray(data.delete_issue_ids)) data.delete_issue_ids = [data.delete_issue_ids];
|
||||
|
||||
const ganttFetchKey = cycleId
|
||||
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString())
|
||||
: moduleId
|
||||
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString())
|
||||
: viewId
|
||||
? VIEW_ISSUES(viewId.toString(), viewGanttParams)
|
||||
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId?.toString() ?? "");
|
||||
|
||||
await issueService
|
||||
.bulkDeleteIssues(
|
||||
workspaceSlug as string,
|
||||
@ -113,17 +93,6 @@ export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
|
||||
message: "Issues deleted successfully!",
|
||||
});
|
||||
|
||||
if (displayFilters.layout === "gantt_chart") mutate(ganttFetchKey);
|
||||
else {
|
||||
if (cycleId) {
|
||||
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params));
|
||||
mutate(CYCLE_DETAILS(cycleId.toString()));
|
||||
} else if (moduleId) {
|
||||
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
|
||||
mutate(MODULE_DETAILS(moduleId as string));
|
||||
} else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params));
|
||||
}
|
||||
|
||||
handleClose();
|
||||
})
|
||||
.catch(() =>
|
||||
|
@ -1,30 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { mutate } from "swr";
|
||||
|
||||
// headless ui
|
||||
import { Combobox, Dialog, Transition } from "@headlessui/react";
|
||||
import { Rocket, Search, X } from "lucide-react";
|
||||
// services
|
||||
import { ProjectService } from "services/project";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useDebounce from "hooks/use-debounce";
|
||||
// ui
|
||||
import { Button, LayersIcon, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
|
||||
// icons
|
||||
import { Rocket, Search, X } from "lucide-react";
|
||||
// types
|
||||
import { ISearchIssueResponse, TProjectIssuesSearchParams } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
CYCLE_DETAILS,
|
||||
CYCLE_ISSUES_WITH_PARAMS,
|
||||
MODULE_DETAILS,
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
@ -53,12 +39,10 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
|
||||
const debouncedSearchTerm: string = useDebounce(searchTerm, 500);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { params } = useIssuesView();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setSearchTerm("");
|
||||
@ -81,16 +65,6 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
|
||||
|
||||
await handleOnSubmit(selectedIssues).finally(() => setIsSubmitting(false));
|
||||
|
||||
if (cycleId) {
|
||||
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
|
||||
mutate(CYCLE_DETAILS(cycleId as string));
|
||||
}
|
||||
|
||||
if (moduleId) {
|
||||
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
|
||||
mutate(MODULE_DETAILS(moduleId as string));
|
||||
}
|
||||
|
||||
handleClose();
|
||||
|
||||
setToastAlert({
|
||||
|
@ -15,6 +15,7 @@ type Props = {
|
||||
|
||||
export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, handleEditLink, userAuth }) => {
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||
|
||||
return (
|
||||
<>
|
||||
{links.map((link) => (
|
||||
|
@ -5,7 +5,6 @@ import Image from "next/image";
|
||||
import { Tab } from "@headlessui/react";
|
||||
// hooks
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
// images
|
||||
import emptyLabel from "public/empty-state/empty_label.svg";
|
||||
import emptyMembers from "public/empty-state/empty_members.svg";
|
||||
@ -47,8 +46,6 @@ export const SidebarProgressStats: React.FC<Props> = ({
|
||||
noBackground,
|
||||
isPeekView = false,
|
||||
}) => {
|
||||
const { filters, setFilters } = useIssuesView();
|
||||
|
||||
const { storedValue: tab, setValue: setTab } = useLocalStorage("tab", "Assignees");
|
||||
|
||||
const currentValue = (tab: string | null) => {
|
||||
@ -145,16 +142,17 @@ export const SidebarProgressStats: React.FC<Props> = ({
|
||||
total={assignee.total_issues}
|
||||
{...(!isPeekView && {
|
||||
onClick: () => {
|
||||
if (filters?.assignees?.includes(assignee.assignee_id ?? ""))
|
||||
setFilters({
|
||||
assignees: filters?.assignees?.filter((a) => a !== assignee.assignee_id),
|
||||
});
|
||||
else
|
||||
setFilters({
|
||||
assignees: [...(filters?.assignees ?? []), assignee.assignee_id ?? ""],
|
||||
});
|
||||
// TODO: set filters here
|
||||
// if (filters?.assignees?.includes(assignee.assignee_id ?? ""))
|
||||
// setFilters({
|
||||
// assignees: filters?.assignees?.filter((a) => a !== assignee.assignee_id),
|
||||
// });
|
||||
// else
|
||||
// setFilters({
|
||||
// assignees: [...(filters?.assignees ?? []), assignee.assignee_id ?? ""],
|
||||
// });
|
||||
},
|
||||
selected: filters?.assignees?.includes(assignee.assignee_id ?? ""),
|
||||
// selected: filters?.assignees?.includes(assignee.assignee_id ?? ""),
|
||||
})}
|
||||
/>
|
||||
);
|
||||
@ -203,14 +201,15 @@ export const SidebarProgressStats: React.FC<Props> = ({
|
||||
completed={label.completed_issues}
|
||||
total={label.total_issues}
|
||||
{...(!isPeekView && {
|
||||
// TODO: set filters here
|
||||
onClick: () => {
|
||||
if (filters.labels?.includes(label.label_id ?? ""))
|
||||
setFilters({
|
||||
labels: filters?.labels?.filter((l) => l !== label.label_id),
|
||||
});
|
||||
else setFilters({ labels: [...(filters?.labels ?? []), label.label_id ?? ""] });
|
||||
// if (filters.labels?.includes(label.label_id ?? ""))
|
||||
// setFilters({
|
||||
// labels: filters?.labels?.filter((l) => l !== label.label_id),
|
||||
// });
|
||||
// else setFilters({ labels: [...(filters?.labels ?? []), label.label_id ?? ""] });
|
||||
},
|
||||
selected: filters?.labels?.includes(label.label_id ?? ""),
|
||||
// selected: filters?.labels?.includes(label.label_id ?? ""),
|
||||
})}
|
||||
/>
|
||||
))
|
||||
|
@ -1,62 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
// react hook form
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
// hooks
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
|
||||
// components
|
||||
import { InlineCreateIssueFormWrapper } from "components/core";
|
||||
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
onSuccess?: (data: IIssue) => Promise<void> | void;
|
||||
prePopulatedData?: Partial<IIssue>;
|
||||
};
|
||||
|
||||
const InlineInput = () => {
|
||||
const { projectDetails } = useProjectDetails();
|
||||
|
||||
const { register, setFocus } = useFormContext();
|
||||
|
||||
useEffect(() => {
|
||||
setFocus("name");
|
||||
}, [setFocus]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-[14px] h-[14px] rounded-full border border-custom-border-1000 flex-shrink-0" />
|
||||
<h4 className="text-sm text-custom-text-400">{projectDetails?.identifier ?? "..."}</h4>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
placeholder="Issue Title"
|
||||
{...register("name", {
|
||||
required: "Issue title is required.",
|
||||
})}
|
||||
className="w-full px-2 rounded-md bg-transparent text-sm font-medium leading-5 text-custom-text-200 outline-none"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const GanttInlineCreateIssueForm: React.FC<Props> = (props) => (
|
||||
<>
|
||||
<InlineCreateIssueFormWrapper
|
||||
className="flex py-3 px-4 border-[0.5px] border-custom-border-100 mr-2.5 items-center rounded gap-x-2 bg-custom-background-100 shadow-custom-shadow-sm"
|
||||
{...props}
|
||||
>
|
||||
<InlineInput />
|
||||
</InlineCreateIssueFormWrapper>
|
||||
{props.isOpen && (
|
||||
<p className="text-xs ml-3 mt-3 italic text-custom-text-200">
|
||||
Press {"'"}Enter{"'"} to add another issue
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
@ -1 +0,0 @@
|
||||
export * from "./inline-issue-create-wrapper";
|
@ -1,238 +0,0 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
import { useForm, FormProvider } from "react-hook-form";
|
||||
import { Transition } from "@headlessui/react";
|
||||
// services
|
||||
import { ModuleService } from "services/module.service";
|
||||
import { IssueService, IssueDraftService } from "services/issue";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUser from "hooks/use-user";
|
||||
import useKeypress from "hooks/use-keypress";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useMyIssues from "hooks/my-issues/use-my-issues";
|
||||
import useGanttChartIssues from "hooks/gantt-chart/issue-view";
|
||||
// import useCalendarIssuesView from "hooks/use-calendar-issues-view";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view";
|
||||
// helpers
|
||||
import { getFetchKeysForIssueMutation } from "helpers/string.helper";
|
||||
|
||||
// fetch-keys
|
||||
import {
|
||||
USER_ISSUE,
|
||||
SUB_ISSUES,
|
||||
CYCLE_ISSUES_WITH_PARAMS,
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
CYCLE_DETAILS,
|
||||
MODULE_DETAILS,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
||||
const defaultValues: Partial<IIssue> = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
onSuccess?: (data: IIssue) => Promise<void> | void;
|
||||
prePopulatedData?: Partial<IIssue>;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const issueService = new IssueService();
|
||||
const issueDraftService = new IssueDraftService();
|
||||
const moduleService = new ModuleService();
|
||||
|
||||
export const addIssueToCycle = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
cycleId: string,
|
||||
user: any,
|
||||
params: any
|
||||
) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issueService
|
||||
.addIssueToCycle(
|
||||
workspaceSlug as string,
|
||||
projectId.toString(),
|
||||
cycleId,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
)
|
||||
.then(() => {
|
||||
if (cycleId) {
|
||||
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId, params));
|
||||
mutate(CYCLE_DETAILS(cycleId as string));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addIssueToModule = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleId: string,
|
||||
user: any,
|
||||
params: any
|
||||
) => {
|
||||
await moduleService
|
||||
.addIssuesToModule(
|
||||
workspaceSlug as string,
|
||||
projectId.toString(),
|
||||
moduleId as string,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
)
|
||||
.then(() => {
|
||||
if (moduleId) {
|
||||
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
|
||||
mutate(MODULE_DETAILS(moduleId as string));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const InlineCreateIssueFormWrapper: React.FC<Props> = (props) => {
|
||||
const { isOpen, handleClose, onSuccess, prePopulatedData, children, className } = props;
|
||||
|
||||
const ref = useRef<HTMLFormElement>(null);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
const isDraftIssues = router.pathname?.split("/")?.[4] === "draft-issues";
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { displayFilters, params } = useIssuesView();
|
||||
// const { params: calendarParams } = useCalendarIssuesView();
|
||||
const { ...viewGanttParams } = params;
|
||||
// const { params: spreadsheetParams } = useSpreadsheetIssuesView();
|
||||
const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString());
|
||||
const { params: ganttParams } = useGanttChartIssues(workspaceSlug?.toString(), projectId?.toString());
|
||||
|
||||
const method = useForm<IIssue>({ defaultValues });
|
||||
const {
|
||||
reset,
|
||||
handleSubmit,
|
||||
getValues,
|
||||
formState: { errors, isSubmitting },
|
||||
} = method;
|
||||
|
||||
useOutsideClickDetector(ref, handleClose);
|
||||
useKeypress("Escape", handleClose);
|
||||
|
||||
useEffect(() => {
|
||||
const values = getValues();
|
||||
|
||||
if (prePopulatedData) reset({ ...defaultValues, ...values, ...prePopulatedData });
|
||||
}, [reset, prePopulatedData, getValues]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) reset({ ...defaultValues });
|
||||
}, [isOpen, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!errors) return;
|
||||
|
||||
Object.keys(errors).forEach((key) => {
|
||||
const error = errors[key as keyof IIssue];
|
||||
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: error?.message?.toString() || "Some error occurred. Please try again.",
|
||||
});
|
||||
});
|
||||
}, [errors, setToastAlert]);
|
||||
|
||||
const { ganttFetchKey } = getFetchKeysForIssueMutation({
|
||||
cycleId: cycleId,
|
||||
moduleId: moduleId,
|
||||
viewId: viewId,
|
||||
projectId: projectId?.toString() ?? "",
|
||||
viewGanttParams,
|
||||
ganttParams,
|
||||
});
|
||||
|
||||
const onSubmitHandler = async (formData: IIssue) => {
|
||||
if (!workspaceSlug || !projectId || !user || isSubmitting) return;
|
||||
|
||||
reset({ ...defaultValues });
|
||||
|
||||
await (!isDraftIssues
|
||||
? issueService.createIssue(workspaceSlug.toString(), projectId.toString(), formData, user)
|
||||
: issueDraftService.createDraftIssue(workspaceSlug.toString(), projectId.toString(), formData)
|
||||
)
|
||||
.then(async (res) => {
|
||||
await mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params));
|
||||
if (formData.cycle && formData.cycle !== "")
|
||||
await addIssueToCycle(workspaceSlug.toString(), projectId.toString(), res.id, formData.cycle, user, params);
|
||||
if (formData.module && formData.module !== "")
|
||||
await addIssueToModule(workspaceSlug.toString(), projectId.toString(), res.id, formData.module, user, params);
|
||||
|
||||
if (isDraftIssues) await mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(projectId.toString() ?? "", params));
|
||||
if (displayFilters.layout === "gantt_chart") await mutate(ganttFetchKey);
|
||||
if (groupedIssues) await mutateMyIssues();
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "Issue created successfully.",
|
||||
});
|
||||
|
||||
if (onSuccess) await onSuccess(res);
|
||||
|
||||
if (formData.assignees?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE(workspaceSlug as string));
|
||||
|
||||
if (formData.parent && formData.parent !== "") mutate(SUB_ISSUES(formData.parent));
|
||||
})
|
||||
.catch((err) => {
|
||||
Object.keys(err || {}).forEach((key) => {
|
||||
const error = err?.[key];
|
||||
const errorTitle = error ? (Array.isArray(error) ? error.join(", ") : error) : null;
|
||||
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: errorTitle || "Some error occurred. Please try again.",
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition
|
||||
show={isOpen}
|
||||
enter="transition ease-in-out duration-200 transform"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="transition ease-in-out duration-200 transform"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<FormProvider {...method}>
|
||||
<form ref={ref} className={className} onSubmit={handleSubmit(onSubmitHandler)}>
|
||||
{children}
|
||||
</form>
|
||||
</FormProvider>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,17 +1,16 @@
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import useSWR from "swr";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import { CycleService } from "services/cycle.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
//icons
|
||||
import { ContrastIcon, TransferIcon } from "@plane/ui";
|
||||
import { AlertCircle, Search, X } from "lucide-react";
|
||||
// fetch-key
|
||||
import { CYCLE_ISSUES_WITH_PARAMS, INCOMPLETE_CYCLES_LIST } from "constants/fetch-keys";
|
||||
import { INCOMPLETE_CYCLES_LIST } from "constants/fetch-keys";
|
||||
// types
|
||||
import { ICycle } from "types";
|
||||
//helper
|
||||
@ -30,15 +29,12 @@ export const TransferIssuesModal: React.FC<Props> = ({ isOpen, handleClose }) =>
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
|
||||
const { params } = useIssuesView();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const transferIssue = async (payload: any) => {
|
||||
await cycleService
|
||||
.transferIssues(workspaceSlug as string, projectId as string, cycleId as string, payload)
|
||||
.then(() => {
|
||||
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Issues transfered successfully",
|
||||
|
@ -6,8 +6,6 @@ import { Popover } from "@headlessui/react";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
@ -38,9 +36,9 @@ export const InboxActionsHeader = observer(() => {
|
||||
const { inboxIssues: inboxIssuesStore, inboxIssueDetails: inboxIssueDetailsStore, user: userStore } = useMobxStore();
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
const userRole = userStore.currentProjectRole;
|
||||
const issuesList = inboxId ? inboxIssuesStore.inboxIssues[inboxId.toString()] : null;
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const markInboxStatus = async (data: TInboxStatus) => {
|
||||
@ -73,7 +71,7 @@ export const InboxActionsHeader = observer(() => {
|
||||
}, [issue]);
|
||||
|
||||
const issueStatus = issue?.issue_inbox[0].status;
|
||||
const isAllowed = memberRole.isMember || memberRole.isOwner;
|
||||
const isAllowed = userRole === 15 || userRole === 20;
|
||||
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
|
@ -7,8 +7,6 @@ import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, XCircle
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// components
|
||||
import { IssueDescriptionForm, IssueDetailsSidebar, IssueReaction } from "components/issues";
|
||||
import { InboxIssueActivity } from "components/inbox";
|
||||
@ -35,8 +33,7 @@ export const InboxMainContent: React.FC = observer(() => {
|
||||
const { inboxIssues: inboxIssuesStore, inboxIssueDetails: inboxIssueDetailsStore, user: userStore } = useMobxStore();
|
||||
|
||||
const user = userStore.currentUser;
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const { reset, control, watch } = useForm<IIssue>({
|
||||
defaultValues,
|
||||
@ -225,7 +222,7 @@ export const InboxMainContent: React.FC = observer(() => {
|
||||
description_html: issueDetails.description_html,
|
||||
}}
|
||||
handleFormSubmit={submitChanges}
|
||||
isAllowed={memberRole.isMember || memberRole.isOwner || user?.id === issueDetails.created_by}
|
||||
isAllowed={userRole === 15 || userRole === 20 || user?.id === issueDetails.created_by}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { IMentionHighlight, IMentionSuggestion, RichTextEditorWithRef } from "@plane/rich-text-editor";
|
||||
import { RichTextEditorWithRef } from "@plane/rich-text-editor";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
@ -15,8 +15,6 @@ import { IssuePrioritySelect } from "components/issues/select";
|
||||
import { Button, Input, ToggleSwitch } from "@plane/ui";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
import useProjectMembers from "hooks/use-project-members";
|
||||
import useUser from "hooks/use-user";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
@ -1,17 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { mutate } from "swr";
|
||||
|
||||
import useUser from "hooks/use-user";
|
||||
|
||||
// headless ui
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { IssueDraftService } from "services/issue";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useToast from "hooks/use-toast";
|
||||
// icons
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
@ -19,8 +14,6 @@ import { AlertTriangle } from "lucide-react";
|
||||
import { Button } from "@plane/ui";
|
||||
// types
|
||||
import type { IIssue } from "types";
|
||||
// fetch-keys
|
||||
import { PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
@ -31,20 +24,19 @@ type Props = {
|
||||
|
||||
const issueDraftService = new IssueDraftService();
|
||||
|
||||
export const DeleteDraftIssueModal: React.FC<Props> = (props) => {
|
||||
export const DeleteDraftIssueModal: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, handleClose, data, onSubmit } = props;
|
||||
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
const { user: userStore } = useMobxStore();
|
||||
const user = userStore.currentUser;
|
||||
|
||||
const { params } = useIssuesView();
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
setIsDeleteLoading(false);
|
||||
}, [isOpen]);
|
||||
@ -64,7 +56,7 @@ export const DeleteDraftIssueModal: React.FC<Props> = (props) => {
|
||||
.then(() => {
|
||||
setIsDeleteLoading(false);
|
||||
handleClose();
|
||||
mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(projectId as string, params));
|
||||
|
||||
setToastAlert({
|
||||
title: "Success",
|
||||
message: "Draft Issue deleted successfully",
|
||||
@ -146,4 +138,4 @@ export const DeleteDraftIssueModal: React.FC<Props> = (props) => {
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -9,28 +9,14 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { IssueService, IssueDraftService } from "services/issue";
|
||||
import { ModuleService } from "services/module.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
import useMyIssues from "hooks/my-issues/use-my-issues";
|
||||
// components
|
||||
import { DraftIssueForm } from "components/issues";
|
||||
// types
|
||||
import type { IIssue } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
PROJECT_ISSUES_DETAILS,
|
||||
USER_ISSUE,
|
||||
SUB_ISSUES,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
CYCLE_ISSUES_WITH_PARAMS,
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
VIEW_ISSUES,
|
||||
PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS,
|
||||
CYCLE_DETAILS,
|
||||
MODULE_DETAILS,
|
||||
} from "constants/fetch-keys";
|
||||
import { PROJECT_ISSUES_DETAILS, USER_ISSUE, SUB_ISSUES } from "constants/fetch-keys";
|
||||
|
||||
interface IssuesModalProps {
|
||||
data?: IIssue | null;
|
||||
@ -77,21 +63,15 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = observer(
|
||||
const [prePopulateData, setPreloadedData] = useState<Partial<IIssue> | undefined>(undefined);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
|
||||
const { project: projectStore } = useMobxStore();
|
||||
const { project: projectStore, user: userStore } = useMobxStore();
|
||||
|
||||
const user = userStore.currentUser;
|
||||
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
||||
|
||||
const { displayFilters, params } = useIssuesView();
|
||||
const { ...viewGanttParams } = params;
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const { clearValue: clearDraftIssueLocalStorage } = useLocalStorage("draftedIssue", {});
|
||||
|
||||
const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString());
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const onClose = () => {
|
||||
@ -184,31 +164,19 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = observer(
|
||||
setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null);
|
||||
}, [activeProject, data, projectId, projects, isOpen, prePopulateData]);
|
||||
|
||||
const ganttFetchKey = cycleId
|
||||
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString())
|
||||
: moduleId
|
||||
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString())
|
||||
: viewId
|
||||
? VIEW_ISSUES(viewId.toString(), viewGanttParams)
|
||||
: PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "");
|
||||
|
||||
const createDraftIssue = async (payload: Partial<IIssue>) => {
|
||||
if (!workspaceSlug || !activeProject || !user) return;
|
||||
|
||||
await issueDraftService
|
||||
.createDraftIssue(workspaceSlug as string, activeProject ?? "", payload)
|
||||
.then(async () => {
|
||||
mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
|
||||
|
||||
if (groupedIssues) mutateMyIssues();
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "Issue created successfully.",
|
||||
});
|
||||
|
||||
if (payload.assignees?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE(workspaceSlug as string));
|
||||
if (payload.assignees?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE(workspaceSlug.toString()));
|
||||
})
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
@ -231,8 +199,6 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = observer(
|
||||
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
|
||||
} else {
|
||||
if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString()));
|
||||
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
|
||||
mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
|
||||
}
|
||||
|
||||
if (!payload.is_draft) {
|
||||
@ -258,64 +224,42 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = observer(
|
||||
};
|
||||
|
||||
const addIssueToCycle = async (issueId: string, cycleId: string) => {
|
||||
if (!workspaceSlug || !activeProject) return;
|
||||
if (!workspaceSlug || !activeProject || !user) return;
|
||||
|
||||
await issueService
|
||||
.addIssueToCycle(
|
||||
workspaceSlug as string,
|
||||
activeProject ?? "",
|
||||
cycleId,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
)
|
||||
.then(() => {
|
||||
if (cycleId) {
|
||||
mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId, params));
|
||||
mutate(CYCLE_DETAILS(cycleId as string));
|
||||
}
|
||||
});
|
||||
await issueService.addIssueToCycle(
|
||||
workspaceSlug as string,
|
||||
activeProject ?? "",
|
||||
cycleId,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
);
|
||||
};
|
||||
|
||||
const addIssueToModule = async (issueId: string, moduleId: string) => {
|
||||
if (!workspaceSlug || !activeProject) return;
|
||||
if (!workspaceSlug || !activeProject || !user) return;
|
||||
|
||||
await moduleService
|
||||
.addIssuesToModule(
|
||||
workspaceSlug as string,
|
||||
activeProject ?? "",
|
||||
moduleId as string,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
)
|
||||
.then(() => {
|
||||
if (moduleId) {
|
||||
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
|
||||
mutate(MODULE_DETAILS(moduleId as string));
|
||||
}
|
||||
});
|
||||
await moduleService.addIssuesToModule(
|
||||
workspaceSlug as string,
|
||||
activeProject ?? "",
|
||||
moduleId as string,
|
||||
{
|
||||
issues: [issueId],
|
||||
},
|
||||
user
|
||||
);
|
||||
};
|
||||
|
||||
const createIssue = async (payload: Partial<IIssue>) => {
|
||||
if (!workspaceSlug || !activeProject) return;
|
||||
if (!workspaceSlug || !activeProject || !user) return;
|
||||
|
||||
await issueService
|
||||
.createIssue(workspaceSlug as string, activeProject ?? "", payload, user)
|
||||
.createIssue(workspaceSlug.toString(), activeProject, payload, user)
|
||||
.then(async (res) => {
|
||||
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
|
||||
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle);
|
||||
if (payload.module && payload.module !== "") await addIssueToModule(res.id, payload.module);
|
||||
|
||||
if (displayFilters.layout === "gantt_chart")
|
||||
mutate(ganttFetchKey, {
|
||||
start_target_date: true,
|
||||
order_by: "sort_order",
|
||||
});
|
||||
if (groupedIssues) mutateMyIssues();
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
@ -400,7 +344,7 @@ export const CreateUpdateDraftIssueModal: React.FC<IssuesModalProps> = observer(
|
||||
projectId={activeProject ?? ""}
|
||||
setActiveProject={setActiveProject}
|
||||
status={data ? true : false}
|
||||
user={user}
|
||||
user={user ?? undefined}
|
||||
fieldsToShow={fieldsToShow}
|
||||
/>
|
||||
</Dialog.Panel>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { FC, useState } from "react";
|
||||
import { mutate } from "swr";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// ui icons
|
||||
import { DiceIcon, DoubleCircleIcon, UserGroupIcon } from "@plane/ui";
|
||||
import { CalendarDays, ContrastIcon, Link2, Plus, Signal, Tag, Triangle, User2 } from "lucide-react";
|
||||
@ -23,10 +25,7 @@ import { CustomDatePicker } from "components/ui";
|
||||
import { LinkModal, LinksList } from "components/core";
|
||||
// types
|
||||
import { ICycle, IIssue, IIssueLink, IModule, TIssuePriorities, linkDetails } from "types";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
import { ISSUE_DETAILS } from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
|
||||
@ -38,18 +37,19 @@ interface IPeekOverviewProperties {
|
||||
|
||||
const issueService = new IssueService();
|
||||
|
||||
export const PeekOverviewProperties: FC<IPeekOverviewProperties> = (props) => {
|
||||
export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((props) => {
|
||||
const { issue, issueUpdate, user } = props;
|
||||
const [linkModal, setLinkModal] = useState(false);
|
||||
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
|
||||
const handleState = (_state: string) => {
|
||||
issueUpdate({ ...issue, state: _state });
|
||||
};
|
||||
@ -346,7 +346,12 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = (props) => {
|
||||
links={issue.issue_link}
|
||||
handleDeleteLink={handleDeleteLink}
|
||||
handleEditLink={handleEditLink}
|
||||
userAuth={memberRole}
|
||||
userAuth={{
|
||||
isGuest: userRole === 5,
|
||||
isViewer: userRole === 10,
|
||||
isMember: userRole === 15,
|
||||
isOwner: userRole === 20,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@ -355,4 +360,4 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = (props) => {
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -1,16 +1,13 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { IssueService, IssueCommentService } from "services/issue";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// components
|
||||
import {
|
||||
AddComment,
|
||||
@ -40,16 +37,18 @@ type Props = {
|
||||
const issueService = new IssueService();
|
||||
const issueCommentService = new IssueCommentService();
|
||||
|
||||
export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges, uneditable = false }) => {
|
||||
export const IssueMainContent: React.FC<Props> = observer((props) => {
|
||||
const { issueDetails, submitChanges, uneditable = false } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user } = useUserAuth();
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
|
||||
const { projectDetails } = useProjectDetails();
|
||||
const { user: userStore, project: projectStore } = useMobxStore();
|
||||
const user = userStore.currentUser ?? undefined;
|
||||
const userRole = userStore.currentProjectRole;
|
||||
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined;
|
||||
|
||||
const { data: siblingIssues } = useSWR(
|
||||
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
|
||||
@ -75,7 +74,7 @@ export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges,
|
||||
};
|
||||
|
||||
const handleCommentDelete = async (commentId: string) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
if (!workspaceSlug || !projectId || !issueId || !user) return;
|
||||
|
||||
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
||||
|
||||
@ -85,7 +84,7 @@ export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges,
|
||||
};
|
||||
|
||||
const handleAddComment = async (formData: IIssueComment) => {
|
||||
if (!workspaceSlug || !issueDetails) return;
|
||||
if (!workspaceSlug || !issueDetails || !user) return;
|
||||
|
||||
await issueCommentService
|
||||
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData, user)
|
||||
@ -167,7 +166,7 @@ export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges,
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
issue={issueDetails}
|
||||
handleFormSubmit={submitChanges}
|
||||
isAllowed={memberRole.isMember || memberRole.isOwner || !uneditable}
|
||||
isAllowed={userRole === 20 || userRole === 15 || !uneditable}
|
||||
/>
|
||||
|
||||
<IssueReaction workspaceSlug={workspaceSlug} issueId={issueId} projectId={projectId} />
|
||||
@ -199,4 +198,4 @@ export const IssueMainContent: React.FC<Props> = ({ issueDetails, submitChanges,
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -1,17 +1,17 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { mutate } from "swr";
|
||||
import { Controller, UseFormWatch } from "react-hook-form";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useUserIssueNotificationSubscription from "hooks/use-issue-notification-subscription";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
import { ModuleService } from "services/module.service";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// components
|
||||
import { LinkModal, LinksList } from "components/core";
|
||||
import {
|
||||
@ -72,23 +72,20 @@ type Props = {
|
||||
const issueService = new IssueService();
|
||||
const moduleService = new ModuleService();
|
||||
|
||||
export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
control,
|
||||
submitChanges,
|
||||
issueDetail,
|
||||
watch: watchIssue,
|
||||
fieldsToShow = ["all"],
|
||||
uneditable = false,
|
||||
}) => {
|
||||
export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
const { control, submitChanges, issueDetail, watch: watchIssue, fieldsToShow = ["all"], uneditable = false } = props;
|
||||
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
const [linkModal, setLinkModal] = useState(false);
|
||||
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const user = userStore.currentUser;
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { user } = useUserAuth();
|
||||
|
||||
const { isEstimateActive } = useEstimateOption();
|
||||
|
||||
const { loading, handleSubscribe, handleUnsubscribe, subscribed } = useUserIssueNotificationSubscription(
|
||||
@ -97,13 +94,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
issueId
|
||||
);
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleCycleChange = useCallback(
|
||||
(cycleDetails: ICycle) => {
|
||||
if (!workspaceSlug || !projectId || !issueDetail) return;
|
||||
if (!workspaceSlug || !projectId || !issueDetail || !user) return;
|
||||
|
||||
issueService
|
||||
.addIssueToCycle(
|
||||
@ -124,7 +119,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
|
||||
const handleModuleChange = useCallback(
|
||||
(moduleDetail: IModule) => {
|
||||
if (!workspaceSlug || !projectId || !issueDetail) return;
|
||||
if (!workspaceSlug || !projectId || !issueDetail || !user) return;
|
||||
|
||||
moduleService
|
||||
.addIssuesToModule(
|
||||
@ -262,7 +257,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
setLinkModal(true);
|
||||
};
|
||||
|
||||
const isNotAllowed = memberRole.isGuest || memberRole.isViewer;
|
||||
const isNotAllowed = userRole === 5 || userRole === 10;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -341,7 +336,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarStateSelect
|
||||
value={value}
|
||||
onChange={(val: string) => submitChanges({ state: val })}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -362,7 +357,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarAssigneeSelect
|
||||
value={value}
|
||||
onChange={(val: string[]) => submitChanges({ assignees: val })}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -383,7 +378,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarPrioritySelect
|
||||
value={value}
|
||||
onChange={(val) => submitChanges({ priority: val })}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -404,7 +399,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarEstimateSelect
|
||||
value={value}
|
||||
onChange={(val: number | null) => submitChanges({ estimate_point: val })}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -432,7 +427,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
onChange(val);
|
||||
}}
|
||||
issueDetails={issueDetail}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -457,7 +452,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
|
||||
}}
|
||||
watch={watchIssue}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("blocked")) && (
|
||||
@ -478,7 +473,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
|
||||
}}
|
||||
watch={watchIssue}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("duplicate")) && (
|
||||
@ -496,7 +491,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
|
||||
}}
|
||||
watch={watchIssue}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("relates_to")) && (
|
||||
@ -514,7 +509,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
|
||||
}}
|
||||
watch={watchIssue}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("startDate")) && (
|
||||
@ -587,7 +582,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarCycleSelect
|
||||
issueDetail={issueDetail}
|
||||
handleCycleChange={handleCycleChange}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -602,7 +597,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
<SidebarModuleSelect
|
||||
issueDetail={issueDetail}
|
||||
handleModuleChange={handleModuleChange}
|
||||
disabled={memberRole.isGuest || memberRole.isViewer || uneditable}
|
||||
disabled={isNotAllowed || uneditable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -650,7 +645,12 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
links={issueDetail.issue_link}
|
||||
handleDeleteLink={handleDeleteLink}
|
||||
handleEditLink={handleEditLink}
|
||||
userAuth={memberRole}
|
||||
userAuth={{
|
||||
isGuest: userRole === 5,
|
||||
isViewer: userRole === 10,
|
||||
isMember: userRole === 15,
|
||||
isOwner: userRole === 20,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@ -660,4 +660,4 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useCallback } from "react";
|
||||
// next imports
|
||||
import { useRouter } from "next/router";
|
||||
// swr
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR, { mutate } from "swr";
|
||||
// lucide icons
|
||||
import { Plus, ChevronRight, ChevronDown } from "lucide-react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||
@ -13,8 +13,6 @@ import { ProgressBar } from "./progressbar";
|
||||
// ui
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
import useToast from "hooks/use-toast";
|
||||
// helpers
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
@ -42,23 +40,21 @@ export interface ISubIssuesRootLoadersHandler {
|
||||
|
||||
const issueService = new IssueService();
|
||||
|
||||
export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) => {
|
||||
export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
|
||||
const { parentIssue, user } = props;
|
||||
|
||||
const { user: userStore, issue: issueStore, issueDetail: issueDetailStore } = useMobxStore();
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
peekIssue: string;
|
||||
};
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { issue: issueStore, issueDetail: issueDetailStore } = useMobxStore();
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { data: issues, isLoading } = useSWR(
|
||||
workspaceSlug && projectId && parentIssue && parentIssue?.id ? SUB_ISSUES(parentIssue?.id) : null,
|
||||
workspaceSlug && projectId && parentIssue && parentIssue?.id
|
||||
? () => issueService.subIssues(workspaceSlug, projectId, parentIssue.id)
|
||||
? () => issueService.subIssues(workspaceSlug.toString(), projectId.toString(), parentIssue.id)
|
||||
: null
|
||||
);
|
||||
|
||||
@ -117,20 +113,20 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
};
|
||||
|
||||
const addAsSubIssueFromExistingIssues = async (data: ISearchIssueResponse[]) => {
|
||||
if (!workspaceSlug || !parentIssue || issueCrudOperation?.existing?.issueId === null) return;
|
||||
if (!workspaceSlug || !projectId || !parentIssue || issueCrudOperation?.existing?.issueId === null) return;
|
||||
const issueId = issueCrudOperation?.existing?.issueId;
|
||||
const payload = {
|
||||
sub_issue_ids: data.map((i) => i.id),
|
||||
};
|
||||
await issueService.addSubIssues(workspaceSlug, projectId, issueId, payload).finally(() => {
|
||||
await issueService.addSubIssues(workspaceSlug.toString(), projectId.toString(), issueId, payload).finally(() => {
|
||||
if (issueId) mutate(SUB_ISSUES(issueId));
|
||||
});
|
||||
};
|
||||
|
||||
const removeIssueFromSubIssues = async (parentIssueId: string, issue: IIssue) => {
|
||||
if (!workspaceSlug || !parentIssue || !issue?.id) return;
|
||||
if (!workspaceSlug || !projectId || !parentIssue || !issue?.id) return;
|
||||
issueService
|
||||
.patchIssue(workspaceSlug, projectId, issue.id, { parent: null }, user)
|
||||
.patchIssue(workspaceSlug.toString(), projectId.toString(), issue.id, { parent: null }, user)
|
||||
.then(async () => {
|
||||
if (parentIssueId) await mutate(SUB_ISSUES(parentIssueId));
|
||||
handleIssuesLoader({ key: "delete", issueId: issue?.id });
|
||||
@ -176,7 +172,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
[issueStore, issueDetailStore, projectId, user, workspaceSlug]
|
||||
);
|
||||
|
||||
const isEditable = memberRole?.isGuest || memberRole?.isViewer ? false : true;
|
||||
const isEditable = userRole === 5 || userRole === 10 ? false : true;
|
||||
|
||||
const mutateSubIssues = (parentIssueId: string | null) => {
|
||||
if (parentIssueId) mutate(SUB_ISSUES(parentIssueId));
|
||||
@ -233,11 +229,11 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
</div>
|
||||
|
||||
{/* issues */}
|
||||
{issuesLoader.visibility.includes(parentIssue?.id) && (
|
||||
{issuesLoader.visibility.includes(parentIssue?.id) && workspaceSlug && projectId && (
|
||||
<div className="border border-b-0 border-custom-border-100">
|
||||
<SubIssuesRootList
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
parentIssue={parentIssue}
|
||||
user={undefined}
|
||||
editable={isEditable}
|
||||
@ -371,4 +367,4 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -8,8 +8,6 @@ import { Disclosure, Transition } from "@headlessui/react";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { ModuleService } from "services/module.service";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
@ -60,10 +58,9 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
const { module: moduleStore, user: userStore } = useMobxStore();
|
||||
|
||||
const user = userStore.currentUser ?? undefined;
|
||||
const userRole = userStore.currentProjectRole;
|
||||
const moduleDetails = moduleStore.moduleDetails[moduleId] ?? undefined;
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { reset, control } = useForm({
|
||||
@ -429,7 +426,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<Transition show={open}>
|
||||
<Disclosure.Panel>
|
||||
<div className="flex flex-col w-full mt-2 space-y-3 h-72 overflow-y-auto">
|
||||
{memberRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
|
||||
{userRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
|
||||
<>
|
||||
<div className="flex items-center justify-end w-full">
|
||||
<button
|
||||
@ -445,7 +442,12 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
links={moduleDetails.link_module}
|
||||
handleEditLink={handleEditLink}
|
||||
handleDeleteLink={handleDeleteLink}
|
||||
userAuth={memberRole}
|
||||
userAuth={{
|
||||
isGuest: userRole === 5,
|
||||
isViewer: userRole === 10,
|
||||
isMember: userRole === 15,
|
||||
isOwner: userRole === 20,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
@ -15,7 +15,7 @@ import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
||||
// icons
|
||||
import { Check, ChevronDown, Plus, X } from "lucide-react";
|
||||
// types
|
||||
import { IUser, IWorkspace, TOnboardingSteps } from "types";
|
||||
import { IUser, IWorkspace, TOnboardingSteps, TUserWorkspaceRole } from "types";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
|
||||
@ -28,7 +28,7 @@ type Props = {
|
||||
|
||||
type EmailRole = {
|
||||
email: string;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
|
@ -1,6 +1,5 @@
|
||||
export * from "./overview";
|
||||
export * from "./navbar";
|
||||
export * from "./profile-issues-view";
|
||||
export * from "./sidebar";
|
||||
|
||||
export * from "./profile-issues-filter";
|
||||
|
@ -1,297 +0,0 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
import { DropResult } from "react-beautiful-dnd";
|
||||
// services
|
||||
import { IssueService, IssueLabelService } from "services/issue";
|
||||
import { UserService } from "services/user.service";
|
||||
// hooks
|
||||
import useProfileIssues from "hooks/use-profile-issues";
|
||||
import useUser from "hooks/use-user";
|
||||
// components
|
||||
import { FiltersList } from "components/core";
|
||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||
// helpers
|
||||
import { orderArrayBy } from "helpers/array.helper";
|
||||
// types
|
||||
import { IIssue, IIssueFilterOptions, TIssuePriorities } from "types";
|
||||
// fetch-keys
|
||||
import { USER_PROFILE_PROJECT_SEGREGATION, WORKSPACE_LABELS } from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
const issueService = new IssueService();
|
||||
const issueLabelService = new IssueLabelService();
|
||||
const userService = new UserService();
|
||||
|
||||
export const ProfileIssuesView = () => {
|
||||
// create issue modal
|
||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||
const [preloadedData, setPreloadedData] = useState<
|
||||
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
||||
>(undefined);
|
||||
|
||||
// update issue modal
|
||||
const [editIssueModal, setEditIssueModal] = useState(false);
|
||||
const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
|
||||
|
||||
// delete issue modal
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
const [issueToDelete, setIssueToDelete] = useState<IIssue | null>(null);
|
||||
|
||||
// trash box
|
||||
const [trashBox, setTrashBox] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, userId } = router.query;
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const {
|
||||
groupedIssues,
|
||||
mutateProfileIssues,
|
||||
displayFilters,
|
||||
isEmpty,
|
||||
filters,
|
||||
setFilters,
|
||||
displayProperties,
|
||||
params,
|
||||
} = useProfileIssues(workspaceSlug?.toString(), userId?.toString());
|
||||
|
||||
const { data: profileData } = useSWR(
|
||||
workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null,
|
||||
workspaceSlug && userId
|
||||
? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: labels } = useSWR(
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0 ? WORKSPACE_LABELS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug && (filters?.labels ?? []).length > 0
|
||||
? () => issueLabelService.getWorkspaceIssueLabels(workspaceSlug.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const handleDeleteIssue = useCallback(
|
||||
(issue: IIssue) => {
|
||||
setDeleteIssueModal(true);
|
||||
setIssueToDelete(issue);
|
||||
},
|
||||
[setDeleteIssueModal, setIssueToDelete]
|
||||
);
|
||||
|
||||
const handleOnDragEnd = useCallback(
|
||||
async (result: DropResult) => {
|
||||
setTrashBox(false);
|
||||
|
||||
if (!result.destination || !workspaceSlug || !groupedIssues || displayFilters?.group_by !== "priority") return;
|
||||
|
||||
const { source, destination } = result;
|
||||
|
||||
if (source.droppableId === destination.droppableId) return;
|
||||
|
||||
const draggedItem = groupedIssues[source.droppableId][source.index];
|
||||
|
||||
if (!draggedItem) return;
|
||||
|
||||
if (destination.droppableId === "trashBox") handleDeleteIssue(draggedItem);
|
||||
else {
|
||||
const sourceGroup = source.droppableId;
|
||||
const destinationGroup = destination.droppableId;
|
||||
|
||||
draggedItem[displayFilters.group_by] = destinationGroup as TIssuePriorities;
|
||||
|
||||
mutateProfileIssues((prevData: any) => {
|
||||
if (!prevData) return prevData;
|
||||
|
||||
const sourceGroupArray = [...groupedIssues[sourceGroup]];
|
||||
const destinationGroupArray = [...groupedIssues[destinationGroup]];
|
||||
|
||||
sourceGroupArray.splice(source.index, 1);
|
||||
destinationGroupArray.splice(destination.index, 0, draggedItem);
|
||||
|
||||
return {
|
||||
...prevData,
|
||||
[sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
[destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
};
|
||||
}, false);
|
||||
|
||||
// patch request
|
||||
issueService
|
||||
.patchIssue(
|
||||
workspaceSlug as string,
|
||||
draggedItem.project,
|
||||
draggedItem.id,
|
||||
{
|
||||
priority: draggedItem.priority,
|
||||
},
|
||||
user
|
||||
)
|
||||
.catch(() => mutateProfileIssues());
|
||||
}
|
||||
},
|
||||
[displayFilters, groupedIssues, handleDeleteIssue, mutateProfileIssues, user, workspaceSlug]
|
||||
);
|
||||
|
||||
const addIssueToGroup = useCallback(
|
||||
(groupTitle: string) => {
|
||||
setCreateIssueModal(true);
|
||||
|
||||
let preloadedValue: string | string[] = groupTitle;
|
||||
|
||||
if (displayFilters?.group_by === "labels") {
|
||||
if (groupTitle === "None") preloadedValue = [];
|
||||
else preloadedValue = [groupTitle];
|
||||
}
|
||||
|
||||
if (displayFilters?.group_by)
|
||||
setPreloadedData({
|
||||
[displayFilters?.group_by]: preloadedValue,
|
||||
actionType: "createIssue",
|
||||
});
|
||||
else setPreloadedData({ actionType: "createIssue" });
|
||||
},
|
||||
[setCreateIssueModal, setPreloadedData, displayFilters?.group_by]
|
||||
);
|
||||
|
||||
const addIssueToDate = useCallback(
|
||||
(date: string) => {
|
||||
setCreateIssueModal(true);
|
||||
setPreloadedData({
|
||||
target_date: date,
|
||||
actionType: "createIssue",
|
||||
});
|
||||
},
|
||||
[setCreateIssueModal, setPreloadedData]
|
||||
);
|
||||
|
||||
const makeIssueCopy = useCallback(
|
||||
(issue: IIssue) => {
|
||||
setCreateIssueModal(true);
|
||||
|
||||
setPreloadedData({ ...issue, name: `${issue.name} (Copy)`, actionType: "createIssue" });
|
||||
},
|
||||
[setCreateIssueModal, setPreloadedData]
|
||||
);
|
||||
|
||||
const handleEditIssue = useCallback(
|
||||
(issue: IIssue) => {
|
||||
setEditIssueModal(true);
|
||||
setIssueToEdit({
|
||||
...issue,
|
||||
actionType: "edit",
|
||||
cycle: issue.issue_cycle ? issue.issue_cycle.cycle : null,
|
||||
module: issue.issue_module ? issue.issue_module.module : null,
|
||||
});
|
||||
},
|
||||
[setEditIssueModal, setIssueToEdit]
|
||||
);
|
||||
|
||||
const handleIssueAction = useCallback(
|
||||
(issue: IIssue, action: "copy" | "edit" | "delete" | "updateDraft") => {
|
||||
if (action === "copy") makeIssueCopy(issue);
|
||||
else if (action === "edit") handleEditIssue(issue);
|
||||
else if (action === "delete") handleDeleteIssue(issue);
|
||||
},
|
||||
[makeIssueCopy, handleEditIssue, handleDeleteIssue]
|
||||
);
|
||||
|
||||
const filtersToDisplay = { ...filters, assignees: null, created_by: null, subscriber: null };
|
||||
|
||||
const nullFilters = Object.keys(filtersToDisplay).filter(
|
||||
(key) => filtersToDisplay[key as keyof IIssueFilterOptions] === null
|
||||
);
|
||||
const areFiltersApplied =
|
||||
Object.keys(filtersToDisplay).length > 0 && nullFilters.length !== Object.keys(filtersToDisplay).length;
|
||||
|
||||
const isSubscribedIssuesRoute = router.pathname.includes("subscribed");
|
||||
const isMySubscribedIssues =
|
||||
(filters.subscriber && filters.subscriber.length > 0 && router.pathname.includes("my-issues")) ?? false;
|
||||
|
||||
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues || user?.id !== userId;
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateUpdateIssueModal
|
||||
isOpen={createIssueModal && preloadedData?.actionType === "createIssue"}
|
||||
handleClose={() => setCreateIssueModal(false)}
|
||||
prePopulateData={{
|
||||
...preloadedData,
|
||||
}}
|
||||
onSubmit={async () => {
|
||||
mutateProfileIssues();
|
||||
}}
|
||||
/>
|
||||
<CreateUpdateIssueModal
|
||||
isOpen={editIssueModal && issueToEdit?.actionType !== "delete"}
|
||||
handleClose={() => setEditIssueModal(false)}
|
||||
data={issueToEdit}
|
||||
onSubmit={async () => {
|
||||
mutateProfileIssues();
|
||||
}}
|
||||
/>
|
||||
{issueToDelete && (
|
||||
<DeleteIssueModal
|
||||
handleClose={() => setDeleteIssueModal(false)}
|
||||
isOpen={deleteIssueModal}
|
||||
data={issueToDelete}
|
||||
onSubmit={async () => {
|
||||
mutateProfileIssues();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{areFiltersApplied && (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2 px-5 pt-3 pb-0">
|
||||
<FiltersList
|
||||
filters={filtersToDisplay}
|
||||
setFilters={setFilters}
|
||||
labels={labels}
|
||||
members={undefined}
|
||||
states={undefined}
|
||||
clearAllFilters={() =>
|
||||
setFilters({
|
||||
mentions: null,
|
||||
labels: null,
|
||||
priority: null,
|
||||
state_group: null,
|
||||
start_date: null,
|
||||
target_date: null,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{<div className="mt-3 border-t border-custom-border-200" />}
|
||||
</>
|
||||
)}
|
||||
{/* <AllViews
|
||||
addIssueToDate={addIssueToDate}
|
||||
addIssueToGroup={addIssueToGroup}
|
||||
disableUserActions={false}
|
||||
dragDisabled={displayFilters?.group_by !== "priority"}
|
||||
emptyState={{
|
||||
title: router.pathname.includes("assigned")
|
||||
? `Issues assigned to ${profileData?.user_data.display_name} will appear here`
|
||||
: router.pathname.includes("created")
|
||||
? `Issues created by ${profileData?.user_data.display_name} will appear here`
|
||||
: `Issues subscribed by ${profileData?.user_data.display_name} will appear here`,
|
||||
}}
|
||||
handleOnDragEnd={handleOnDragEnd}
|
||||
handleIssueAction={handleIssueAction}
|
||||
openIssuesListModal={null}
|
||||
removeIssue={null}
|
||||
disableAddIssueOption={disableAddIssueOption}
|
||||
trashBox={trashBox}
|
||||
setTrashBox={setTrashBox}
|
||||
viewProps={{
|
||||
groupedIssues,
|
||||
displayFilters,
|
||||
isEmpty,
|
||||
mutateIssues: mutateProfileIssues,
|
||||
params,
|
||||
properties: displayProperties,
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
};
|
@ -16,6 +16,7 @@ import { CustomMenu, CustomSelect } from "@plane/ui";
|
||||
import { ChevronDown, X } from "lucide-react";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
import { TUserProjectRole } from "types";
|
||||
|
||||
// services
|
||||
const projectInvitationService = new ProjectInvitationService();
|
||||
@ -144,7 +145,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
}
|
||||
value={member.role}
|
||||
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
||||
onChange={(value: TUserProjectRole | undefined) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
projectStore
|
||||
|
@ -1,20 +1,21 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import { useForm, Controller, useFieldArray } from "react-hook-form";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { ChevronDown, Plus, X } from "lucide-react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// ui
|
||||
import { Avatar, Button, CustomSelect, CustomSearchSelect } from "@plane/ui";
|
||||
// services
|
||||
import { ProjectService } from "services/project";
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// contexts
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// types
|
||||
import { IUser } from "types";
|
||||
import { IUser, TUserProjectRole } from "types";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||
// constants
|
||||
@ -29,7 +30,7 @@ type Props = {
|
||||
};
|
||||
|
||||
type member = {
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserProjectRole;
|
||||
member_id: string;
|
||||
};
|
||||
|
||||
@ -50,14 +51,16 @@ const defaultValues: FormValues = {
|
||||
const projectService = new ProjectService();
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export const SendProjectInvitationModal: React.FC<Props> = (props) => {
|
||||
export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, setIsOpen, members, user, onSuccess } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
const { memberDetails } = useProjectMyMembership();
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const { data: people } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug as string) : null,
|
||||
@ -243,7 +246,7 @@ export const SendProjectInvitationModal: React.FC<Props> = (props) => {
|
||||
width="w-full"
|
||||
>
|
||||
{Object.entries(ROLE).map(([key, label]) => {
|
||||
if (parseInt(key) > (memberDetails?.role ?? 5)) return null;
|
||||
if (parseInt(key) > (userRole ?? 5)) return null;
|
||||
|
||||
return (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
@ -305,4 +308,4 @@ export const SendProjectInvitationModal: React.FC<Props> = (props) => {
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
@ -1,182 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { AuthService } from "services/auth.service";
|
||||
import { AppConfigService } from "services/app_config.service";
|
||||
// components
|
||||
import {
|
||||
GoogleLoginButton,
|
||||
GithubLoginButton,
|
||||
EmailCodeForm,
|
||||
EmailPasswordForm,
|
||||
EmailPasswordFormValues,
|
||||
} from "components/account";
|
||||
// ui
|
||||
import { Spinner } from "@plane/ui";
|
||||
// images
|
||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
import { IUserSettings } from "types";
|
||||
|
||||
const appConfigService = new AppConfigService();
|
||||
const authService = new AuthService();
|
||||
|
||||
export const SignInView = observer(() => {
|
||||
const { user: userStore } = useMobxStore();
|
||||
// router
|
||||
const router = useRouter();
|
||||
// toast
|
||||
const { setToastAlert } = useToast();
|
||||
// fetch app config
|
||||
const { data } = useSWR("APP_CONFIG", () => appConfigService.envConfig());
|
||||
// fetch user info
|
||||
const { data: user, error } = useSWR("USER_INFO", () => userStore.fetchCurrentUser());
|
||||
// computed
|
||||
const enableEmailPassword =
|
||||
data?.email_password_login || !(data?.email_password_login || data?.magic_login || data?.google || data?.github);
|
||||
|
||||
const handleLoginRedirection = () =>
|
||||
userStore.fetchCurrentUserSettings().then((userSettings: IUserSettings) => {
|
||||
const workspaceSlug =
|
||||
userSettings?.workspace?.last_workspace_slug || userSettings?.workspace?.fallback_workspace_slug;
|
||||
router.push(`/${workspaceSlug}`);
|
||||
});
|
||||
|
||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||
try {
|
||||
if (clientId && credential) {
|
||||
const socialAuthPayload = {
|
||||
medium: "google",
|
||||
credential,
|
||||
clientId,
|
||||
};
|
||||
const response = await authService.socialAuth(socialAuthPayload);
|
||||
if (response) {
|
||||
handleLoginRedirection();
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: err?.error || "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleGitHubSignIn = async (credential: string) => {
|
||||
try {
|
||||
if (data && data.github && credential) {
|
||||
const socialAuthPayload = {
|
||||
medium: "github",
|
||||
credential,
|
||||
clientId: data.github,
|
||||
};
|
||||
const response = await authService.socialAuth(socialAuthPayload);
|
||||
if (response) {
|
||||
handleLoginRedirection();
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: err?.error || "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordSignIn = async (formData: EmailPasswordFormValues) => {
|
||||
await authService
|
||||
.emailLogin(formData)
|
||||
.then((response) => {
|
||||
if (response) {
|
||||
handleLoginRedirection();
|
||||
}
|
||||
})
|
||||
.catch((err) =>
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: err?.error || "Something went wrong. Please try again later or contact the support team.",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleEmailCodeSignIn = async (response: any) => {
|
||||
try {
|
||||
if (response) {
|
||||
handleLoginRedirection();
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: err?.error || "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!user && !error ? (
|
||||
<div className="grid place-items-center h-screen">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<div className="hidden sm:block sm:fixed border-r-[0.5px] border-custom-border-200 h-screen w-[0.5px] top-0 left-20 lg:left-32" />
|
||||
<div className="fixed grid place-items-center bg-custom-background-100 sm:py-5 top-11 sm:top-12 left-7 sm:left-16 lg:left-28">
|
||||
<div className="grid place-items-center bg-custom-background-100">
|
||||
<div className="h-[30px] w-[30px]">
|
||||
<Image src={BluePlaneLogoWithoutText} alt="Plane Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="grid place-items-center h-full overflow-y-auto py-5 px-7">
|
||||
<div>
|
||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
||||
Sign in to Plane
|
||||
</h1>
|
||||
<>
|
||||
{enableEmailPassword && <EmailPasswordForm onSubmit={handlePasswordSignIn} />}
|
||||
{data?.magic_login && (
|
||||
<div className="flex flex-col divide-y divide-custom-border-200">
|
||||
<div className="pb-7">
|
||||
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
|
||||
{data?.google && <GoogleLoginButton clientId={data?.google} handleSignIn={handleGoogleSignIn} />}
|
||||
{data?.github && <GithubLoginButton clientId={data?.github} handleSignIn={handleGitHubSignIn} />}
|
||||
</div>
|
||||
</>
|
||||
<p className="pt-16 text-custom-text-200 text-sm text-center">
|
||||
By signing up, you agree to the{" "}
|
||||
<a
|
||||
href="https://plane.so/terms-and-conditions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-medium underline"
|
||||
>
|
||||
Terms & Conditions
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
@ -11,7 +11,7 @@ import { Button, CustomSelect, Input } from "@plane/ui";
|
||||
// icons
|
||||
import { Plus, X } from "lucide-react";
|
||||
// types
|
||||
import { IUser } from "types";
|
||||
import { IUser, TUserWorkspaceRole } from "types";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
// fetch-keys
|
||||
@ -27,7 +27,7 @@ type Props = {
|
||||
|
||||
type EmailRole = {
|
||||
email: string;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
|
@ -15,6 +15,7 @@ import { CustomSelect, Tooltip } from "@plane/ui";
|
||||
import { ChevronDown, XCircle } from "lucide-react";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
import { TUserWorkspaceRole } from "types";
|
||||
|
||||
type Props = {
|
||||
member: {
|
||||
@ -25,7 +26,7 @@ type Props = {
|
||||
last_name: string;
|
||||
email: string | undefined;
|
||||
display_name: string;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
status: boolean;
|
||||
member: boolean;
|
||||
accountCreated: boolean;
|
||||
@ -153,7 +154,7 @@ export const WorkspaceMembersListItem: FC<Props> = (props) => {
|
||||
</div>
|
||||
}
|
||||
value={member.role}
|
||||
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
||||
onChange={(value: TUserWorkspaceRole | undefined) => {
|
||||
if (!workspaceSlug || !value) return;
|
||||
|
||||
workspaceStore
|
||||
|
@ -8,10 +8,8 @@ import { Check, LogOut, Plus, Settings, UserCircle2 } from "lucide-react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import { UserService } from "services/user.service";
|
||||
import { AuthService } from "services/auth.service";
|
||||
// ui
|
||||
import { Avatar, Loader } from "@plane/ui";
|
||||
@ -47,30 +45,26 @@ const profileLinks = (workspaceSlug: string, userId: string) => [
|
||||
},
|
||||
];
|
||||
|
||||
const userService = new UserService();
|
||||
const authService = new AuthService();
|
||||
|
||||
export const WorkspaceSidebarDropdown = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { theme: themeStore, workspace: workspaceStore } = useMobxStore();
|
||||
|
||||
const { theme: themeStore, workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const { workspaces, currentWorkspace: activeWorkspace } = workspaceStore;
|
||||
|
||||
const { user, mutateUser } = useUser();
|
||||
const user = userStore.currentUser;
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleWorkspaceNavigation = (workspace: IWorkspace) => {
|
||||
userService
|
||||
.updateUser({
|
||||
userStore
|
||||
.updateCurrentUser({
|
||||
last_workspace_id: workspace?.id,
|
||||
})
|
||||
.then(() => {
|
||||
mutateUser();
|
||||
router.push(`/${workspace.slug}/`);
|
||||
})
|
||||
.catch(() =>
|
||||
@ -86,7 +80,6 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
||||
await authService
|
||||
.signOut()
|
||||
.then(() => {
|
||||
mutateUser(undefined);
|
||||
router.push("/");
|
||||
setTheme("system");
|
||||
})
|
||||
|
@ -1,61 +0,0 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import { ProjectService } from "services/project";
|
||||
// keys
|
||||
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
||||
// types
|
||||
import { IProjectMember } from "types";
|
||||
|
||||
type ContextType = {
|
||||
loading: boolean;
|
||||
memberDetails?: IProjectMember;
|
||||
error: any;
|
||||
};
|
||||
|
||||
// services
|
||||
const projectService = new ProjectService();
|
||||
|
||||
export const ProjectMemberContext = createContext<ContextType>({} as ContextType);
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const ProjectMemberProvider: React.FC<Props> = (props) => {
|
||||
const { children } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { data: memberDetails, error } = useSWR(
|
||||
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const loading = !memberDetails && !error;
|
||||
|
||||
return (
|
||||
<ProjectMemberContext.Provider value={{ loading, memberDetails, error }}>{children}</ProjectMemberContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useProjectMyMembership = () => {
|
||||
const context = useContext(ProjectMemberContext);
|
||||
|
||||
if (context === undefined) throw new Error(`useProjectMember must be used within a ProjectMemberProvider.`);
|
||||
|
||||
return {
|
||||
...context,
|
||||
memberRole: {
|
||||
isOwner: context.memberDetails?.role === 20,
|
||||
isMember: context.memberDetails?.role === 15,
|
||||
isViewer: context.memberDetails?.role === 10,
|
||||
isGuest: context.memberDetails?.role === 5,
|
||||
},
|
||||
};
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
// hooks
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
// fetch-keys
|
||||
import { PROJECT_ISSUES_LIST_WITH_PARAMS } from "constants/fetch-keys";
|
||||
|
||||
const issueService = new IssueService();
|
||||
|
||||
const useGanttChartIssues = (workspaceSlug: string | undefined, projectId: string | undefined) => {
|
||||
const { displayFilters, filters } = useIssuesView();
|
||||
|
||||
const params: any = {
|
||||
order_by: displayFilters.order_by,
|
||||
type: displayFilters?.type ? displayFilters?.type : undefined,
|
||||
sub_issue: displayFilters.sub_issue,
|
||||
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
||||
state: filters?.state ? filters?.state.join(",") : undefined,
|
||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||
start_target_date: true, // to fetch only issues with a start and target date
|
||||
};
|
||||
|
||||
// all issues under the workspace and project
|
||||
const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => issueService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
return {
|
||||
ganttIssues,
|
||||
mutateGanttIssues,
|
||||
params,
|
||||
};
|
||||
};
|
||||
|
||||
export default useGanttChartIssues;
|
@ -1,178 +0,0 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
import useSWR, { mutate } from "swr";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// types
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueFilterOptions,
|
||||
IWorkspaceMemberMe,
|
||||
IWorkspaceViewProps,
|
||||
Properties,
|
||||
} from "types";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_MEMBERS_ME } from "constants/fetch-keys";
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
const initialValues: IWorkspaceViewProps = {
|
||||
display_filters: {
|
||||
group_by: null,
|
||||
layout: "list",
|
||||
order_by: "-created_at",
|
||||
show_empty_groups: true,
|
||||
sub_issue: true,
|
||||
type: null,
|
||||
},
|
||||
display_properties: {
|
||||
assignee: true,
|
||||
start_date: true,
|
||||
due_date: true,
|
||||
key: true,
|
||||
labels: true,
|
||||
priority: true,
|
||||
state: true,
|
||||
sub_issue_count: true,
|
||||
attachment_count: true,
|
||||
link: true,
|
||||
estimate: true,
|
||||
created_on: true,
|
||||
updated_on: true,
|
||||
},
|
||||
filters: {
|
||||
assignees: null,
|
||||
created_by: null,
|
||||
labels: null,
|
||||
priority: null,
|
||||
state_group: null,
|
||||
subscriber: null,
|
||||
start_date: null,
|
||||
target_date: null,
|
||||
},
|
||||
};
|
||||
|
||||
const useMyIssuesFilters = (workspaceSlug: string | undefined) => {
|
||||
const { data: myWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug as string) : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceMemberMe(workspaceSlug as string) : null,
|
||||
{
|
||||
shouldRetryOnError: false,
|
||||
}
|
||||
);
|
||||
|
||||
const saveData = useCallback(
|
||||
(data: Partial<IWorkspaceViewProps>) => {
|
||||
if (!workspaceSlug || !myWorkspace) return;
|
||||
|
||||
const oldData = { ...myWorkspace };
|
||||
|
||||
mutate<IWorkspaceMemberMe>(
|
||||
WORKSPACE_MEMBERS_ME(workspaceSlug.toString()),
|
||||
(prevData) => {
|
||||
if (!prevData) return;
|
||||
return {
|
||||
...prevData,
|
||||
view_props: {
|
||||
...prevData?.view_props,
|
||||
...data,
|
||||
},
|
||||
};
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
workspaceService.updateWorkspaceView(workspaceSlug, {
|
||||
view_props: {
|
||||
...oldData.view_props,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
},
|
||||
[myWorkspace, workspaceSlug]
|
||||
);
|
||||
|
||||
const groupBy = (myWorkspace?.view_props ?? initialValues).display_filters?.group_by;
|
||||
const filters = (myWorkspace?.view_props ?? initialValues).filters;
|
||||
|
||||
const setDisplayFilters = useCallback(
|
||||
(displayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
const payload: Partial<IWorkspaceViewProps> = {
|
||||
display_filters: {
|
||||
...myWorkspace?.view_props?.display_filters,
|
||||
...displayFilter,
|
||||
},
|
||||
};
|
||||
|
||||
if (displayFilter.layout && displayFilter.layout === "kanban" && groupBy === null && payload.display_filters)
|
||||
payload.display_filters.group_by = "state_detail.group";
|
||||
|
||||
saveData(payload);
|
||||
},
|
||||
[groupBy, myWorkspace?.view_props.display_filters, saveData]
|
||||
);
|
||||
|
||||
const setProperty = useCallback(
|
||||
(key: keyof Properties) => {
|
||||
if (!myWorkspace?.view_props.display_properties) return;
|
||||
|
||||
saveData({
|
||||
display_properties: {
|
||||
...myWorkspace.view_props?.display_properties,
|
||||
[key]: !myWorkspace.view_props?.display_properties[key],
|
||||
},
|
||||
});
|
||||
},
|
||||
[myWorkspace, saveData]
|
||||
);
|
||||
|
||||
const setFilters = useCallback(
|
||||
(updatedFilter: Partial<IIssueFilterOptions>) => {
|
||||
if (!myWorkspace) return;
|
||||
|
||||
saveData({
|
||||
filters: {
|
||||
...myWorkspace.view_props?.filters,
|
||||
...updatedFilter,
|
||||
},
|
||||
});
|
||||
},
|
||||
[myWorkspace, saveData]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!myWorkspace || !workspaceSlug) return;
|
||||
|
||||
if (!myWorkspace.view_props) {
|
||||
workspaceService.updateWorkspaceView(workspaceSlug, {
|
||||
view_props: { ...initialValues },
|
||||
});
|
||||
}
|
||||
}, [myWorkspace, workspaceSlug]);
|
||||
|
||||
const newProperties: Properties = {
|
||||
assignee: myWorkspace?.view_props.display_properties?.assignee ?? true,
|
||||
start_date: myWorkspace?.view_props.display_properties?.start_date ?? true,
|
||||
due_date: myWorkspace?.view_props.display_properties?.due_date ?? true,
|
||||
key: myWorkspace?.view_props.display_properties?.key ?? true,
|
||||
labels: myWorkspace?.view_props.display_properties?.labels ?? true,
|
||||
priority: myWorkspace?.view_props.display_properties?.priority ?? true,
|
||||
state: myWorkspace?.view_props.display_properties?.state ?? true,
|
||||
sub_issue_count: myWorkspace?.view_props.display_properties?.sub_issue_count ?? true,
|
||||
attachment_count: myWorkspace?.view_props.display_properties?.attachment_count ?? true,
|
||||
link: myWorkspace?.view_props.display_properties?.link ?? true,
|
||||
estimate: myWorkspace?.view_props.display_properties?.estimate ?? true,
|
||||
created_on: myWorkspace?.view_props.display_properties?.created_on ?? true,
|
||||
updated_on: myWorkspace?.view_props.display_properties?.updated_on ?? true,
|
||||
};
|
||||
|
||||
return {
|
||||
displayFilters: myWorkspace?.view_props?.display_filters,
|
||||
setDisplayFilters,
|
||||
properties: newProperties,
|
||||
setProperty,
|
||||
filters,
|
||||
setFilters,
|
||||
};
|
||||
};
|
||||
|
||||
export default useMyIssuesFilters;
|
@ -1,87 +0,0 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import { UserService } from "services/user.service";
|
||||
// hooks
|
||||
import useMyIssuesFilters from "hooks/my-issues/use-my-issues-filter";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
// fetch-keys
|
||||
import { USER_ISSUES } from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
const userService = new UserService();
|
||||
|
||||
const useMyIssues = (workspaceSlug: string | undefined) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { filters, displayFilters } = useMyIssuesFilters(workspaceSlug);
|
||||
|
||||
const params: any = {
|
||||
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||
group_by: displayFilters?.group_by,
|
||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||
order_by: displayFilters?.order_by,
|
||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
||||
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
||||
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||
type: displayFilters?.type,
|
||||
};
|
||||
|
||||
const { data: myIssues, mutate: mutateMyIssues } = useSWR(
|
||||
workspaceSlug && router.pathname.includes("my-issues") ? USER_ISSUES(workspaceSlug.toString(), params) : null,
|
||||
workspaceSlug && router.pathname.includes("my-issues")
|
||||
? () => userService.userIssues(workspaceSlug.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
const groupedIssues:
|
||||
| {
|
||||
[key: string]: IIssue[];
|
||||
}
|
||||
| undefined = useMemo(() => {
|
||||
if (!myIssues) return undefined;
|
||||
|
||||
if (Array.isArray(myIssues))
|
||||
return {
|
||||
allIssues: myIssues,
|
||||
};
|
||||
|
||||
if (displayFilters?.group_by === "state_detail.group") {
|
||||
return myIssues
|
||||
? Object.assign(
|
||||
{
|
||||
backlog: [],
|
||||
unstarted: [],
|
||||
started: [],
|
||||
completed: [],
|
||||
cancelled: [],
|
||||
},
|
||||
myIssues
|
||||
)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
return myIssues;
|
||||
}, [displayFilters, myIssues]);
|
||||
|
||||
const isEmpty =
|
||||
Object.values(groupedIssues ?? {}).every((group) => group.length === 0) ||
|
||||
Object.keys(groupedIssues ?? {}).length === 0;
|
||||
|
||||
return {
|
||||
groupedIssues,
|
||||
isEmpty,
|
||||
mutateMyIssues,
|
||||
params,
|
||||
};
|
||||
};
|
||||
|
||||
export default useMyIssues;
|
@ -1,19 +1,15 @@
|
||||
import { IMentionHighlight, IMentionSuggestion } from "@plane/rich-text-editor";
|
||||
import useProjectMembers from "./use-project-members";
|
||||
import useUser from "./use-user";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { RootStore } from "store/root";
|
||||
|
||||
const useEditorSuggestions = (
|
||||
_workspaceSlug: string | undefined,
|
||||
_projectId: string | undefined,
|
||||
) => {
|
||||
const { mentionsStore }: RootStore = useMobxStore()
|
||||
const useEditorSuggestions = (_workspaceSlug: string | undefined, _projectId: string | undefined) => {
|
||||
const { mentionsStore }: RootStore = useMobxStore();
|
||||
|
||||
return {
|
||||
mentionSuggestions: mentionsStore.mentionSuggestions,
|
||||
mentionHighlights: mentionsStore.mentionHighlights
|
||||
}
|
||||
return {
|
||||
mentionSuggestions: mentionsStore.mentionSuggestions,
|
||||
mentionHighlights: mentionsStore.mentionHighlights,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEditorSuggestions;
|
||||
export default useEditorSuggestions;
|
||||
|
@ -1,211 +0,0 @@
|
||||
import { useContext, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
// contexts
|
||||
import { issueViewContext } from "contexts/issue-view.context";
|
||||
// services
|
||||
import { IssueService, IssueArchiveService, IssueDraftService } from "services/issue";
|
||||
import { CycleService } from "services/cycle.service";
|
||||
import { ModuleService } from "services/module.service";
|
||||
import { ProjectStateService } from "services/project";
|
||||
// helpers
|
||||
import { getStatesList } from "helpers/state.helper";
|
||||
// types
|
||||
import type { IIssue } from "types";
|
||||
// fetch-keys
|
||||
import {
|
||||
CYCLE_ISSUES_WITH_PARAMS,
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
PROJECT_ARCHIVED_ISSUES_LIST_WITH_PARAMS,
|
||||
PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
STATES_LIST,
|
||||
VIEW_ISSUES,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
// services
|
||||
const issueService = new IssueService();
|
||||
const issueArchiveService = new IssueArchiveService();
|
||||
const issueDraftService = new IssueDraftService();
|
||||
const cycleService = new CycleService();
|
||||
const moduleService = new ModuleService();
|
||||
const projectStateService = new ProjectStateService();
|
||||
|
||||
const useIssuesView = () => {
|
||||
const {
|
||||
display_filters: displayFilters,
|
||||
setDisplayFilters,
|
||||
filters,
|
||||
setFilters,
|
||||
resetFilterToDefault,
|
||||
setNewFilterDefaultView,
|
||||
} = useContext(issueViewContext);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId, archivedIssueId } = router.query;
|
||||
const isArchivedIssues = router.pathname.includes("archived-issues");
|
||||
const isDraftIssues = router.pathname.includes("draft-issues");
|
||||
|
||||
const params: any = {
|
||||
order_by: displayFilters?.order_by,
|
||||
group_by: displayFilters?.group_by,
|
||||
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
||||
mentions: filters?.mentions ? filters?.mentions.join(",") : undefined,
|
||||
state: filters?.state ? filters?.state.join(",") : undefined,
|
||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||
type: !isArchivedIssues ? (displayFilters?.type ? displayFilters?.type : undefined) : undefined,
|
||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||
sub_issue: displayFilters?.sub_issue,
|
||||
};
|
||||
|
||||
const { data: projectIssues, mutate: mutateProjectIssues } = useSWR(
|
||||
workspaceSlug && projectId && params ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params) : null,
|
||||
workspaceSlug && projectId && params
|
||||
? () => issueService.getIssuesWithParams(workspaceSlug as string, projectId as string, params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: projectArchivedIssues, mutate: mutateProjectArchivedIssues } = useSWR(
|
||||
workspaceSlug && projectId && params && isArchivedIssues && !archivedIssueId
|
||||
? PROJECT_ARCHIVED_ISSUES_LIST_WITH_PARAMS(projectId as string, params)
|
||||
: null,
|
||||
workspaceSlug && projectId && params && isArchivedIssues && !archivedIssueId
|
||||
? () => issueArchiveService.getArchivedIssues(workspaceSlug as string, projectId as string, params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: draftIssues, mutate: mutateDraftIssues } = useSWR(
|
||||
workspaceSlug && projectId && params && isDraftIssues && !archivedIssueId
|
||||
? PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(projectId as string, params)
|
||||
: null,
|
||||
workspaceSlug && projectId && params && isDraftIssues && !archivedIssueId
|
||||
? () => issueDraftService.getDraftIssues(workspaceSlug as string, projectId as string, params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: cycleIssues, mutate: mutateCycleIssues } = useSWR(
|
||||
workspaceSlug && projectId && cycleId && params ? CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params) : null,
|
||||
workspaceSlug && projectId && cycleId && params
|
||||
? () =>
|
||||
cycleService.getCycleIssuesWithParams(workspaceSlug as string, projectId as string, cycleId as string, params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: moduleIssues, mutate: mutateModuleIssues } = useSWR(
|
||||
workspaceSlug && projectId && moduleId && params ? MODULE_ISSUES_WITH_PARAMS(moduleId as string, params) : null,
|
||||
workspaceSlug && projectId && moduleId && params
|
||||
? () =>
|
||||
moduleService.getModuleIssuesWithParams(
|
||||
workspaceSlug as string,
|
||||
projectId as string,
|
||||
moduleId as string,
|
||||
params
|
||||
)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: viewIssues, mutate: mutateViewIssues } = useSWR(
|
||||
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
|
||||
workspaceSlug && projectId && viewId && params
|
||||
? () => issueService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: states } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectStateService.getStates(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
);
|
||||
const statesList = getStatesList(states);
|
||||
const activeStatesList = statesList?.filter((state) => state.group === "started" || state.group === "unstarted");
|
||||
const backlogStatesList = statesList?.filter((state) => state.group === "backlog");
|
||||
|
||||
const stateIds =
|
||||
displayFilters && displayFilters?.type === "active"
|
||||
? activeStatesList?.map((state) => state.id)
|
||||
: displayFilters?.type === "backlog"
|
||||
? backlogStatesList?.map((state) => state.id)
|
||||
: statesList?.map((state) => state.id);
|
||||
|
||||
const filteredStateIds =
|
||||
(filters && filters?.state ? stateIds?.filter((s) => filters.state?.includes(s)) : stateIds) ?? [];
|
||||
|
||||
const emptyStatesObject: { [key: string]: [] } = {};
|
||||
for (let i = 0; i < filteredStateIds.length; i++) {
|
||||
emptyStatesObject[filteredStateIds[i]] = [];
|
||||
}
|
||||
|
||||
const groupedByIssues:
|
||||
| {
|
||||
[key: string]: IIssue[];
|
||||
}
|
||||
| undefined = useMemo(() => {
|
||||
const issuesToGroup = cycleId
|
||||
? cycleIssues
|
||||
: moduleId
|
||||
? moduleIssues
|
||||
: viewId
|
||||
? viewIssues
|
||||
: isArchivedIssues
|
||||
? projectArchivedIssues
|
||||
: isDraftIssues
|
||||
? draftIssues
|
||||
: projectIssues;
|
||||
|
||||
if (Array.isArray(issuesToGroup)) return { allIssues: issuesToGroup };
|
||||
if (displayFilters?.group_by === "state")
|
||||
return issuesToGroup ? Object.assign(emptyStatesObject, issuesToGroup) : undefined;
|
||||
|
||||
return issuesToGroup;
|
||||
}, [
|
||||
displayFilters?.group_by,
|
||||
projectIssues,
|
||||
cycleIssues,
|
||||
moduleIssues,
|
||||
viewIssues,
|
||||
projectArchivedIssues,
|
||||
cycleId,
|
||||
moduleId,
|
||||
viewId,
|
||||
isArchivedIssues,
|
||||
isDraftIssues,
|
||||
draftIssues,
|
||||
emptyStatesObject,
|
||||
]);
|
||||
|
||||
const isEmpty =
|
||||
Object.values(groupedByIssues ?? {}).every((group) => group.length === 0) ||
|
||||
Object.keys(groupedByIssues ?? {}).length === 0;
|
||||
|
||||
return {
|
||||
displayFilters: {
|
||||
...displayFilters,
|
||||
layout: isArchivedIssues ? "list" : displayFilters?.layout,
|
||||
},
|
||||
setDisplayFilters,
|
||||
groupedByIssues,
|
||||
mutateIssues: cycleId
|
||||
? mutateCycleIssues
|
||||
: moduleId
|
||||
? mutateModuleIssues
|
||||
: viewId
|
||||
? mutateViewIssues
|
||||
: isArchivedIssues
|
||||
? mutateProjectArchivedIssues
|
||||
: isDraftIssues
|
||||
? mutateDraftIssues
|
||||
: mutateProjectIssues,
|
||||
filters,
|
||||
setFilters,
|
||||
params,
|
||||
isEmpty,
|
||||
resetFilterToDefault,
|
||||
setNewFilterDefaultView,
|
||||
} as const;
|
||||
};
|
||||
|
||||
export default useIssuesView;
|
@ -1,123 +0,0 @@
|
||||
import { useContext, useEffect, useMemo } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import { UserService } from "services/user.service";
|
||||
// contexts
|
||||
import { profileIssuesContext } from "contexts/profile-issues-context";
|
||||
import { useWorkspaceMyMembership } from "contexts/workspace-member.context";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
// fetch-keys
|
||||
import { USER_PROFILE_ISSUES } from "constants/fetch-keys";
|
||||
|
||||
const userService = new UserService();
|
||||
|
||||
const useProfileIssues = (workspaceSlug: string | undefined, userId: string | undefined) => {
|
||||
const {
|
||||
display_filters: displayFilters,
|
||||
setDisplayFilters,
|
||||
filters,
|
||||
setFilters,
|
||||
display_properties: displayProperties,
|
||||
setProperties,
|
||||
} = useContext(profileIssuesContext);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { memberRole } = useWorkspaceMyMembership();
|
||||
|
||||
const params: any = {
|
||||
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
|
||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||
group_by: displayFilters?.group_by,
|
||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||
order_by: displayFilters?.order_by,
|
||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
||||
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||
type: displayFilters?.type ? displayFilters?.type : undefined,
|
||||
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
||||
};
|
||||
|
||||
const { data: userProfileIssues, mutate: mutateProfileIssues } = useSWR(
|
||||
workspaceSlug && userId && (memberRole.isOwner || memberRole.isMember || memberRole.isViewer)
|
||||
? USER_PROFILE_ISSUES(workspaceSlug.toString(), userId.toString(), params)
|
||||
: null,
|
||||
workspaceSlug && userId && (memberRole.isOwner || memberRole.isMember || memberRole.isViewer)
|
||||
? () => userService.getUserProfileIssues(workspaceSlug.toString(), userId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
const groupedIssues:
|
||||
| {
|
||||
[key: string]: IIssue[];
|
||||
}
|
||||
| undefined = useMemo(() => {
|
||||
if (!userProfileIssues) return undefined;
|
||||
|
||||
if (Array.isArray(userProfileIssues))
|
||||
return {
|
||||
allIssues: userProfileIssues,
|
||||
};
|
||||
|
||||
if (displayFilters?.group_by === "state_detail.group") {
|
||||
return userProfileIssues
|
||||
? Object.assign(
|
||||
{
|
||||
backlog: [],
|
||||
unstarted: [],
|
||||
started: [],
|
||||
completed: [],
|
||||
cancelled: [],
|
||||
},
|
||||
userProfileIssues
|
||||
)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
return userProfileIssues;
|
||||
}, [displayFilters?.group_by, userProfileIssues]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userId || !filters) return;
|
||||
|
||||
if (router.pathname.includes("assigned") && (!filters.assignees || !filters.assignees.includes(userId))) {
|
||||
setFilters({ assignees: [...(filters.assignees ?? []), userId] });
|
||||
return;
|
||||
}
|
||||
|
||||
if (router.pathname.includes("created") && (!filters.created_by || !filters.created_by.includes(userId))) {
|
||||
setFilters({ created_by: [...(filters.created_by ?? []), userId] });
|
||||
return;
|
||||
}
|
||||
|
||||
if (router.pathname.includes("subscribed") && filters.subscriber === null) {
|
||||
setFilters({ subscriber: [userId] });
|
||||
return;
|
||||
}
|
||||
}, [filters, router, setFilters, userId]);
|
||||
|
||||
const isEmpty =
|
||||
Object.values(groupedIssues ?? {}).every((group) => group.length === 0) ||
|
||||
Object.keys(groupedIssues ?? {}).length === 0;
|
||||
|
||||
return {
|
||||
groupedIssues,
|
||||
displayFilters,
|
||||
setDisplayFilters,
|
||||
filters,
|
||||
setFilters,
|
||||
displayProperties,
|
||||
setProperties,
|
||||
isEmpty,
|
||||
mutateProfileIssues,
|
||||
params,
|
||||
};
|
||||
};
|
||||
|
||||
export default useProfileIssues;
|
@ -1,44 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
// services
|
||||
import { ProjectService } from "services/project";
|
||||
// fetch-keys
|
||||
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||
// hooks
|
||||
import useUser from "./use-user";
|
||||
|
||||
const projectService = new ProjectService();
|
||||
|
||||
const useProjectMembers = (
|
||||
workspaceSlug: string | undefined,
|
||||
projectId: string | undefined,
|
||||
fetchCondition?: boolean
|
||||
) => {
|
||||
fetchCondition = fetchCondition ?? true;
|
||||
|
||||
const { user } = useUser();
|
||||
// fetching project members
|
||||
const { data: members } = useSWR(
|
||||
workspaceSlug && projectId && fetchCondition ? PROJECT_MEMBERS(projectId) : null,
|
||||
workspaceSlug && projectId && fetchCondition
|
||||
? () => projectService.fetchProjectMembers(workspaceSlug, projectId)
|
||||
: null
|
||||
);
|
||||
|
||||
const hasJoined = members?.some((item: any) => item.member.id === (user as any)?.id);
|
||||
|
||||
const isOwner = members?.some((item) => item.member.id === (user as any)?.id && item.role === 20);
|
||||
const isMember = members?.some((item) => item.member.id === (user as any)?.id && item.role === 15);
|
||||
const isViewer = members?.some((item) => item.member.id === (user as any)?.id && item.role === 10);
|
||||
const isGuest = members?.some((item) => item.member.id === (user as any)?.id && item.role === 5);
|
||||
|
||||
return {
|
||||
members,
|
||||
hasJoined,
|
||||
isOwner,
|
||||
isMember,
|
||||
isViewer,
|
||||
isGuest,
|
||||
};
|
||||
};
|
||||
|
||||
export default useProjectMembers;
|
@ -1,39 +0,0 @@
|
||||
import useSWR from "swr";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||
// hooks
|
||||
import useUser from "./use-user";
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
const useWorkspaceMembers = (workspaceSlug: string | undefined, fetchCondition?: boolean) => {
|
||||
fetchCondition = fetchCondition ?? true;
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const { data: workspaceMembers, error: workspaceMemberErrors } = useSWR(
|
||||
workspaceSlug && fetchCondition ? WORKSPACE_MEMBERS(workspaceSlug) : null,
|
||||
workspaceSlug && fetchCondition ? () => workspaceService.fetchWorkspaceMembers(workspaceSlug) : null
|
||||
);
|
||||
|
||||
const hasJoined = workspaceMembers?.some((item: any) => item.member.id === (user as any)?.id);
|
||||
|
||||
const isOwner = workspaceMembers?.some((item) => item.member.id === (user as any)?.id && item.role === 20);
|
||||
const isMember = workspaceMembers?.some((item) => item.member.id === (user as any)?.id && item.role === 15);
|
||||
const isViewer = workspaceMembers?.some((item) => item.member.id === (user as any)?.id && item.role === 10);
|
||||
const isGuest = workspaceMembers?.some((item) => item.member.id === (user as any)?.id && item.role === 5);
|
||||
|
||||
return {
|
||||
workspaceMembers,
|
||||
workspaceMemberErrors,
|
||||
hasJoined,
|
||||
isOwner,
|
||||
isMember,
|
||||
isViewer,
|
||||
isGuest,
|
||||
};
|
||||
};
|
||||
|
||||
export default useWorkspaceMembers;
|
@ -2,9 +2,9 @@ import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import useUser from "hooks/use-user";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
import { UserAuthWrapper } from "layouts/auth-layout";
|
||||
@ -17,7 +17,7 @@ import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-l
|
||||
import { IWorkspace } from "types";
|
||||
import type { NextPage } from "next";
|
||||
|
||||
const CreateWorkspace: NextPage = () => {
|
||||
const CreateWorkspace: NextPage = observer(() => {
|
||||
const [defaultValues, setDefaultValues] = useState({
|
||||
name: "",
|
||||
slug: "",
|
||||
@ -27,11 +27,10 @@ const CreateWorkspace: NextPage = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const user = userStore.currentUser;
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const onSubmit = async (workspace: IWorkspace) => {
|
||||
await userStore
|
||||
.updateCurrentUser({ last_workspace_id: workspace.id })
|
||||
@ -76,6 +75,6 @@ const CreateWorkspace: NextPage = () => {
|
||||
</DefaultLayout>
|
||||
</UserAuthWrapper>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default CreateWorkspace;
|
@ -6,7 +6,7 @@ import { UserService } from "services/user.service";
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// interfaces
|
||||
import { IUser, IUserSettings } from "types/users";
|
||||
import { IWorkspaceMemberMe, IProjectMember } from "types";
|
||||
import { IWorkspaceMemberMe, IProjectMember, TUserProjectRole, TUserWorkspaceRole } from "types";
|
||||
import { RootStore } from "./root";
|
||||
|
||||
export interface IUserStore {
|
||||
@ -34,8 +34,8 @@ export interface IUserStore {
|
||||
|
||||
currentProjectMemberInfo: IProjectMember | undefined;
|
||||
currentWorkspaceMemberInfo: IWorkspaceMemberMe | undefined;
|
||||
currentProjectRole: number | undefined;
|
||||
currentWorkspaceRole: number | undefined;
|
||||
currentProjectRole: TUserProjectRole | undefined;
|
||||
currentWorkspaceRole: TUserWorkspaceRole | undefined;
|
||||
|
||||
hasPermissionToCurrentWorkspace: boolean | undefined;
|
||||
hasPermissionToCurrentProject: boolean | undefined;
|
||||
@ -261,7 +261,15 @@ class UserStore implements IUserStore {
|
||||
|
||||
updateCurrentUser = async (data: Partial<IUser>) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.currentUser = {
|
||||
...this.currentUser,
|
||||
...data,
|
||||
} as IUser;
|
||||
});
|
||||
|
||||
const response = await this.userService.updateUser(data);
|
||||
|
||||
runInAction(() => {
|
||||
this.currentUser = response;
|
||||
});
|
||||
|
10
web/types/projects.d.ts
vendored
10
web/types/projects.d.ts
vendored
@ -1,5 +1,7 @@
|
||||
import type { IUserLite, IWorkspace, IWorkspaceLite, IUserMemberLite, TStateGroups, IProjectViewProps } from ".";
|
||||
|
||||
export type TUserProjectRole = 5 | 10 | 15 | 20;
|
||||
|
||||
export interface IProject {
|
||||
archive_in: number;
|
||||
close_in: number;
|
||||
@ -32,7 +34,7 @@ export interface IProject {
|
||||
is_deployed: boolean;
|
||||
is_favorite: boolean;
|
||||
is_member: boolean;
|
||||
member_role: 5 | 10 | 15 | 20 | null;
|
||||
member_role: TUserProjectRole | null;
|
||||
members: IProjectMemberLite[];
|
||||
issue_views_view: boolean;
|
||||
module_view: boolean;
|
||||
@ -76,7 +78,7 @@ export interface IProjectMember {
|
||||
project: IProjectLite;
|
||||
workspace: IWorkspaceLite;
|
||||
comment: string;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserProjectRole;
|
||||
|
||||
preferences: ProjectPreferences;
|
||||
|
||||
@ -100,7 +102,7 @@ export interface IProjectMemberInvitation {
|
||||
token: string;
|
||||
message: string;
|
||||
responded_at: Date;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserProjectRole;
|
||||
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
@ -109,7 +111,7 @@ export interface IProjectMemberInvitation {
|
||||
}
|
||||
|
||||
export interface IProjectBulkAddFormData {
|
||||
members: { role: 5 | 10 | 15 | 20; member_id: string }[];
|
||||
members: { role: TUserProjectRole; member_id: string }[];
|
||||
}
|
||||
|
||||
export interface IGithubRepository {
|
||||
|
10
web/types/workspace.d.ts
vendored
10
web/types/workspace.d.ts
vendored
@ -1,5 +1,7 @@
|
||||
import type { IProjectMember, IUser, IUserLite, IUserMemberLite, IWorkspaceViewProps } from "types";
|
||||
|
||||
export type TUserWorkspaceRole = 5 | 10 | 15 | 20;
|
||||
|
||||
export interface IWorkspace {
|
||||
readonly id: string;
|
||||
readonly owner: IUser;
|
||||
@ -30,13 +32,13 @@ export interface IWorkspaceMemberInvitation {
|
||||
token: string;
|
||||
message: string;
|
||||
responded_at: Date;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
created_by_detail: IUser;
|
||||
workspace: IWorkspace;
|
||||
}
|
||||
|
||||
export interface IWorkspaceBulkInviteFormData {
|
||||
emails: { email: string; role: 5 | 10 | 15 | 20 }[];
|
||||
emails: { email: string; role: TUserWorkspaceRole }[];
|
||||
}
|
||||
|
||||
export type Properties = {
|
||||
@ -61,7 +63,7 @@ export interface IWorkspaceMember {
|
||||
created_by: string;
|
||||
id: string;
|
||||
member: IUserLite;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
updated_at: Date;
|
||||
updated_by: string;
|
||||
workspace: IWorkspaceLite;
|
||||
@ -74,7 +76,7 @@ export interface IWorkspaceMemberMe {
|
||||
default_props: IWorkspaceViewProps;
|
||||
id: string;
|
||||
member: string;
|
||||
role: 5 | 10 | 15 | 20;
|
||||
role: TUserWorkspaceRole;
|
||||
updated_at: Date;
|
||||
updated_by: string;
|
||||
view_props: IWorkspaceViewProps;
|
||||
|
Loading…
Reference in New Issue
Block a user