chore: inbox issue update

This commit is contained in:
LAKHAN BAHETI 2023-12-04 21:09:59 +05:30
parent aeb2c0383b
commit 42f2d346bf
6 changed files with 92 additions and 10 deletions

View File

@ -3,15 +3,17 @@ import Router, { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import useSWR from "swr"; import useSWR from "swr";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, XCircle } from "lucide-react"; import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, RefreshCw, XCircle } from "lucide-react";
// mobx store // mobx store
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { IssueDescriptionForm, IssueDetailsSidebar, IssueReaction } from "components/issues"; import { IssueDescriptionForm, IssueDetailsSidebar, IssueReaction } from "components/issues";
import { InboxIssueActivity } from "components/inbox"; import { InboxIssueActivity } from "components/inbox";
// hooks
import useReloadConfirmations from "hooks/use-reload-confirmation";
// ui // ui
import { Loader } from "@plane/ui"; import { Loader, StateGroupIcon } from "@plane/ui";
// helpers // helpers
import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
// types // types
@ -30,11 +32,18 @@ export const InboxMainContent: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query; const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query;
const { inboxIssues: inboxIssuesStore, inboxIssueDetails: inboxIssueDetailsStore, user: userStore } = useMobxStore(); const {
inboxIssues: inboxIssuesStore,
inboxIssueDetails: inboxIssueDetailsStore,
user: userStore,
projectState: { states },
} = useMobxStore();
const user = userStore.currentUser; const user = userStore.currentUser;
const userRole = userStore.currentProjectRole; const userRole = userStore.currentProjectRole;
const { setShowAlert } = useReloadConfirmations();
const { reset, control, watch } = useForm<IIssue>({ const { reset, control, watch } = useForm<IIssue>({
defaultValues, defaultValues,
}); });
@ -54,6 +63,9 @@ export const InboxMainContent: React.FC = observer(() => {
const issuesList = inboxId ? inboxIssuesStore.inboxIssues[inboxId.toString()] : undefined; const issuesList = inboxId ? inboxIssuesStore.inboxIssues[inboxId.toString()] : undefined;
const issueDetails = inboxIssueId ? inboxIssueDetailsStore.issueDetails[inboxIssueId.toString()] : undefined; const issueDetails = inboxIssueId ? inboxIssueDetailsStore.issueDetails[inboxIssueId.toString()] : undefined;
const currentIssueState = projectId
? states[projectId.toString()]?.find((s) => s.id === issueDetails?.state)
: undefined;
const submitChanges = useCallback( const submitChanges = useCallback(
async (formData: Partial<IInboxIssue>) => { async (formData: Partial<IInboxIssue>) => {
@ -124,6 +136,17 @@ export const InboxMainContent: React.FC = observer(() => {
}); });
}, [issueDetails, reset, inboxIssueId]); }, [issueDetails, reset, inboxIssueId]);
useEffect(() => {
if (inboxIssueDetailsStore.isSubmitting === "submitted") {
setShowAlert(false);
setTimeout(async () => {
inboxIssueDetailsStore.setIsSubmitting("saved");
}, 2000);
} else if (inboxIssueDetailsStore.isSubmitting === "submitting") {
setShowAlert(true);
}
}, [inboxIssueDetailsStore.isSubmitting, setShowAlert]);
const issueStatus = issueDetails?.issue_inbox[0].status; const issueStatus = issueDetails?.issue_inbox[0].status;
if (!inboxIssueId) if (!inboxIssueId)
@ -214,8 +237,36 @@ export const InboxMainContent: React.FC = observer(() => {
</> </>
) : null} ) : null}
</div> </div>
<div className="flex items-center mb-5">
{!currentIssueState ? (
<StateGroupIcon className="h-4 w-4 mr-3" stateGroup="backlog" color="#ff7700" />
) : (
<StateGroupIcon
className="h-4 w-4 mr-3"
stateGroup={currentIssueState.group}
color={currentIssueState.color}
/>
)}
<h4 className="text-lg text-custom-text-300 font-medium mr-4">
{issueDetails?.project_detail?.identifier}-{issueDetails?.sequence_id}
</h4>
<div
className={`flex transition-all duration-300 items-center gap-x-2 ${
inboxIssueDetailsStore.isSubmitting === "saved" ? "fadeOut" : "fadeIn"
}`}
>
{inboxIssueDetailsStore.isSubmitting !== "submitted" &&
inboxIssueDetailsStore.isSubmitting !== "saved" && (
<RefreshCw className="h-4 w-4 stroke-custom-text-300" />
)}
<span className="text-sm text-custom-text-300">
{inboxIssueDetailsStore.isSubmitting === "submitting" ? "Saving..." : "Saved"}
</span>
</div>
</div>
<div> <div>
<IssueDescriptionForm <IssueDescriptionForm
setShowAlert={setShowAlert}
workspaceSlug={workspaceSlug as string} workspaceSlug={workspaceSlug as string}
issue={{ issue={{
name: issueDetails.name, name: issueDetails.name,

View File

@ -11,6 +11,7 @@ import { IIssue } from "types";
// services // services
import { FileService } from "services/file.service"; import { FileService } from "services/file.service";
import useEditorSuggestions from "hooks/use-editor-suggestions"; import useEditorSuggestions from "hooks/use-editor-suggestions";
import { useRouter } from "next/router";
export interface IssueDescriptionFormValues { export interface IssueDescriptionFormValues {
name: string; name: string;
@ -36,9 +37,14 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
// states // states
const [characterLimit, setCharacterLimit] = useState(false); const [characterLimit, setCharacterLimit] = useState(false);
// router
const router = useRouter();
const { inboxId } = router.query;
// mobx store // mobx store
const { const {
projectIssues: { setIsSubmitting }, projectIssues: { setIsSubmitting: PIsetIsSubmitting },
inboxIssueDetails: { setIsSubmitting: IIsetIsSubmitting },
} = useMobxStore(); } = useMobxStore();
const editorSuggestion = useEditorSuggestions(); const editorSuggestion = useEditorSuggestions();
@ -89,6 +95,8 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
handleSubmit(handleDescriptionFormSubmit)(); handleSubmit(handleDescriptionFormSubmit)();
}, 1500); }, 1500);
const setIsSubmitting = inboxId ? IIsetIsSubmitting : PIsetIsSubmitting;
return ( return (
<div className="relative"> <div className="relative">
<div className="relative"> <div className="relative">

View File

@ -87,7 +87,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const userRole = userStore.currentProjectRole; const userRole = userStore.currentProjectRole;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query; const { workspaceSlug, projectId, issueId, inboxIssueId } = router.query;
const { isEstimateActive } = useEstimateOption(); const { isEstimateActive } = useEstimateOption();
@ -273,13 +273,15 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<div className="h-full w-full flex flex-col divide-y-2 divide-custom-border-200 overflow-hidden"> <div className="h-full w-full flex flex-col divide-y-2 divide-custom-border-200 overflow-hidden">
<div className="flex items-center justify-between px-5 pb-3"> <div className="flex items-center justify-between px-5 pb-3">
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
{currentIssueState && ( {currentIssueState ? (
<StateGroupIcon <StateGroupIcon
className="h-4 w-4" className="h-4 w-4"
stateGroup={currentIssueState.group} stateGroup={currentIssueState.group}
color={currentIssueState.color} color={currentIssueState.color}
/> />
)} ) : inboxIssueId ? (
<StateGroupIcon className="h-4 w-4" stateGroup="backlog" color="#ff7700" />
) : null}
<h4 className="text-lg text-custom-text-300 font-medium"> <h4 className="text-lg text-custom-text-300 font-medium">
{issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id} {issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
</h4> </h4>

View File

@ -50,6 +50,7 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => {
projectIssues: { isSubmitting, setIsSubmitting }, projectIssues: { isSubmitting, setIsSubmitting },
} = useMobxStore(); } = useMobxStore();
// hooks
const { setShowAlert } = useReloadConfirmations(); const { setShowAlert } = useReloadConfirmations();
const { const {

View File

@ -5,6 +5,7 @@ import { RootStore } from "../root";
import { InboxService } from "services/inbox.service"; import { InboxService } from "services/inbox.service";
// types // types
import { IInboxIssue, IIssue, TInboxStatus } from "types"; import { IInboxIssue, IIssue, TInboxStatus } from "types";
import { TIssueUpdateStatus } from "store/issues/types";
// constants // constants
import { INBOX_ISSUE_SOURCE } from "constants/inbox"; import { INBOX_ISSUE_SOURCE } from "constants/inbox";
@ -12,6 +13,7 @@ export interface IInboxIssueDetailsStore {
// states // states
loader: boolean; loader: boolean;
error: any | null; error: any | null;
isSubmitting: TIssueUpdateStatus; // inbox issue update status;
// observables // observables
issueDetails: { issueDetails: {
@ -46,12 +48,14 @@ export interface IInboxIssueDetailsStore {
data: TInboxStatus data: TInboxStatus
) => Promise<void>; ) => Promise<void>;
deleteIssue: (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => Promise<void>; deleteIssue: (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => Promise<void>;
setIsSubmitting: (status: TIssueUpdateStatus) => void;
} }
export class InboxIssueDetailsStore implements IInboxIssueDetailsStore { export class InboxIssueDetailsStore implements IInboxIssueDetailsStore {
// states // states
loader: boolean = false; loader: boolean = false;
error: any | null = null; error: any | null = null;
isSubmitting: TIssueUpdateStatus = "saved";
// observables // observables
issueDetails: { [issueId: string]: IInboxIssue } = {}; issueDetails: { [issueId: string]: IInboxIssue } = {};
@ -67,6 +71,7 @@ export class InboxIssueDetailsStore implements IInboxIssueDetailsStore {
// states // states
loader: observable.ref, loader: observable.ref,
error: observable.ref, error: observable.ref,
isSubmitting: observable.ref,
// observables // observables
issueDetails: observable.ref, issueDetails: observable.ref,
@ -76,12 +81,17 @@ export class InboxIssueDetailsStore implements IInboxIssueDetailsStore {
createIssue: action, createIssue: action,
updateIssueStatus: action, updateIssueStatus: action,
deleteIssue: action, deleteIssue: action,
setIsSubmitting: action,
}); });
this.rootStore = _rootStore; this.rootStore = _rootStore;
this.inboxService = new InboxService(); this.inboxService = new InboxService();
} }
setIsSubmitting = (status: TIssueUpdateStatus) => {
this.isSubmitting = status;
};
fetchIssueDetails = async (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => { fetchIssueDetails = async (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => {
try { try {
runInAction(() => { runInAction(() => {
@ -155,6 +165,7 @@ export class InboxIssueDetailsStore implements IInboxIssueDetailsStore {
try { try {
runInAction(() => { runInAction(() => {
this.isSubmitting = "submitting";
this.issueDetails = { this.issueDetails = {
...this.issueDetails, ...this.issueDetails,
[issueId]: updatedIssue, [issueId]: updatedIssue,
@ -170,6 +181,7 @@ export class InboxIssueDetailsStore implements IInboxIssueDetailsStore {
}); });
await this.inboxService.patchInboxIssue(workspaceSlug, projectId, inboxId, issueId, { issue: data }); await this.inboxService.patchInboxIssue(workspaceSlug, projectId, inboxId, issueId, { issue: data });
this.isSubmitting = "submitted";
} catch (error) { } catch (error) {
runInAction(() => { runInAction(() => {
this.error = error; this.error = error;

View File

@ -6,13 +6,21 @@ import { IssueService } from "services/issue/issue.service";
// types // types
import { TIssueGroupByOptions } from "types"; import { TIssueGroupByOptions } from "types";
import { IIssue } from "types/issues"; import { IIssue } from "types/issues";
import { IIssueResponse, TLoader, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues, ViewFlags, TIssueUpdateStatus } from "../../types"; import {
IIssueResponse,
TLoader,
IGroupedIssues,
ISubGroupedIssues,
TUnGroupedIssues,
ViewFlags,
TIssueUpdateStatus,
} from "../../types";
import { RootStore } from "store/root"; import { RootStore } from "store/root";
export interface IProjectIssuesStore { export interface IProjectIssuesStore {
// observable // observable
loader: TLoader; loader: TLoader;
isSubmitting: TIssueUpdateStatus; isSubmitting: TIssueUpdateStatus; // update issue status
issues: { [project_id: string]: IIssueResponse } | undefined; issues: { [project_id: string]: IIssueResponse } | undefined;
// computed // computed
getIssues: IIssueResponse | undefined; getIssues: IIssueResponse | undefined;