Compare commits

...

2 Commits

Author SHA1 Message Date
Dakshesh Jain
b9975dfa24 refactor: typed context and using observer hoc 2023-08-11 14:05:32 +05:30
Dakshesh Jain
380ad340a6 dev: label state management setup 2023-08-10 17:51:47 +05:30
6 changed files with 165 additions and 30 deletions

View File

@ -4,8 +4,13 @@ import { useRouter } from "next/router";
import { mutate } from "swr";
// mobx
import { useObserver } from "mobx-react-lite";
// react-hook-form
import { Controller, SubmitHandler, useForm } from "react-hook-form";
// store
import labelStore from "store/labels";
// hooks
import useUserAuth from "hooks/use-user-auth";
// react-color
@ -58,20 +63,17 @@ export const CreateUpdateLabelInline = forwardRef<Ref, Props>(function CreateUpd
defaultValues,
});
const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => {
if (!workspaceSlug || !projectId || isSubmitting) return;
const { createLabel } = useObserver(() => ({
createLabel: labelStore.createLabel,
}));
await issuesService
.createIssueLabel(workspaceSlug as string, projectId as string, formData, user)
.then((res) => {
mutate<IIssueLabels[]>(
PROJECT_ISSUE_LABELS(projectId as string),
(prevData) => [res, ...(prevData ?? [])],
false
);
reset(defaultValues);
setLabelForm(false);
});
const handleLabelCreate: SubmitHandler<IIssueLabels> = async (formData) => {
if (!workspaceSlug || !projectId || isSubmitting || !user) return;
await createLabel(workspaceSlug.toString(), projectId.toString(), formData, user).then(() => {
reset(defaultValues);
setLabelForm(false);
});
};
const handleLabelUpdate: SubmitHandler<IIssueLabels> = async (formData) => {

View File

@ -4,7 +4,7 @@ import { RootStore } from "store/root";
let rootStore: any = null;
export const MobxStoreContext = createContext(null);
export const MobxStoreContext = createContext<RootStore>({} as RootStore);
const initializeStore = () => {
const _rootStore = rootStore ?? new RootStore();

View File

@ -1,14 +1,17 @@
import React, { useState, useRef } from "react";
import React, { useState, useRef, useEffect } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx
import { observer } from "mobx-react-lite";
// hooks
import useUserAuth from "hooks/use-user-auth";
import { useMobxStore } from "lib/mobx/store-provider";
// services
import projectService from "services/project.service";
import issuesService from "services/issues.service";
// layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
// components
@ -31,7 +34,7 @@ import emptyLabel from "public/empty-state/label.svg";
import { IIssueLabels } from "types";
import type { NextPage } from "next";
// fetch-keys
import { PROJECT_DETAILS, PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
import { PROJECT_DETAILS } from "constants/fetch-keys";
// helper
import { truncateText } from "helpers/string.helper";
@ -57,6 +60,9 @@ const LabelsSettings: NextPage = () => {
const scrollToRef = useRef<HTMLDivElement>(null);
const { labelStore } = useMobxStore();
const { isLabelsLoading: isLoading, labels } = labelStore;
const { data: projectDetails } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId
@ -64,13 +70,6 @@ const LabelsSettings: NextPage = () => {
: null
);
const { data: issueLabels } = useSWR(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
workspaceSlug && projectId
? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
: null
);
const newLabel = () => {
setIsUpdating(false);
setLabelForm(true);
@ -87,6 +86,11 @@ const LabelsSettings: NextPage = () => {
setLabelToUpdate(label);
};
useEffect(() => {
if (workspaceSlug && projectId)
labelStore.loadLabels(workspaceSlug.toString(), projectId.toString());
}, [workspaceSlug, projectId, labelStore]);
return (
<>
<LabelsListModal
@ -137,10 +141,10 @@ const LabelsSettings: NextPage = () => {
/>
)}
<>
{issueLabels ? (
issueLabels.length > 0 ? (
issueLabels.map((label) => {
const children = issueLabels?.filter((l) => l.parent === label.id);
{!isLoading ? (
labels.length > 0 ? (
labels.map((label) => {
const children = labels?.filter((l) => l.parent === label.id);
if (children && children.length === 0) {
if (!label.parent)
@ -148,7 +152,17 @@ const LabelsSettings: NextPage = () => {
<SingleLabel
key={label.id}
label={label}
addLabelToGroup={() => addLabelToGroup(label)}
addLabelToGroup={() => {
addLabelToGroup(label);
// if (!workspaceSlug || !projectId || !user) return;
// labelStore.updateLabel(
// workspaceSlug.toString(),
// projectId.toString(),
// label.id,
// { parent: label.id },
// user
// );
}}
editLabel={(label) => {
editLabel(label);
scrollToRef.current?.scrollIntoView({
@ -205,4 +219,4 @@ const LabelsSettings: NextPage = () => {
);
};
export default LabelsSettings;
export default observer(LabelsSettings);

107
apps/app/store/labels.ts Normal file
View File

@ -0,0 +1,107 @@
// mobx
import { action, observable, runInAction, makeAutoObservable } from "mobx";
// services
import issueService from "services/issues.service";
// types
import type { IIssueLabels, LabelForm, ICurrentUserResponse } from "types";
class LabelStore {
labels: IIssueLabels[] = [];
isLabelsLoading: boolean = false;
rootStore: any | null = null;
constructor(_rootStore: any | null = null) {
makeAutoObservable(this, {
labels: observable.ref,
loadLabels: action,
isLabelsLoading: observable,
createLabel: action,
updateLabel: action,
deleteLabel: action,
});
this.rootStore = _rootStore;
}
loadLabels = async (workspaceSlug: string, projectId: string) => {
this.isLabelsLoading = true;
try {
const labelsResponse: IIssueLabels[] = await issueService.getIssueLabels(
workspaceSlug,
projectId
);
runInAction(() => {
this.labels = labelsResponse;
this.isLabelsLoading = false;
});
} catch (error) {
this.isLabelsLoading = false;
console.error("Fetching labels error", error);
}
};
createLabel = async (
workspaceSlug: string,
projectId: string,
labelForm: LabelForm,
user: ICurrentUserResponse
) => {
try {
const labelResponse: IIssueLabels = await issueService.createIssueLabel(
workspaceSlug,
projectId,
labelForm,
user
);
runInAction(() => {
this.labels.push(labelResponse);
});
} catch (error) {
console.error("Creating label error", error);
}
};
updateLabel = async (
workspaceSlug: string,
projectId: string,
labelId: string,
labelForm: Partial<LabelForm>,
user: ICurrentUserResponse
) => {
try {
const labelResponse: IIssueLabels = await issueService.patchIssueLabel(
workspaceSlug,
projectId,
labelId,
labelForm,
user
);
runInAction(() => {
const labelIndex = this.labels.findIndex((label) => label.id === labelId);
this.labels[labelIndex] = labelResponse;
});
} catch (error) {
console.error("Updating label error", error);
}
};
deleteLabel = async (
workspaceSlug: string,
projectId: string,
labelId: string,
user: ICurrentUserResponse
) => {
try {
await issueService.deleteIssueLabel(workspaceSlug, projectId, labelId, user);
runInAction(() => {
this.labels = this.labels.filter((label) => label.id !== labelId);
});
} catch (error) {
console.error("Deleting label error", error);
}
};
}
export default LabelStore;

View File

@ -3,15 +3,18 @@ import { enableStaticRendering } from "mobx-react-lite";
// store imports
import UserStore from "./user";
import ThemeStore from "./theme";
import LabelStore from "./labels";
enableStaticRendering(typeof window === "undefined");
export class RootStore {
user;
theme;
labelStore: LabelStore;
constructor() {
this.user = new UserStore(this);
this.theme = new ThemeStore(this);
this.labelStore = new LabelStore(this);
}
}

View File

@ -164,6 +164,15 @@ export interface IIssueLabels {
parent: string | null;
}
export interface LabelForm {
name: string;
description: string;
color: string;
project: string;
workspace: string;
parent: string | null;
}
export interface IIssueActivity {
actor: string;
actor_detail: IUserLite;