forked from github/plane
chore: updating the theme using MobX from command k (#1879)
* chore: updating the theme using mobx from command k * feat: Showing the project published status in the app header * dev: updated validation and redirection the project publish modal and added redirection on the app header
This commit is contained in:
parent
5b6b43fb83
commit
65295f6c6f
@ -9,12 +9,18 @@ import userService from "services/user.service";
|
|||||||
import useUser from "hooks/use-user";
|
import useUser from "hooks/use-user";
|
||||||
// helper
|
// helper
|
||||||
import { unsetCustomCssVariables } from "helpers/theme.helper";
|
import { unsetCustomCssVariables } from "helpers/theme.helper";
|
||||||
|
// mobx react lite
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
export const ChangeInterfaceTheme: React.FC<Props> = observer(({ setIsPaletteOpen }) => {
|
||||||
|
const store: any = useMobxStore();
|
||||||
|
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
@ -23,29 +29,11 @@ export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
|||||||
|
|
||||||
const updateUserTheme = (newTheme: string) => {
|
const updateUserTheme = (newTheme: string) => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
unsetCustomCssVariables();
|
|
||||||
|
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
|
return store.user
|
||||||
mutateUser((prevData: any) => {
|
.updateCurrentUserSettings({ theme: { ...user.theme, theme: newTheme } })
|
||||||
if (!prevData) return prevData;
|
.then((response: any) => response)
|
||||||
|
.catch((error: any) => error);
|
||||||
return {
|
|
||||||
...prevData,
|
|
||||||
theme: {
|
|
||||||
...prevData?.theme,
|
|
||||||
theme: newTheme,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
userService.updateUser({
|
|
||||||
theme: {
|
|
||||||
...user.theme,
|
|
||||||
theme: newTheme,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// useEffect only runs on the client, so now we can safely show the UI
|
// useEffect only runs on the client, so now we can safely show the UI
|
||||||
@ -74,4 +62,4 @@ export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -14,6 +14,9 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { RootStore } from "store/root";
|
import { RootStore } from "store/root";
|
||||||
import { IProjectPublishSettingsViews } from "store/project-publish";
|
import { IProjectPublishSettingsViews } from "store/project-publish";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
|
import useProjectDetails from "hooks/use-project-details";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
// user: ICurrentUserResponse | undefined;
|
// user: ICurrentUserResponse | undefined;
|
||||||
@ -25,7 +28,7 @@ const defaultValues: Partial<any> = {
|
|||||||
reactions: false,
|
reactions: false,
|
||||||
votes: false,
|
votes: false,
|
||||||
inbox: null,
|
inbox: null,
|
||||||
views: [],
|
views: ["list", "kanban"],
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewOptions = [
|
const viewOptions = [
|
||||||
@ -40,6 +43,17 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
|||||||
const store: RootStore = useMobxStore();
|
const store: RootStore = useMobxStore();
|
||||||
const { projectPublish } = store;
|
const { projectPublish } = store;
|
||||||
|
|
||||||
|
const { projectDetails, mutateProjectDetails } = useProjectDetails();
|
||||||
|
|
||||||
|
const { setToastAlert } = useToast();
|
||||||
|
const handleToastAlert = (title: string, type: string, message: string) => {
|
||||||
|
setToastAlert({
|
||||||
|
title: title || "Title",
|
||||||
|
type: "error" || "warning",
|
||||||
|
message: message || "Message",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const { NEXT_PUBLIC_DEPLOY_URL } = process.env;
|
const { NEXT_PUBLIC_DEPLOY_URL } = process.env;
|
||||||
const plane_deploy_url = NEXT_PUBLIC_DEPLOY_URL
|
const plane_deploy_url = NEXT_PUBLIC_DEPLOY_URL
|
||||||
? NEXT_PUBLIC_DEPLOY_URL
|
? NEXT_PUBLIC_DEPLOY_URL
|
||||||
@ -111,32 +125,41 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
|||||||
}, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]);
|
}, [workspaceSlug, projectPublish, projectPublish.projectPublishModal]);
|
||||||
|
|
||||||
const onSettingsPublish = async (formData: any) => {
|
const onSettingsPublish = async (formData: any) => {
|
||||||
const payload = {
|
if (formData.views && formData.views.length > 0) {
|
||||||
comments: formData.comments || false,
|
const payload = {
|
||||||
reactions: formData.reactions || false,
|
comments: formData.comments || false,
|
||||||
votes: formData.votes || false,
|
reactions: formData.reactions || false,
|
||||||
inbox: formData.inbox || null,
|
votes: formData.votes || false,
|
||||||
views: {
|
inbox: formData.inbox || null,
|
||||||
list: formData.views.includes("list") || false,
|
views: {
|
||||||
kanban: formData.views.includes("kanban") || false,
|
list: formData.views.includes("list") || false,
|
||||||
calendar: formData.views.includes("calendar") || false,
|
kanban: formData.views.includes("kanban") || false,
|
||||||
gantt: formData.views.includes("gantt") || false,
|
calendar: formData.views.includes("calendar") || false,
|
||||||
spreadsheet: formData.views.includes("spreadsheet") || false,
|
gantt: formData.views.includes("gantt") || false,
|
||||||
},
|
spreadsheet: formData.views.includes("spreadsheet") || false,
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return projectPublish
|
const _workspaceSlug = workspaceSlug;
|
||||||
.createProjectSettingsAsync(
|
const _projectId = projectPublish.project_id;
|
||||||
workspaceSlug as string,
|
|
||||||
projectPublish.project_id as string,
|
return projectPublish
|
||||||
payload,
|
.createProjectSettingsAsync(_workspaceSlug as string, _projectId as string, payload, null)
|
||||||
null
|
.then((response) => {
|
||||||
)
|
mutateProjectDetails();
|
||||||
.then((response) => response)
|
handleClose();
|
||||||
.catch((error) => {
|
console.log("_projectId", _projectId);
|
||||||
console.error("error", error);
|
if (_projectId)
|
||||||
return error;
|
window.open(`${plane_deploy_url}/${_workspaceSlug}/${_projectId}`, "_blank");
|
||||||
});
|
return response;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("error", error);
|
||||||
|
return error;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleToastAlert("Missing fields", "warning", "Please select at least one view to publish");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSettingsUpdate = async (key: string, value: any) => {
|
const onSettingsUpdate = async (key: string, value: any) => {
|
||||||
@ -171,7 +194,10 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
|||||||
payload,
|
payload,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
.then((response) => response)
|
.then((response) => {
|
||||||
|
mutateProjectDetails();
|
||||||
|
return response;
|
||||||
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log("error", error);
|
console.log("error", error);
|
||||||
return error;
|
return error;
|
||||||
@ -187,7 +213,9 @@ export const PublishProjectModal: React.FC<Props> = observer(() => {
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
mutateProjectDetails();
|
||||||
reset({ ...defaultValues });
|
reset({ ...defaultValues });
|
||||||
|
handleClose();
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
|
// next imports
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import Link from "next/link";
|
||||||
// icons
|
// icons
|
||||||
import { Bars3Icon } from "@heroicons/react/24/outline";
|
import { Bars3Icon } from "@heroicons/react/24/outline";
|
||||||
|
// ui components
|
||||||
|
import { Tooltip } from "components/ui";
|
||||||
|
// hooks
|
||||||
|
import useProjectDetails from "hooks/use-project-details";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
breadcrumbs?: JSX.Element;
|
breadcrumbs?: JSX.Element;
|
||||||
@ -9,27 +16,61 @@ type Props = {
|
|||||||
noHeader: boolean;
|
noHeader: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header: React.FC<Props> = ({ breadcrumbs, left, right, setToggleSidebar, noHeader }) => (
|
const { NEXT_PUBLIC_DEPLOY_URL } = process.env;
|
||||||
<div
|
const plane_deploy_url = NEXT_PUBLIC_DEPLOY_URL ? NEXT_PUBLIC_DEPLOY_URL : "http://localhost:3001";
|
||||||
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 px-5 py-4 ${
|
|
||||||
noHeader ? "md:hidden" : ""
|
const Header: React.FC<Props> = ({ breadcrumbs, left, right, setToggleSidebar, noHeader }) => {
|
||||||
}`}
|
const { projectDetails } = useProjectDetails();
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
const router = useRouter();
|
||||||
<div className="block md:hidden">
|
const { workspaceSlug, projectId } = router.query;
|
||||||
<button
|
|
||||||
type="button"
|
return (
|
||||||
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
|
<div
|
||||||
onClick={() => setToggleSidebar((prevData) => !prevData)}
|
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 px-5 py-4 ${
|
||||||
>
|
noHeader ? "md:hidden" : ""
|
||||||
<Bars3Icon className="h-5 w-5" />
|
}`}
|
||||||
</button>
|
>
|
||||||
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
|
<div className="block md:hidden">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid h-8 w-8 place-items-center rounded border border-custom-border-200"
|
||||||
|
onClick={() => setToggleSidebar((prevData) => !prevData)}
|
||||||
|
>
|
||||||
|
<Bars3Icon className="h-5 w-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>{breadcrumbs}</div>
|
||||||
|
|
||||||
|
{projectDetails && projectDetails?.is_deployed && (
|
||||||
|
<Link href={`${plane_deploy_url}/${workspaceSlug}/${projectId}`}>
|
||||||
|
<a target="_blank" rel="noreferrer">
|
||||||
|
<Tooltip
|
||||||
|
tooltipContent="This project is public, and live on web."
|
||||||
|
position="bottom-left"
|
||||||
|
>
|
||||||
|
<div className="transition-all flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 p-1 rounded overflow-hidden relative flex items-center gap-1 cursor-pointer group">
|
||||||
|
<div className="w-[14px] h-[14px] flex justify-center items-center">
|
||||||
|
<span className="material-symbols-rounded text-[14px]">
|
||||||
|
radio_button_checked
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs font-medium">Public</div>
|
||||||
|
<div className="w-[14px] h-[14px] hidden group-hover:flex justify-center items-center">
|
||||||
|
<span className="material-symbols-rounded text-[14px]">open_in_new</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex-shrink-0">{left}</div>
|
||||||
</div>
|
</div>
|
||||||
{breadcrumbs}
|
<div className="flex-shrink-0">{right}</div>
|
||||||
<div className="flex-shrink-0">{left}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-shrink-0">{right}</div>
|
);
|
||||||
</div>
|
};
|
||||||
);
|
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
||||||
|
@ -259,15 +259,13 @@ class ProjectPublishStore implements IProjectPublishStore {
|
|||||||
user
|
user
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response) {
|
runInAction(() => {
|
||||||
runInAction(() => {
|
this.projectPublishSettings = "not-initialized";
|
||||||
this.projectPublishSettings = "not-initialized";
|
this.loader = false;
|
||||||
this.loader = false;
|
this.error = null;
|
||||||
this.error = null;
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.loader = false;
|
this.loader = false;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
|
1
apps/app/types/projects.d.ts
vendored
1
apps/app/types/projects.d.ts
vendored
@ -57,6 +57,7 @@ export interface IProject {
|
|||||||
updated_by: string;
|
updated_by: string;
|
||||||
workspace: IWorkspace | string;
|
workspace: IWorkspace | string;
|
||||||
workspace_detail: IWorkspaceLite;
|
workspace_detail: IWorkspaceLite;
|
||||||
|
is_deployed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProjectLite {
|
export interface IProjectLite {
|
||||||
|
Loading…
Reference in New Issue
Block a user