refactor: publish project store and components

This commit is contained in:
Aaryan Khandelwal 2024-06-04 21:00:19 +05:30
parent 3fe9e3515b
commit 761c65830c
3 changed files with 107 additions and 116 deletions

View File

@ -25,6 +25,7 @@ type Props = {
};
type FormData = {
anchor: string;
id: string | null;
comments: boolean;
reactions: boolean;
@ -34,6 +35,7 @@ type FormData = {
};
const defaultValues: FormData = {
anchor: "",
id: null,
comments: false,
reactions: false,
@ -48,34 +50,27 @@ const viewOptions: {
}[] = [
{ key: "list", label: "List" },
{ key: "kanban", label: "Kanban" },
// { key: "calendar", label: "Calendar" },
// { key: "gantt", label: "Gantt" },
// { key: "spreadsheet", label: "Spreadsheet" },
];
export const PublishProjectModal: React.FC<Props> = observer((props) => {
const { isOpen, project, onClose } = props;
// hooks
// const { instance } = useInstance();
// states
const [isUnPublishing, setIsUnPublishing] = useState(false);
const [isUpdateRequired, setIsUpdateRequired] = useState(false);
// const plane_deploy_url = instance?.config?.space_base_url || "";
const SPACE_URL = (SPACE_BASE_URL === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH;
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// store hooks
const {
projectPublishSettings,
getProjectSettingsAsync,
fetchPublishSettings,
getPublishSettingsByProjectID,
publishProject,
updateProjectSettingsAsync,
updatePublishSettings,
unPublishProject,
fetchSettingsLoader,
} = useProjectPublish();
// derived values
const projectPublishSettings = getPublishSettingsByProjectID(project.id);
// form info
const {
control,
@ -97,44 +92,44 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
// prefill form with the saved settings if the project is already published
useEffect(() => {
if (projectPublishSettings && projectPublishSettings !== "not-initialized") {
let userBoards: TProjectPublishViews[] = [];
if (!projectPublishSettings) return;
if (projectPublishSettings?.views) {
const savedViews = projectPublishSettings?.views;
let userBoards: TProjectPublishViews[] = [];
if (!savedViews) return;
if (projectPublishSettings?.view_props) {
const savedViews = projectPublishSettings?.view_props;
if (savedViews.list) userBoards.push("list");
if (savedViews.kanban) userBoards.push("kanban");
if (savedViews.calendar) userBoards.push("calendar");
if (savedViews.gantt) userBoards.push("gantt");
if (savedViews.spreadsheet) userBoards.push("spreadsheet");
if (!savedViews) return;
userBoards = userBoards && userBoards.length > 0 ? userBoards : ["list"];
}
if (savedViews.list) userBoards.push("list");
if (savedViews.kanban) userBoards.push("kanban");
if (savedViews.calendar) userBoards.push("calendar");
if (savedViews.gantt) userBoards.push("gantt");
if (savedViews.spreadsheet) userBoards.push("spreadsheet");
const updatedData = {
id: projectPublishSettings?.id || null,
comments: projectPublishSettings?.comments || false,
reactions: projectPublishSettings?.reactions || false,
votes: projectPublishSettings?.votes || false,
inbox: projectPublishSettings?.inbox || null,
views: userBoards,
};
reset({ ...updatedData });
userBoards = userBoards && userBoards.length > 0 ? userBoards : ["list"];
}
const updatedData = {
id: projectPublishSettings?.id || null,
comments: projectPublishSettings?.comments || false,
reactions: projectPublishSettings?.reactions || false,
votes: projectPublishSettings?.votes || false,
inbox: projectPublishSettings?.inbox || null,
views: userBoards,
};
reset({ ...updatedData });
}, [reset, projectPublishSettings, isOpen]);
// fetch publish settings
useEffect(() => {
if (!workspaceSlug || !isOpen) return;
if (projectPublishSettings === "not-initialized") {
getProjectSettingsAsync(workspaceSlug.toString(), project.id);
if (!projectPublishSettings) {
fetchPublishSettings(workspaceSlug.toString(), project.id);
}
}, [isOpen, workspaceSlug, project, projectPublishSettings, getProjectSettingsAsync]);
}, [fetchPublishSettings, isOpen, project, projectPublishSettings, workspaceSlug]);
const handlePublishProject = async (payload: IProjectPublishSettings) => {
if (!workspaceSlug) return;
@ -145,7 +140,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
const handleUpdatePublishSettings = async (payload: IProjectPublishSettings) => {
if (!workspaceSlug) return;
await updateProjectSettingsAsync(workspaceSlug.toString(), project.id, payload.id ?? "", payload)
await updatePublishSettings(workspaceSlug.toString(), project.id, payload.id ?? "", payload)
.then((res) => {
setToast({
type: TOAST_TYPE.SUCCESS,
@ -172,7 +167,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Something went wrong while un-publishing the project.",
message: "Something went wrong while unpublishing the project.",
})
)
.finally(() => setIsUnPublishing(false));
@ -214,7 +209,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
reactions: formData.reactions,
votes: formData.votes,
inbox: formData.inbox,
views: {
view_props: {
list: formData.views.includes("list"),
kanban: formData.views.includes("kanban"),
calendar: formData.views.includes("calendar"),
@ -223,13 +218,18 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
},
};
if (project.is_deployed) await handleUpdatePublishSettings({ id: watch("id") ?? "", ...payload });
if (project.is_deployed)
await handleUpdatePublishSettings({
anchor: watch("anchor") ?? "",
id: watch("id") ?? "",
...payload,
});
else await handlePublishProject(payload);
};
// check if an update is required or not
const checkIfUpdateIsRequired = () => {
if (!projectPublishSettings || projectPublishSettings === "not-initialized") return;
if (!projectPublishSettings || !projectPublishSettings) return;
const currentSettings = projectPublishSettings;
const newSettings = getValues();
@ -245,7 +245,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
let viewCheckFlag = 0;
viewOptions.forEach((option) => {
if (currentSettings.views[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++;
if (currentSettings.view_props?.[option.key] !== newSettings.views.includes(option.key)) viewCheckFlag++;
});
if (viewCheckFlag !== 0) {
@ -256,6 +256,8 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
setIsUpdateRequired(false);
};
const SPACE_URL = (SPACE_BASE_URL === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH;
return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-20" onClose={handleClose}>
@ -293,7 +295,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
onClick={() => handleUnPublishProject(watch("id") ?? "")}
loading={isUnPublishing}
>
{isUnPublishing ? "Un-publishing..." : "Un-publish"}
{isUnPublishing ? "Unpublishing" : "Unpublish"}
</Button>
)}
</div>
@ -308,12 +310,12 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
</Loader>
) : (
<div className="px-6">
{project.is_deployed && (
{project.is_deployed && projectPublishSettings && (
<>
<div className="relative flex items-center gap-2 rounded-md border border-custom-border-100 bg-custom-background-80 px-3 py-2">
<div className="flex-grow truncate text-sm">{`${SPACE_URL}/issues/`}</div>
<div className="flex-grow truncate text-sm">{`${SPACE_URL}/issues/${projectPublishSettings.anchor}`}</div>
<div className="relative flex flex-shrink-0 items-center gap-1">
<CopyLinkToClipboard copy_link={`${SPACE_URL}/issues`} />
<CopyLinkToClipboard copy_link={`${SPACE_URL}/issues/${projectPublishSettings.anchor}`} />
</div>
</div>
<div className="mt-3 flex items-center gap-1 text-custom-primary-100">

View File

@ -34,7 +34,7 @@ export class ProjectPublishService extends APIService {
projectID: string,
project_publish_id: string,
data: IProjectPublishSettings
): Promise<any> {
): Promise<IProjectPublishSettings> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectID}/project-deploy-boards/${project_publish_id}/`,
data

View File

@ -1,4 +1,5 @@
import set from "lodash/set";
import unset from "lodash/unset";
import { observable, action, makeObservable, runInAction } from "mobx";
// types
import { ProjectPublishService } from "@/services/project";
@ -12,12 +13,13 @@ export type TProjectPublishViewsSettings = {
};
export interface IProjectPublishSettings {
anchor?: string;
id?: string;
project?: string;
comments: boolean;
reactions: boolean;
votes: boolean;
views: TProjectPublishViewsSettings;
view_props: TProjectPublishViewsSettings;
inbox: string | null;
}
@ -26,31 +28,31 @@ export interface IProjectPublishStore {
generalLoader: boolean;
fetchSettingsLoader: boolean;
// observables
projectPublishSettings: IProjectPublishSettings | "not-initialized";
// project settings actions
getProjectSettingsAsync: (workspaceSlug: string, projectId: string) => Promise<IProjectPublishSettings>;
updateProjectSettingsAsync: (
publishSettingsMap: Record<string, IProjectPublishSettings>; // projectID => IProjectPublishSettings
// helpers
getPublishSettingsByProjectID: (projectID: string) => IProjectPublishSettings | undefined;
// actions
fetchPublishSettings: (workspaceSlug: string, projectID: string) => Promise<IProjectPublishSettings>;
updatePublishSettings: (
workspaceSlug: string,
projectId: string,
projectID: string,
projectPublishId: string,
data: IProjectPublishSettings
) => Promise<void>;
// project publish actions
) => Promise<IProjectPublishSettings>;
publishProject: (
workspaceSlug: string,
projectId: string,
projectID: string,
data: IProjectPublishSettings
) => Promise<IProjectPublishSettings>;
unPublishProject: (workspaceSlug: string, projectId: string, projectPublishId: string) => Promise<void>;
unPublishProject: (workspaceSlug: string, projectID: string, projectPublishId: string) => Promise<void>;
}
export class ProjectPublishStore implements IProjectPublishStore {
// states
generalLoader: boolean = false;
fetchSettingsLoader: boolean = false;
// actions
project_id: string | null = null;
projectPublishSettings: IProjectPublishSettings | "not-initialized" = "not-initialized";
// observables
publishSettingsMap: Record<string, IProjectPublishSettings> = {};
// root store
projectRootStore: ProjectRootStore;
// services
@ -62,12 +64,10 @@ export class ProjectPublishStore implements IProjectPublishStore {
generalLoader: observable.ref,
fetchSettingsLoader: observable.ref,
// observables
project_id: observable.ref,
projectPublishSettings: observable.ref,
// project settings actions
getProjectSettingsAsync: action,
updateProjectSettingsAsync: action,
// project publish actions
publishSettingsMap: observable,
// actions
fetchPublishSettings: action,
updatePublishSettings: action,
publishProject: action,
unPublishProject: action,
});
@ -77,29 +77,31 @@ export class ProjectPublishStore implements IProjectPublishStore {
this.projectPublishService = new ProjectPublishService();
}
/**
* @description returns the publish settings of a particular project
* @param {string} projectID
* @returns {IProjectPublishSettings | undefined}
*/
getPublishSettingsByProjectID = (projectID: string): IProjectPublishSettings | undefined =>
this.publishSettingsMap?.[projectID] ?? undefined;
/**
* Fetches project publish settings
* @param workspaceSlug
* @param projectId
* @param projectID
* @returns
*/
getProjectSettingsAsync = async (workspaceSlug: string, projectId: string) => {
fetchPublishSettings = async (workspaceSlug: string, projectID: string) => {
try {
runInAction(() => {
this.fetchSettingsLoader = true;
});
const response = await this.projectPublishService.getProjectSettingsAsync(workspaceSlug, projectId);
if (response) {
runInAction(() => {
this.projectPublishSettings = response;
this.fetchSettingsLoader = false;
});
} else {
runInAction(() => {
this.projectPublishSettings = "not-initialized";
this.fetchSettingsLoader = false;
});
}
const response = await this.projectPublishService.getProjectSettingsAsync(workspaceSlug, projectID);
runInAction(() => {
set(this.publishSettingsMap, [projectID], response);
this.fetchSettingsLoader = false;
});
return response;
} catch (error) {
runInAction(() => {
@ -112,23 +114,21 @@ export class ProjectPublishStore implements IProjectPublishStore {
/**
* Publishes project and updates project publish status in the store
* @param workspaceSlug
* @param projectId
* @param projectID
* @param data
* @returns
*/
publishProject = async (workspaceSlug: string, projectId: string, data: IProjectPublishSettings) => {
publishProject = async (workspaceSlug: string, projectID: string, data: IProjectPublishSettings) => {
try {
runInAction(() => {
this.generalLoader = true;
});
const response = await this.projectPublishService.createProjectSettingsAsync(workspaceSlug, projectId, data);
if (response) {
runInAction(() => {
this.projectPublishSettings = response;
set(this.projectRootStore.project.projectMap, [projectId, "is_deployed"], true);
this.generalLoader = false;
});
}
const response = await this.projectPublishService.createProjectSettingsAsync(workspaceSlug, projectID, data);
runInAction(() => {
set(this.publishSettingsMap, [projectID], response);
set(this.projectRootStore.project.projectMap, [projectID, "is_deployed"], true);
this.generalLoader = false;
});
return response;
} catch (error) {
runInAction(() => {
@ -141,14 +141,14 @@ export class ProjectPublishStore implements IProjectPublishStore {
/**
* Updates project publish settings
* @param workspaceSlug
* @param projectId
* @param projectID
* @param projectPublishId
* @param data
* @returns
*/
updateProjectSettingsAsync = async (
updatePublishSettings = async (
workspaceSlug: string,
projectId: string,
projectID: string,
projectPublishId: string,
data: IProjectPublishSettings
) => {
@ -158,26 +158,15 @@ export class ProjectPublishStore implements IProjectPublishStore {
});
const response = await this.projectPublishService.updateProjectSettingsAsync(
workspaceSlug,
projectId,
projectID,
projectPublishId,
data
);
if (response) {
const _projectPublishSettings: IProjectPublishSettings = {
id: response?.id || null,
comments: response?.comments || false,
reactions: response?.reactions || false,
votes: response?.votes || false,
views: { ...response?.views },
inbox: response?.inbox || null,
project: response?.project || null,
};
runInAction(() => {
this.projectPublishSettings = _projectPublishSettings;
this.generalLoader = false;
});
return response;
}
runInAction(() => {
set(this.publishSettingsMap, [projectID], response);
this.generalLoader = false;
});
return response;
} catch (error) {
runInAction(() => {
this.generalLoader = false;
@ -189,23 +178,23 @@ export class ProjectPublishStore implements IProjectPublishStore {
/**
* Unpublishes project and updates project publish status in the store
* @param workspaceSlug
* @param projectId
* @param projectID
* @param projectPublishId
* @returns
*/
unPublishProject = async (workspaceSlug: string, projectId: string, projectPublishId: string) => {
unPublishProject = async (workspaceSlug: string, projectID: string, projectPublishId: string) => {
try {
runInAction(() => {
this.generalLoader = true;
});
const response = await this.projectPublishService.deleteProjectSettingsAsync(
workspaceSlug,
projectId,
projectID,
projectPublishId
);
runInAction(() => {
this.projectPublishSettings = "not-initialized";
set(this.projectRootStore.project.projectMap, [projectId, "is_deployed"], false);
unset(this.publishSettingsMap, [projectID]);
set(this.projectRootStore.project.projectMap, [projectID, "is_deployed"], false);
this.generalLoader = false;
});
return response;