mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'chore-admin-file-structure' of github.com:makeplane/plane into chore-admin-file-structure
This commit is contained in:
commit
6c09e6dbde
@ -7,7 +7,7 @@ const nextConfig = {
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
basePath: process.env.NEXT_PUBLIC_DEPLOY_WITH_NGINX === "1" ? "/god-mode" : "",
|
||||
basePath: "/god-mode",
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
@ -10,7 +10,8 @@ import { EAuthModes, EAuthSteps, ForgotPasswordPopover, PasswordStrengthMeter }
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import { getPasswordStrength } from "@/helpers/password.helper";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store";
|
||||
import { AuthService } from "@/services/authentication.service";
|
||||
|
||||
type Props = {
|
||||
@ -42,9 +43,7 @@ export const PasswordForm: React.FC<Props> = (props) => {
|
||||
const [isPasswordInputFocused, setIsPasswordInputFocused] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
// hooks
|
||||
const {
|
||||
instanceStore: { instance },
|
||||
} = useMobxStore();
|
||||
const { instance } = useInstance();
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { next_path } = router.query;
|
||||
|
@ -4,8 +4,8 @@ import { observer } from "mobx-react-lite";
|
||||
import { IEmailCheckData } from "@plane/types";
|
||||
import { EmailForm, UniqueCodeForm, PasswordForm, OAuthOptions, TermsAndConditions } from "@/components/accounts";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store";
|
||||
import useToast from "@/hooks/use-toast";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// services
|
||||
import { AuthService } from "@/services/authentication.service";
|
||||
|
||||
@ -60,9 +60,7 @@ export const AuthRoot = observer(() => {
|
||||
const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL);
|
||||
const [email, setEmail] = useState("");
|
||||
// hooks
|
||||
const {
|
||||
instanceStore: { instance },
|
||||
} = useMobxStore();
|
||||
const { instance } = useInstance();
|
||||
// derived values
|
||||
const isSmtpConfigured = instance?.config?.is_smtp_configured;
|
||||
|
||||
|
@ -2,13 +2,11 @@ import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { GithubOAuthButton, GoogleOAuthButton } from "@/components/accounts";
|
||||
// hooks
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { useInstance } from "@/hooks/store";
|
||||
|
||||
export const OAuthOptions: React.FC = observer(() => {
|
||||
// hooks
|
||||
const {
|
||||
instanceStore: { instance },
|
||||
} = useMobxStore();
|
||||
const { instance } = useInstance();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -6,7 +6,7 @@ import { Transition, Dialog } from "@headlessui/react";
|
||||
// hooks
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// hooks
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { useInstance } from "@/hooks/store";
|
||||
// services
|
||||
import fileService from "@/services/file.service";
|
||||
|
||||
@ -27,9 +27,7 @@ export const UserImageUploadModal: React.FC<Props> = observer((props) => {
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [isImageUploading, setIsImageUploading] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
instanceStore: { instance },
|
||||
} = useMobxStore();
|
||||
const { instance } = useInstance();
|
||||
|
||||
const onDrop = (acceptedFiles: File[]) => setImage(acceptedFiles[0]);
|
||||
|
||||
|
@ -1,17 +1,14 @@
|
||||
import Image from "next/image";
|
||||
|
||||
// mobx
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
// assets
|
||||
import PlaneLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
|
||||
import UserLoggedInImage from "public/user-logged-in.svg";
|
||||
|
||||
export const UserLoggedIn = () => {
|
||||
const { user: userStore } = useMobxStore();
|
||||
const user = userStore.currentUser;
|
||||
const { data: user } = useUser();
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen flex-col">
|
||||
<div className="relative flex w-full items-center justify-between gap-4 border-b border-custom-border-200 px-6 py-5">
|
||||
|
@ -10,14 +10,7 @@ import instanceNotReady from "public/instance/plane-instance-not-ready.webp";
|
||||
import PlaneBlackLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
|
||||
import PlaneWhiteLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
|
||||
|
||||
type TInstanceNotReady = {
|
||||
isGodModeEnabled: boolean;
|
||||
handleGodModeStateChange?: (state: boolean) => void;
|
||||
};
|
||||
|
||||
export const InstanceNotReady: FC<TInstanceNotReady> = () => {
|
||||
// const { isGodModeEnabled, handleGodModeStateChange } = props;
|
||||
|
||||
export const InstanceNotReady: FC = () => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo;
|
||||
|
@ -10,7 +10,7 @@ import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
|
||||
// components
|
||||
// interfaces
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssue } from "types/issue";
|
||||
|
||||
export const IssueKanBanBlock = observer(({ issue }: { issue: IIssue }) => {
|
||||
|
@ -7,7 +7,7 @@ import { issueGroupFilter } from "@/constants/data";
|
||||
// ui
|
||||
// mobx hook
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssueState } from "types/issue";
|
||||
|
||||
export const IssueKanBanHeader = observer(({ state }: { state: IIssueState }) => {
|
||||
|
@ -10,7 +10,7 @@ import { Icon } from "@/components/ui";
|
||||
// interfaces
|
||||
// mobx hook
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssueState, IIssue } from "types/issue";
|
||||
|
||||
export const IssueKanbanView = observer(() => {
|
||||
|
@ -9,7 +9,7 @@ import { IssueBlockState } from "@/components/issues/board-views/block-state";
|
||||
// mobx hook
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// interfaces
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssue } from "types/issue";
|
||||
// store
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { StateGroupIcon } from "@plane/ui";
|
||||
import { issueGroupFilter } from "@/constants/data";
|
||||
// mobx hook
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssueState } from "types/issue";
|
||||
|
||||
export const IssueListHeader = observer(({ state }: { state: IIssueState }) => {
|
||||
|
@ -6,7 +6,7 @@ import { IssueListHeader } from "@/components/issues/board-views/list/header";
|
||||
// mobx hook
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// store
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssueState, IIssue } from "types/issue";
|
||||
|
||||
export const IssueListView = observer(() => {
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
// store
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { IIssueFilterOptions } from "@/store/issues/types";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { AppliedFiltersList } from "./filters-list";
|
||||
|
||||
export const IssueAppliedFilters: FC = observer(() => {
|
||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/store/issues/helpers";
|
||||
import { IIssueFilterOptions } from "@/store/issues/types";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { FiltersDropdown } from "./helpers/dropdown";
|
||||
import { FilterSelection } from "./selection";
|
||||
// types
|
||||
|
@ -7,21 +7,21 @@ import { Briefcase } from "lucide-react";
|
||||
import { Avatar, Button } from "@plane/ui";
|
||||
import { ProjectLogo } from "@/components/common";
|
||||
import { IssueFiltersDropdown } from "@/components/issues/filters";
|
||||
// ui
|
||||
// lib
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// store
|
||||
import { RootStore } from "@/store/root";
|
||||
import { TIssueBoardKeys } from "types/issue";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { TIssueBoardKeys } from "@/types/issue";
|
||||
import { NavbarIssueBoardView } from "./issue-board-view";
|
||||
import { NavbarTheme } from "./theme";
|
||||
|
||||
const IssueNavbar = observer(() => {
|
||||
const {
|
||||
project: projectStore,
|
||||
user: userStore,
|
||||
issuesFilter: { updateFilters },
|
||||
}: RootStore = useMobxStore();
|
||||
const { data: user } = useUser();
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug, board, peekId, states, priorities, labels } = router.query as {
|
||||
@ -34,8 +34,6 @@ const IssueNavbar = observer(() => {
|
||||
labels: string;
|
||||
};
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace_slug && project_slug) {
|
||||
projectStore.fetchProjectSettings(workspace_slug.toString(), project_slug.toString());
|
||||
|
@ -4,7 +4,7 @@ import { useRouter } from "next/router";
|
||||
import { issueViews } from "@/constants/data";
|
||||
// mobx
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { TIssueBoardKeys } from "types/issue";
|
||||
|
||||
export const NavbarIssueBoardView = observer(() => {
|
||||
|
@ -6,6 +6,7 @@ import { useForm, Controller } from "react-hook-form";
|
||||
import { EditorRefApi } from "@plane/lite-text-editor";
|
||||
import { LiteTextEditor } from "@/components/editor/lite-text-editor";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import useToast from "@/hooks/use-toast";
|
||||
// lib
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
@ -29,7 +30,8 @@ export const AddComment: React.FC<Props> = observer(() => {
|
||||
const { workspace_slug, project_slug } = router.query;
|
||||
// store hooks
|
||||
const { project } = useMobxStore();
|
||||
const { user: userStore, issueDetails: issueDetailStore } = useMobxStore();
|
||||
const { issueDetails: issueDetailStore } = useMobxStore();
|
||||
const { data: currentUser } = useUser();
|
||||
// derived values
|
||||
const workspaceId = project.workspace?.id;
|
||||
const issueId = issueDetailStore.peekId;
|
||||
@ -62,6 +64,7 @@ export const AddComment: React.FC<Props> = observer(() => {
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: on click if he user is not logged in redirect to login page
|
||||
return (
|
||||
<div>
|
||||
<div className="issue-comments-section">
|
||||
@ -70,7 +73,9 @@ export const AddComment: React.FC<Props> = observer(() => {
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<LiteTextEditor
|
||||
onEnterKeyPress={(e) => userStore.requiredLogin(() => handleSubmit(onSubmit)(e))}
|
||||
onEnterKeyPress={(e) => {
|
||||
if (currentUser) handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
workspaceId={workspaceId as string}
|
||||
workspaceSlug={workspace_slug as string}
|
||||
ref={editorRef}
|
||||
|
@ -9,10 +9,12 @@ import { LiteTextEditor, LiteTextReadOnlyEditor } from "@/components/editor";
|
||||
import { CommentReactions } from "@/components/issues/peek-overview";
|
||||
// helpers
|
||||
import { timeAgo } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
// mobx store
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// store
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
// types
|
||||
import { Comment } from "@/types/issue";
|
||||
|
||||
@ -27,7 +29,8 @@ export const CommentCard: React.FC<Props> = observer((props) => {
|
||||
const workspaceId = project.workspace?.id;
|
||||
|
||||
// store
|
||||
const { user: userStore, issueDetails: issueDetailStore } = useMobxStore();
|
||||
const { issueDetails: issueDetailStore } = useMobxStore();
|
||||
const { data: currentUser } = useUser();
|
||||
// states
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
// refs
|
||||
@ -139,7 +142,7 @@ export const CommentCard: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{userStore?.currentUser?.id === comment?.actor_detail?.id && (
|
||||
{currentUser?.id === comment?.actor_detail?.id && (
|
||||
<Menu as="div" className="relative w-min text-left">
|
||||
<Menu.Button
|
||||
type="button"
|
||||
|
@ -8,6 +8,8 @@ import { Tooltip } from "@plane/ui";
|
||||
import { ReactionSelector } from "@/components/ui";
|
||||
// helpers
|
||||
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
|
||||
type Props = {
|
||||
@ -20,12 +22,11 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
|
||||
|
||||
const router = useRouter();
|
||||
const { workspace_slug } = router.query;
|
||||
|
||||
const { issueDetails: issueDetailsStore, user: userStore } = useMobxStore();
|
||||
// hooks
|
||||
const { issueDetails: issueDetailsStore } = useMobxStore();
|
||||
const { data: user } = useUser();
|
||||
|
||||
const peekId = issueDetailsStore.peekId;
|
||||
const user = userStore.currentUser;
|
||||
|
||||
const commentReactions = peekId
|
||||
? issueDetailsStore.details[peekId].comments.find((c) => c.id === commentId)?.comment_reactions
|
||||
: [];
|
||||
@ -64,13 +65,13 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
|
||||
else handleAddReaction(reactionHex);
|
||||
};
|
||||
|
||||
// TODO: on onclick redirect to login page if the user is not logged in
|
||||
return (
|
||||
<div className="mt-2 flex items-center gap-1.5">
|
||||
<ReactionSelector
|
||||
onSelect={(value) => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleReactionClick(value);
|
||||
});
|
||||
if (user) handleReactionClick(value);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
position="top"
|
||||
selected={userReactions?.map((r) => r.reaction)}
|
||||
@ -98,14 +99,11 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleReactionClick(reaction);
|
||||
});
|
||||
if (user) handleReactionClick(reaction);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
|
||||
commentReactions?.some(
|
||||
(r) => r.actor_detail.id === userStore.currentUser?.id && r.reaction === reaction
|
||||
)
|
||||
commentReactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction)
|
||||
? "bg-custom-primary-100/10"
|
||||
: "bg-custom-background-80"
|
||||
}`}
|
||||
@ -113,9 +111,7 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
|
||||
<span>{renderEmoji(reaction)}</span>
|
||||
<span
|
||||
className={
|
||||
commentReactions?.some(
|
||||
(r) => r.actor_detail.id === userStore.currentUser?.id && r.reaction === reaction
|
||||
)
|
||||
commentReactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction)
|
||||
? "text-custom-primary-100"
|
||||
: ""
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||
// store
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { IPeekMode } from "@/store/issue_details";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
// lib
|
||||
import useToast from "hooks/use-toast";
|
||||
// types
|
||||
|
@ -1,19 +1,16 @@
|
||||
import React from "react";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// mobx
|
||||
// lib
|
||||
import { Button } from "@plane/ui";
|
||||
import { CommentCard, AddComment } from "@/components/issues/peek-overview";
|
||||
import { Icon } from "@/components/ui";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// components
|
||||
// ui
|
||||
// types
|
||||
import { IIssue } from "types/issue";
|
||||
import { IIssue } from "@/types/issue";
|
||||
|
||||
type Props = {
|
||||
issueDetails: IIssue;
|
||||
@ -24,11 +21,8 @@ export const PeekOverviewIssueActivity: React.FC<Props> = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspace_slug } = router.query;
|
||||
// store
|
||||
const {
|
||||
issueDetails: issueDetailStore,
|
||||
project: projectStore,
|
||||
user: { currentUser },
|
||||
} = useMobxStore();
|
||||
const { issueDetails: issueDetailStore, project: projectStore } = useMobxStore();
|
||||
const { data: currentUser } = useUser();
|
||||
const comments = issueDetailStore.details[issueDetailStore.peekId || ""]?.comments || [];
|
||||
|
||||
return (
|
||||
|
@ -5,6 +5,8 @@ import { useRouter } from "next/router";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
import { ReactionSelector } from "@/components/ui";
|
||||
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// helpers
|
||||
// components
|
||||
@ -14,9 +16,9 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug } = router.query;
|
||||
// store
|
||||
const { user: userStore, issueDetails: issueDetailsStore } = useMobxStore();
|
||||
const { issueDetails: issueDetailsStore } = useMobxStore();
|
||||
const { data: user, fetchCurrentUser } = useUser();
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
const issueId = issueDetailsStore.peekId;
|
||||
const reactions = issueId ? issueDetailsStore.details[issueId]?.reactions || [] : [];
|
||||
const groupedReactions = groupReactions(reactions, "reaction");
|
||||
@ -44,16 +46,16 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
|
||||
useEffect(() => {
|
||||
if (user) return;
|
||||
userStore.fetchCurrentUser();
|
||||
}, [user, userStore]);
|
||||
fetchCurrentUser();
|
||||
}, [user, fetchCurrentUser]);
|
||||
|
||||
// TODO: on onclick of reaction, if user not logged in redirect to login page
|
||||
return (
|
||||
<>
|
||||
<ReactionSelector
|
||||
onSelect={(value) => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleReactionClick(value);
|
||||
});
|
||||
if (user) handleReactionClick(value);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
selected={userReactions?.map((r) => r.reaction)}
|
||||
size="md"
|
||||
@ -80,9 +82,8 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleReactionClick(reaction);
|
||||
});
|
||||
if (user) handleReactionClick(reaction);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
|
||||
reactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction)
|
||||
|
@ -6,6 +6,8 @@ import { useRouter } from "next/router";
|
||||
// mobx
|
||||
// lib
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// ui
|
||||
|
||||
@ -16,9 +18,9 @@ export const IssueVotes: React.FC = observer(() => {
|
||||
|
||||
const { workspace_slug, project_slug } = router.query;
|
||||
|
||||
const { user: userStore, issueDetails: issueDetailsStore } = useMobxStore();
|
||||
const { issueDetails: issueDetailsStore } = useMobxStore();
|
||||
const { data: user, fetchCurrentUser } = useUser();
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
const issueId = issueDetailsStore.peekId;
|
||||
|
||||
const votes = issueId ? issueDetailsStore.details[issueId]?.votes : [];
|
||||
@ -49,8 +51,8 @@ export const IssueVotes: React.FC = observer(() => {
|
||||
useEffect(() => {
|
||||
if (user) return;
|
||||
|
||||
userStore.fetchCurrentUser();
|
||||
}, [user, userStore]);
|
||||
fetchCurrentUser();
|
||||
}, [user, fetchCurrentUser]);
|
||||
|
||||
const VOTES_LIMIT = 1000;
|
||||
|
||||
@ -78,9 +80,8 @@ export const IssueVotes: React.FC = observer(() => {
|
||||
type="button"
|
||||
disabled={isSubmitting}
|
||||
onClick={(e) => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleVote(e, 1);
|
||||
});
|
||||
if (user) handleVote(e, 1);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${
|
||||
isUpVotedByUser ? "border-custom-primary-200 text-custom-primary-200" : "border-custom-border-300"
|
||||
@ -113,9 +114,8 @@ export const IssueVotes: React.FC = observer(() => {
|
||||
type="button"
|
||||
disabled={isSubmitting}
|
||||
onClick={(e) => {
|
||||
userStore.requiredLogin(() => {
|
||||
handleVote(e, -1);
|
||||
});
|
||||
if (user) handleVote(e, -1);
|
||||
// userStore.requiredLogin(() => {});
|
||||
}}
|
||||
className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${
|
||||
isDownVotedByUser ? "border-red-600 text-red-600" : "border-custom-border-300"
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Image from "next/image";
|
||||
// ui
|
||||
@ -7,9 +6,8 @@ import useSWR from "swr";
|
||||
import { Spinner } from "@plane/ui";
|
||||
// components
|
||||
import { AuthRoot, UserLoggedIn } from "@/components/accounts";
|
||||
// mobx
|
||||
import useAuthRedirection from "@/hooks/use-auth-redirection";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
// images
|
||||
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
|
||||
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
|
||||
@ -19,11 +17,7 @@ export const AuthView = observer(() => {
|
||||
// hooks
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store
|
||||
const {
|
||||
user: { currentUser, fetchCurrentUser, loader },
|
||||
} = useMobxStore();
|
||||
// sign in redirection hook
|
||||
const { isRedirecting, handleRedirection } = useAuthRedirection();
|
||||
const { data: currentUser, fetchCurrentUser, isLoading } = useUser();
|
||||
|
||||
// fetching user information
|
||||
useSWR("CURRENT_USER_DETAILS", () => fetchCurrentUser(), {
|
||||
@ -31,13 +25,9 @@ export const AuthView = observer(() => {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
handleRedirection();
|
||||
}, [handleRedirection]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{loader || isRedirecting ? (
|
||||
{isLoading ? (
|
||||
<div className="relative flex h-screen w-screen items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
|
@ -11,8 +11,9 @@ import { IssueSpreadsheetView } from "@/components/issues/board-views/spreadshee
|
||||
import { IssueAppliedFilters } from "@/components/issues/filters/applied-filters/root";
|
||||
import { IssuePeekOverview } from "@/components/issues/peek-overview";
|
||||
// mobx store
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
// assets
|
||||
import SomethingWentWrongImage from "public/something-went-wrong.svg";
|
||||
|
||||
@ -20,18 +21,14 @@ export const ProjectDetailsView = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug, states, labels, priorities, peekId } = router.query;
|
||||
|
||||
const {
|
||||
issue: issueStore,
|
||||
project: projectStore,
|
||||
issueDetails: issueDetailStore,
|
||||
user: userStore,
|
||||
}: RootStore = useMobxStore();
|
||||
const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
|
||||
const { data: currentUser, fetchCurrentUser } = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
if (!userStore.currentUser) {
|
||||
userStore.fetchCurrentUser();
|
||||
if (!currentUser) {
|
||||
fetchCurrentUser();
|
||||
}
|
||||
}, [userStore]);
|
||||
}, [currentUser, fetchCurrentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace_slug && project_slug) {
|
||||
|
2
space/hooks/store/index.ts
Normal file
2
space/hooks/store/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./use-instance";
|
||||
export * from "./user";
|
10
space/hooks/store/use-instance.ts
Normal file
10
space/hooks/store/use-instance.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useContext } from "react";
|
||||
// store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
import { IInstanceStore } from "@/store/instance.store";
|
||||
|
||||
export const useInstance = (): IInstanceStore => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useInstance must be used within StoreProvider");
|
||||
return context.instance;
|
||||
};
|
2
space/hooks/store/user/index.ts
Normal file
2
space/hooks/store/user/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./use-user";
|
||||
export * from "./use-user-profile";
|
10
space/hooks/store/user/use-user-profile.ts
Normal file
10
space/hooks/store/user/use-user-profile.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useContext } from "react";
|
||||
// store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
import { IProfileStore } from "@/store/user/profile.store";
|
||||
|
||||
export const useUserProfile = (): IProfileStore => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useUserProfile must be used within StoreProvider");
|
||||
return context.profile;
|
||||
};
|
10
space/hooks/store/user/use-user.ts
Normal file
10
space/hooks/store/user/use-user.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useContext } from "react";
|
||||
// store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
import { IUserStore } from "@/store/user/index.store";
|
||||
|
||||
export const useUser = (): IUserStore => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useUser must be used within StoreProvider");
|
||||
return context.user;
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
// types
|
||||
import { TUserProfile } from "@plane/types";
|
||||
// mobx store
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
|
||||
type UseAuthRedirectionProps = {
|
||||
error: any | null;
|
||||
isRedirecting: boolean;
|
||||
handleRedirection: () => Promise<void>;
|
||||
};
|
||||
|
||||
const useAuthRedirection = (): UseAuthRedirectionProps => {
|
||||
// states
|
||||
const [isRedirecting, setIsRedirecting] = useState(false);
|
||||
const [error, setError] = useState<any | null>(null);
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { next_path } = router.query;
|
||||
// mobx store
|
||||
const {
|
||||
profile: { fetchUserProfile },
|
||||
} = useMobxStore();
|
||||
|
||||
const isValidURL = (url: string): boolean => {
|
||||
const disallowedSchemes = /^(https?|ftp):\/\//i;
|
||||
return !disallowedSchemes.test(url);
|
||||
};
|
||||
|
||||
const getAuthRedirectionUrl = useCallback(
|
||||
async (profile: TUserProfile | undefined) => {
|
||||
try {
|
||||
if (!profile) return "/";
|
||||
|
||||
const isOnboard = profile.onboarding_step?.profile_complete;
|
||||
|
||||
if (isOnboard) {
|
||||
// if next_path is provided, redirect the user to that url
|
||||
if (next_path) {
|
||||
if (isValidURL(next_path.toString())) {
|
||||
return next_path.toString();
|
||||
} else {
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the user profile is not complete, redirect them to the onboarding page to complete their profile and then redirect them to the next path
|
||||
if (next_path) return `/onboarding?next_path=${next_path}`;
|
||||
else return "/onboarding";
|
||||
}
|
||||
|
||||
return "/";
|
||||
} catch {
|
||||
setIsRedirecting(false);
|
||||
console.error("Error in handleSignInRedirection:", error);
|
||||
setError(error);
|
||||
}
|
||||
},
|
||||
[next_path]
|
||||
);
|
||||
|
||||
const updateUserProfileInfo = useCallback(async () => {
|
||||
setIsRedirecting(true);
|
||||
|
||||
await fetchUserProfile()
|
||||
.then(async (profile) => {
|
||||
if (profile)
|
||||
await getAuthRedirectionUrl(profile)
|
||||
.then((url: string | undefined) => {
|
||||
if (url) {
|
||||
router.push(url);
|
||||
}
|
||||
if (!url || url === "/") setIsRedirecting(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setIsRedirecting(false);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setIsRedirecting(false);
|
||||
});
|
||||
}, [fetchUserProfile, getAuthRedirectionUrl]);
|
||||
|
||||
return {
|
||||
error,
|
||||
isRedirecting,
|
||||
handleRedirection: updateUserProfileInfo,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAuthRedirection;
|
@ -1,5 +1,5 @@
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
const useEditorSuggestions = () => {
|
||||
const { mentionsStore }: RootStore = useMobxStore();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC, ReactNode, useState } from "react";
|
||||
import { FC, ReactNode } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
// ui
|
||||
@ -6,7 +6,7 @@ import { Spinner } from "@plane/ui";
|
||||
// components
|
||||
import { InstanceNotReady } from "@/components/instance";
|
||||
// hooks
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { useInstance } from "@/hooks/store";
|
||||
|
||||
type TInstanceLayout = {
|
||||
children: ReactNode;
|
||||
@ -15,18 +15,12 @@ type TInstanceLayout = {
|
||||
const InstanceLayout: FC<TInstanceLayout> = observer((props) => {
|
||||
const { children } = props;
|
||||
// store
|
||||
const {
|
||||
instanceStore: { isLoading, instance, error, fetchInstanceInfo },
|
||||
} = useMobxStore();
|
||||
// states
|
||||
const [isGodModeEnabled, setIsGodModeEnabled] = useState(false);
|
||||
const handleGodModeStateChange = (state: boolean) => setIsGodModeEnabled(state);
|
||||
const { isLoading, instance, fetchInstanceInfo } = useInstance();
|
||||
|
||||
useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
// loading state
|
||||
if (isLoading)
|
||||
return (
|
||||
<div className="relative flex h-screen w-full items-center justify-center">
|
||||
@ -34,21 +28,7 @@ const InstanceLayout: FC<TInstanceLayout> = observer((props) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
// something went wrong while in the request
|
||||
if (error && error?.status === "error")
|
||||
return (
|
||||
<div className="relative flex h-screen w-screen items-center justify-center">
|
||||
Something went wrong. please try again later
|
||||
</div>
|
||||
);
|
||||
|
||||
// checking if the instance is activated or not
|
||||
if (error && !error?.data?.is_activated) return <InstanceNotReady isGodModeEnabled={false} />;
|
||||
|
||||
// instance is not ready and setup is not done
|
||||
if (instance?.instance?.is_setup_done === false)
|
||||
// if (isGodModeEnabled) return <MiniGodModeForm />;
|
||||
return <InstanceNotReady isGodModeEnabled handleGodModeStateChange={handleGodModeStateChange} />;
|
||||
if (instance?.instance?.is_setup_done === false) return <InstanceNotReady />;
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import { createContext, useContext } from "react";
|
||||
// mobx store
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
let rootStore: RootStore = new RootStore();
|
||||
|
||||
|
19
space/lib/store-context.tsx
Normal file
19
space/lib/store-context.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { ReactElement, createContext } from "react";
|
||||
// mobx store
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
let rootStore = new RootStore();
|
||||
|
||||
export const StoreContext = createContext<RootStore>(rootStore);
|
||||
|
||||
const initializeStore = () => {
|
||||
const singletonRootStore = rootStore ?? new RootStore();
|
||||
if (typeof window === "undefined") return singletonRootStore;
|
||||
if (!rootStore) rootStore = singletonRootStore;
|
||||
return singletonRootStore;
|
||||
};
|
||||
|
||||
export const StoreProvider = ({ children }: { children: ReactElement }) => {
|
||||
const store = initializeStore();
|
||||
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
|
||||
};
|
@ -13,7 +13,7 @@ const nextConfig = {
|
||||
},
|
||||
];
|
||||
},
|
||||
basePath: process.env.NEXT_PUBLIC_DEPLOY_WITH_NGINX === "1" ? "/spaces" : "",
|
||||
basePath: "/spaces",
|
||||
reactStrictMode: false,
|
||||
swcMinify: true,
|
||||
images: {
|
||||
@ -29,7 +29,8 @@ const nextConfig = {
|
||||
};
|
||||
|
||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_SENTRY || "0", 10)) {
|
||||
module.exports = withSentryConfig(nextConfig,
|
||||
module.exports = withSentryConfig(
|
||||
nextConfig,
|
||||
{ silent: true, authToken: process.env.SENTRY_AUTH_TOKEN },
|
||||
{ hideSourceMaps: true }
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ import { Avatar } from "@plane/ui";
|
||||
// components
|
||||
import { OnBoardingForm } from "@/components/accounts/onboarding-form";
|
||||
// mobx
|
||||
import { useMobxStore } from "@/lib/mobx/store-provider";
|
||||
import { useUser, useUserProfile } from "@/hooks/store";
|
||||
// assets
|
||||
import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg";
|
||||
import ProfileSetup from "public/onboarding/profile-setup.svg";
|
||||
@ -22,12 +22,10 @@ const OnBoardingPage = observer(() => {
|
||||
|
||||
// hooks
|
||||
const { resolvedTheme } = useTheme();
|
||||
const {
|
||||
user: userStore,
|
||||
profile: { currentUserProfile, updateUserProfile },
|
||||
} = useMobxStore();
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
const { data: user } = useUser();
|
||||
const { data: currentUserProfile, updateUserProfile } = useUserProfile();
|
||||
|
||||
if (!user) {
|
||||
router.push("/");
|
||||
return <></>;
|
||||
|
@ -1,108 +1,52 @@
|
||||
import axios from "axios";
|
||||
import Cookies from "js-cookie";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
abstract class APIService {
|
||||
protected baseURL: string;
|
||||
protected headers: any = {};
|
||||
private axiosInstance: AxiosInstance;
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.baseURL = baseURL;
|
||||
}
|
||||
|
||||
setCSRFToken(token: string) {
|
||||
Cookies.set("csrf_token", token, { expires: 30 });
|
||||
}
|
||||
|
||||
getCSRFToken() {
|
||||
return Cookies.get("csrf_token");
|
||||
}
|
||||
|
||||
setRefreshToken(token: string) {
|
||||
Cookies.set("refresh_token", token, { expires: 30 });
|
||||
}
|
||||
|
||||
getRefreshToken() {
|
||||
return Cookies.get("refresh_token");
|
||||
}
|
||||
|
||||
purgeRefreshToken() {
|
||||
Cookies.remove("refresh_token", { path: "/" });
|
||||
}
|
||||
|
||||
setAccessToken(token: string) {
|
||||
Cookies.set("access_token", token, { expires: 30 });
|
||||
}
|
||||
|
||||
getAccessToken() {
|
||||
return Cookies.get("access_token");
|
||||
}
|
||||
|
||||
purgeAccessToken() {
|
||||
Cookies.remove("access_token", { path: "/" });
|
||||
}
|
||||
|
||||
getHeaders() {
|
||||
return {
|
||||
Authorization: `Bearer ${this.getAccessToken()}`,
|
||||
};
|
||||
}
|
||||
|
||||
get(url: string, config = {}): Promise<any> {
|
||||
return axios({
|
||||
method: "get",
|
||||
url: this.baseURL + url,
|
||||
headers: this.getAccessToken() ? this.getHeaders() : {},
|
||||
...config,
|
||||
this.axiosInstance = axios.create({
|
||||
baseURL,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
this.setupInterceptors();
|
||||
}
|
||||
|
||||
post(url: string, data = {}, config = {}): Promise<any> {
|
||||
return axios({
|
||||
method: "post",
|
||||
url: this.baseURL + url,
|
||||
data,
|
||||
headers: this.getAccessToken() ? this.getHeaders() : {},
|
||||
...config,
|
||||
withCredentials: true,
|
||||
});
|
||||
private setupInterceptors() {
|
||||
this.axiosInstance.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) window.location.href = "/space/login";
|
||||
return Promise.reject(error.response?.data ?? error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
put(url: string, data = {}, config = {}): Promise<any> {
|
||||
return axios({
|
||||
method: "put",
|
||||
url: this.baseURL + url,
|
||||
data,
|
||||
headers: this.getAccessToken() ? this.getHeaders() : {},
|
||||
...config,
|
||||
withCredentials: true,
|
||||
});
|
||||
get(url: string, params = {}) {
|
||||
return this.axiosInstance.get(url, { params });
|
||||
}
|
||||
|
||||
patch(url: string, data = {}, config = {}): Promise<any> {
|
||||
return axios({
|
||||
method: "patch",
|
||||
url: this.baseURL + url,
|
||||
data,
|
||||
headers: this.getAccessToken() ? this.getHeaders() : {},
|
||||
...config,
|
||||
withCredentials: true,
|
||||
});
|
||||
post(url: string, data: any, config = {}) {
|
||||
return this.axiosInstance.post(url, data, config);
|
||||
}
|
||||
|
||||
delete(url: string, data?: any, config = {}): Promise<any> {
|
||||
return axios({
|
||||
method: "delete",
|
||||
url: this.baseURL + url,
|
||||
data: data,
|
||||
headers: this.getAccessToken() ? this.getHeaders() : {},
|
||||
...config,
|
||||
withCredentials: true,
|
||||
});
|
||||
put(url: string, data: any, config = {}) {
|
||||
return this.axiosInstance.put(url, data, config);
|
||||
}
|
||||
|
||||
patch(url: string, data: any, config = {}) {
|
||||
return this.axiosInstance.patch(url, data, config);
|
||||
}
|
||||
|
||||
delete(url: string, data?: any, config = {}) {
|
||||
return this.axiosInstance.delete(url, { data, ...config });
|
||||
}
|
||||
|
||||
request(config = {}) {
|
||||
return axios(config);
|
||||
return this.axiosInstance(config);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,17 +42,5 @@ export class AuthService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async signOut() {
|
||||
return this.post("/api/sign-out/", { refresh_token: this.getRefreshToken() })
|
||||
.then((response) => {
|
||||
this.purgeAccessToken();
|
||||
this.purgeRefreshToken();
|
||||
return response?.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.purgeAccessToken();
|
||||
this.purgeRefreshToken();
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
async signOut() {}
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ class FileService extends APIService {
|
||||
this.cancelSource = axios.CancelToken.source();
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, {
|
||||
headers: {
|
||||
...this.getHeaders(),
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
cancelToken: this.cancelSource.token,
|
||||
@ -117,7 +116,6 @@ class FileService extends APIService {
|
||||
|
||||
async restoreImage(assetUrlWithWorkspaceId: string): Promise<any> {
|
||||
return this.post(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/restore/`, {
|
||||
headers: this.getHeaders(),
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
.then((response) => response?.status)
|
||||
@ -140,7 +138,6 @@ class FileService extends APIService {
|
||||
async uploadUserFile(file: FormData): Promise<any> {
|
||||
return this.post(`/api/users/file-assets/`, file, {
|
||||
headers: {
|
||||
...this.getHeaders(),
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
})
|
||||
|
@ -3,24 +3,13 @@ import type { IInstance } from "@plane/types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import APIService from "@/services/api.service";
|
||||
import APIService from "@/services/api.service";
|
||||
|
||||
export class InstanceService extends APIService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async requestCSRFToken(): Promise<{ csrf_token: string }> {
|
||||
return this.get("/auth/get-csrf-token/")
|
||||
.then((response) => {
|
||||
this.setCSRFToken(response.data.csrf_token);
|
||||
return response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
async getInstanceInfo(): Promise<IInstance> {
|
||||
return this.get("/api/instances/")
|
||||
.then((response) => response.data)
|
||||
@ -28,21 +17,4 @@ export class InstanceService extends APIService {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
async createInstanceAdmin(data: FormData): Promise<void> {
|
||||
return this.post("/api/instances/admins/sign-in/", {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
"X-CSRFToken": this.getCSRFToken(),
|
||||
},
|
||||
data,
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("response.data", response.data);
|
||||
response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { observable, action, makeObservable, runInAction } from "mobx";
|
||||
import { IInstance } from "@plane/types";
|
||||
// services
|
||||
import { InstanceService } from "@/services/instance.service";
|
||||
import { RootStore } from "./root";
|
||||
// store types
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
type TError = {
|
||||
status: string;
|
||||
@ -27,12 +28,10 @@ export class InstanceStore implements IInstanceStore {
|
||||
isLoading: boolean = true;
|
||||
instance: IInstance | undefined = undefined;
|
||||
error: TError | undefined = undefined;
|
||||
// root store
|
||||
rootStore: RootStore;
|
||||
// services
|
||||
instanceService;
|
||||
|
||||
constructor(_rootStore: any) {
|
||||
constructor(private store: RootStore) {
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
isLoading: observable.ref,
|
||||
@ -41,7 +40,6 @@ export class InstanceStore implements IInstanceStore {
|
||||
// actions
|
||||
fetchInstanceInfo: action,
|
||||
});
|
||||
this.rootStore = _rootStore;
|
||||
// services
|
||||
this.instanceService = new InstanceService();
|
||||
}
|
||||
@ -51,33 +49,13 @@ export class InstanceStore implements IInstanceStore {
|
||||
*/
|
||||
fetchInstanceInfo = async () => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.isLoading = true;
|
||||
this.error = undefined;
|
||||
});
|
||||
|
||||
this.isLoading = true;
|
||||
this.error = undefined;
|
||||
const instance = await this.instanceService.getInstanceInfo();
|
||||
|
||||
const isInstanceNotSetup = (instance: IInstance) => "is_activated" in instance && "is_setup_done" in instance;
|
||||
|
||||
if (isInstanceNotSetup(instance)) {
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.error = {
|
||||
status: "success",
|
||||
message: "Instance is not created in the backend",
|
||||
data: {
|
||||
is_activated: instance?.instance?.is_activated,
|
||||
is_setup_done: instance?.instance?.is_setup_done,
|
||||
},
|
||||
};
|
||||
});
|
||||
} else {
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.instance = instance;
|
||||
});
|
||||
}
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.instance = instance;
|
||||
});
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
|
@ -2,7 +2,7 @@ import { observable, action, computed, makeObservable, runInAction } from "mobx"
|
||||
// services
|
||||
import IssueService from "@/services/issue.service";
|
||||
// store
|
||||
import { RootStore } from "./root";
|
||||
import { RootStore } from "./root.store";
|
||||
// types
|
||||
// import { IssueDetailType, TIssueBoardKeys } from "types/issue";
|
||||
import { IIssue, IIssueState, IIssueLabel } from "types/issue";
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { makeObservable, observable, action, runInAction } from "mobx";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
// store
|
||||
import { RootStore } from "./root";
|
||||
// services
|
||||
import IssueService from "@/services/issue.service";
|
||||
import { IIssue, IVote } from "types/issue";
|
||||
// store types
|
||||
import { RootStore } from "@/store/root.store";
|
||||
// types
|
||||
import { IIssue, IVote } from "@/types/issue";
|
||||
|
||||
export type IPeekMode = "side" | "modal" | "full";
|
||||
|
||||
@ -330,7 +331,7 @@ class IssueDetailStore implements IIssueDetailStore {
|
||||
removeIssueReaction = async (workspaceSlug: string, projectId: string, issueId: string, reactionHex: string) => {
|
||||
try {
|
||||
const newReactions = this.details[issueId].reactions.filter(
|
||||
(_r) => !(_r.reaction === reactionHex && _r.actor_detail.id === this.rootStore.user.currentUser?.id)
|
||||
(_r) => !(_r.reaction === reactionHex && _r.actor_detail.id === this.rootStore.user.data?.id)
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
@ -361,7 +362,7 @@ class IssueDetailStore implements IIssueDetailStore {
|
||||
|
||||
addIssueVote = async (workspaceSlug: string, projectId: string, issueId: string, data: { vote: 1 | -1 }) => {
|
||||
const newVote: IVote = {
|
||||
actor: this.rootStore.user.currentUser?.id ?? "",
|
||||
actor: this.rootStore.user.data?.id ?? "",
|
||||
actor_detail: this.rootStore.user.currentActor,
|
||||
issue: issueId,
|
||||
project: projectId,
|
||||
@ -369,7 +370,7 @@ class IssueDetailStore implements IIssueDetailStore {
|
||||
vote: data.vote,
|
||||
};
|
||||
|
||||
const filteredVotes = this.details[issueId].votes.filter((v) => v.actor !== this.rootStore.user.currentUser?.id);
|
||||
const filteredVotes = this.details[issueId].votes.filter((v) => v.actor !== this.rootStore.user.data?.id);
|
||||
|
||||
try {
|
||||
runInAction(() => {
|
||||
@ -400,7 +401,7 @@ class IssueDetailStore implements IIssueDetailStore {
|
||||
};
|
||||
|
||||
removeIssueVote = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
const newVotes = this.details[issueId].votes.filter((v) => v.actor !== this.rootStore.user.currentUser?.id);
|
||||
const newVotes = this.details[issueId].votes.filter((v) => v.actor !== this.rootStore.user.data?.id);
|
||||
|
||||
try {
|
||||
runInAction(() => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// types
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
export interface IIssueFilterBaseStore {
|
||||
// helper methods
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { action, makeObservable, observable, runInAction, computed } from "mobx";
|
||||
// types
|
||||
import { RootStore } from "@/store/root";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { IIssueFilterOptions, TIssueParams } from "./types";
|
||||
import { handleIssueQueryParamsByLayout } from "./helpers";
|
||||
import { IssueFilterBaseStore } from "./base-issue-filter.store";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IMentionHighlight } from "@plane/lite-text-editor";
|
||||
import { RootStore } from "./root";
|
||||
import { computed, makeObservable } from "mobx";
|
||||
import { IMentionHighlight } from "@plane/lite-text-editor";
|
||||
import { RootStore } from "./root.store";
|
||||
|
||||
export interface IMentionsStore {
|
||||
// mentionSuggestions: IMentionSuggestion[];
|
||||
@ -37,7 +37,7 @@ export class MentionsStore implements IMentionsStore {
|
||||
// }
|
||||
|
||||
get mentionHighlights() {
|
||||
const user = this.rootStore.user.currentUser;
|
||||
const user = this.rootStore.user.data;
|
||||
return user ? [user.id] : [];
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
import { observable, action, makeObservable, runInAction } from "mobx";
|
||||
// service
|
||||
import ProjectService from "@/services/project.service";
|
||||
import { TIssueBoardKeys } from "types/issue";
|
||||
// types
|
||||
import { IWorkspace, IProject, IProjectSettings } from "types/project";
|
||||
import { TIssueBoardKeys } from "@/types/issue";
|
||||
import { IWorkspace, IProject, IProjectSettings } from "@/types/project";
|
||||
|
||||
export interface IProjectStore {
|
||||
loader: boolean;
|
||||
@ -18,7 +18,7 @@ export interface IProjectStore {
|
||||
setActiveBoard: (value: TIssueBoardKeys) => void;
|
||||
}
|
||||
|
||||
class ProjectStore implements IProjectStore {
|
||||
export class ProjectStore implements IProjectStore {
|
||||
loader: boolean = false;
|
||||
error: any | null = null;
|
||||
// data
|
||||
@ -61,15 +61,15 @@ class ProjectStore implements IProjectStore {
|
||||
const response = await this.projectService.getProjectSettings(workspace_slug, project_slug);
|
||||
|
||||
if (response) {
|
||||
const _project: IProject = { ...response?.project_details };
|
||||
const _workspace: IWorkspace = { ...response?.workspace_detail };
|
||||
const _viewOptions = { ...response?.views };
|
||||
const _deploySettings = { ...response };
|
||||
const currentProject: IProject = { ...response?.project_details };
|
||||
const currentWorkspace: IWorkspace = { ...response?.workspace_detail };
|
||||
const currentViewOptions = { ...response?.views };
|
||||
const currentDeploySettings = { ...response };
|
||||
runInAction(() => {
|
||||
this.project = _project;
|
||||
this.workspace = _workspace;
|
||||
this.viewOptions = _viewOptions;
|
||||
this.deploySettings = _deploySettings;
|
||||
this.project = currentProject;
|
||||
this.workspace = currentWorkspace;
|
||||
this.viewOptions = currentViewOptions;
|
||||
this.deploySettings = currentDeploySettings;
|
||||
this.loader = false;
|
||||
});
|
||||
}
|
||||
@ -85,5 +85,3 @@ class ProjectStore implements IProjectStore {
|
||||
this.activeBoard = boardValue;
|
||||
};
|
||||
}
|
||||
|
||||
export default ProjectStore;
|
||||
|
@ -1,35 +1,52 @@
|
||||
// mobx lite
|
||||
import { enableStaticRendering } from "mobx-react-lite";
|
||||
// store imports
|
||||
import { InstanceStore } from "./instance.store";
|
||||
import { IInstanceStore, InstanceStore } from "@/store/instance.store";
|
||||
import { IProjectStore, ProjectStore } from "@/store/project";
|
||||
import { IUserStore, UserStore } from "@/store/user/index.store";
|
||||
import { IProfileStore, ProfileStore } from "@/store/user/profile.store";
|
||||
|
||||
import IssueStore, { IIssueStore } from "./issue";
|
||||
import IssueDetailStore, { IIssueDetailStore } from "./issue_details";
|
||||
import { IIssuesFilterStore, IssuesFilterStore } from "./issues/issue-filters.store";
|
||||
import { IMentionsStore, MentionsStore } from "./mentions.store";
|
||||
import ProfileStore from "./profile";
|
||||
import ProjectStore, { IProjectStore } from "./project";
|
||||
import UserStore from "./user";
|
||||
|
||||
enableStaticRendering(typeof window === "undefined");
|
||||
|
||||
export class RootStore {
|
||||
instanceStore: InstanceStore;
|
||||
user: UserStore;
|
||||
profile: ProfileStore;
|
||||
instance: IInstanceStore;
|
||||
user: IUserStore;
|
||||
profile: IProfileStore;
|
||||
project: IProjectStore;
|
||||
|
||||
issue: IIssueStore;
|
||||
issueDetails: IIssueDetailStore;
|
||||
project: IProjectStore;
|
||||
mentionsStore: IMentionsStore;
|
||||
issuesFilter: IIssuesFilterStore;
|
||||
|
||||
constructor() {
|
||||
this.instanceStore = new InstanceStore(this);
|
||||
this.instance = new InstanceStore(this);
|
||||
this.user = new UserStore(this);
|
||||
this.profile = new ProfileStore(this);
|
||||
this.issue = new IssueStore(this);
|
||||
this.project = new ProjectStore(this);
|
||||
|
||||
this.issue = new IssueStore(this);
|
||||
this.issueDetails = new IssueDetailStore(this);
|
||||
this.mentionsStore = new MentionsStore(this);
|
||||
this.issuesFilter = new IssuesFilterStore(this);
|
||||
}
|
||||
|
||||
resetOnSignOut = () => {
|
||||
localStorage.setItem("theme", "system");
|
||||
|
||||
this.instance = new InstanceStore(this);
|
||||
this.user = new UserStore(this);
|
||||
this.profile = new ProfileStore(this);
|
||||
this.project = new ProjectStore(this);
|
||||
|
||||
this.issue = new IssueStore(this);
|
||||
this.issueDetails = new IssueDetailStore(this);
|
||||
this.mentionsStore = new MentionsStore(this);
|
||||
this.issuesFilter = new IssuesFilterStore(this);
|
||||
};
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
// mobx
|
||||
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
||||
// types
|
||||
import { IUser } from "@plane/types";
|
||||
// service
|
||||
import { UserService } from "@/services/user.service";
|
||||
|
||||
export interface IUserStore {
|
||||
loader: boolean;
|
||||
error: any | null;
|
||||
currentUser: any | null;
|
||||
fetchCurrentUser: () => Promise<IUser | undefined>;
|
||||
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser>;
|
||||
currentActor: () => any;
|
||||
}
|
||||
|
||||
class UserStore implements IUserStore {
|
||||
loader: boolean = false;
|
||||
error: any | null = null;
|
||||
|
||||
currentUser: IUser | null = null;
|
||||
// root store
|
||||
rootStore;
|
||||
// service
|
||||
userService;
|
||||
|
||||
constructor(_rootStore: any) {
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
error: observable.ref,
|
||||
|
||||
currentUser: observable.ref,
|
||||
// actions
|
||||
setCurrentUser: action,
|
||||
// computed
|
||||
currentActor: computed,
|
||||
});
|
||||
this.rootStore = _rootStore;
|
||||
this.userService = new UserService();
|
||||
}
|
||||
|
||||
setCurrentUser = (user: any) => {
|
||||
runInAction(() => {
|
||||
this.currentUser = { ...user };
|
||||
});
|
||||
};
|
||||
|
||||
get currentActor(): any {
|
||||
return {
|
||||
avatar: this.currentUser?.avatar,
|
||||
display_name: this.currentUser?.display_name,
|
||||
first_name: this.currentUser?.first_name,
|
||||
id: this.currentUser?.id,
|
||||
is_bot: false,
|
||||
last_name: this.currentUser?.last_name,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param callback
|
||||
* @description A wrapper function to check user authentication; it redirects to the login page if not authenticated, otherwise, it executes a callback.
|
||||
* @example this.requiredLogin(() => { // do something });
|
||||
*/
|
||||
|
||||
requiredLogin = (callback: () => void) => {
|
||||
if (this.currentUser) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPath = window.location.pathname + window.location.search;
|
||||
this.fetchCurrentUser()
|
||||
.then(() => {
|
||||
if (!this.currentUser) window.location.href = `/?next_path=${currentPath}`;
|
||||
else callback();
|
||||
})
|
||||
.catch(() => (window.location.href = `/?next_path=${currentPath}`));
|
||||
};
|
||||
|
||||
fetchCurrentUser = async () => {
|
||||
try {
|
||||
this.loader = true;
|
||||
this.error = null;
|
||||
const response = await this.userService.currentUser();
|
||||
|
||||
if (response)
|
||||
runInAction(() => {
|
||||
this.loader = false;
|
||||
this.currentUser = response;
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch current user", error);
|
||||
this.loader = false;
|
||||
this.error = error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current user
|
||||
* @param data
|
||||
* @returns Promise<IUser>
|
||||
*/
|
||||
updateCurrentUser = async (data: Partial<IUser>) => {
|
||||
try {
|
||||
const user = await this.userService.updateUser(data);
|
||||
runInAction(() => {
|
||||
this.currentUser = user;
|
||||
});
|
||||
return user;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default UserStore;
|
164
space/store/user/index.store.ts
Normal file
164
space/store/user/index.store.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import set from "lodash/set";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
// types
|
||||
import { IUser } from "@plane/types";
|
||||
// helpers
|
||||
// import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import { AuthService } from "@/services/authentication.service";
|
||||
import { UserService } from "@/services/user.service";
|
||||
// stores
|
||||
import { RootStore } from "@/store/root.store";
|
||||
import { ProfileStore, IProfileStore } from "@/store/user/profile.store";
|
||||
import { ActorDetail } from "@/types/issue";
|
||||
|
||||
type TUserErrorStatus = {
|
||||
status: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export interface IUserStore {
|
||||
// observables
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
error: TUserErrorStatus | undefined;
|
||||
data: IUser | undefined;
|
||||
// store observables
|
||||
userProfile: IProfileStore;
|
||||
// computed
|
||||
currentActor: ActorDetail;
|
||||
// actions
|
||||
fetchCurrentUser: () => Promise<IUser | undefined>;
|
||||
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser | undefined>;
|
||||
signOut: () => Promise<void>;
|
||||
}
|
||||
|
||||
export class UserStore implements IUserStore {
|
||||
// observables
|
||||
isAuthenticated: boolean = false;
|
||||
isLoading: boolean = false;
|
||||
error: TUserErrorStatus | undefined = undefined;
|
||||
data: IUser | undefined = undefined;
|
||||
// store observables
|
||||
userProfile: IProfileStore;
|
||||
// service
|
||||
userService: UserService;
|
||||
authService: AuthService;
|
||||
|
||||
constructor(private store: RootStore) {
|
||||
// stores
|
||||
this.userProfile = new ProfileStore(store);
|
||||
// service
|
||||
this.userService = new UserService();
|
||||
this.authService = new AuthService();
|
||||
// observables
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
isAuthenticated: observable.ref,
|
||||
isLoading: observable.ref,
|
||||
error: observable,
|
||||
// model observables
|
||||
data: observable,
|
||||
userProfile: observable,
|
||||
// computed
|
||||
currentActor: computed,
|
||||
// actions
|
||||
fetchCurrentUser: action,
|
||||
updateCurrentUser: action,
|
||||
signOut: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
get currentActor(): ActorDetail {
|
||||
return {
|
||||
id: this.data?.id,
|
||||
first_name: this.data?.first_name,
|
||||
last_name: this.data?.last_name,
|
||||
display_name: this.data?.display_name,
|
||||
avatar: this.data?.avatar || undefined,
|
||||
is_bot: false,
|
||||
};
|
||||
}
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description fetches the current user
|
||||
* @returns {Promise<IUser>}
|
||||
*/
|
||||
fetchCurrentUser = async (): Promise<IUser> => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.isLoading = true;
|
||||
this.error = undefined;
|
||||
});
|
||||
const user = await this.userService.currentUser();
|
||||
if (user && user?.id) {
|
||||
await this.userProfile.fetchUserProfile();
|
||||
runInAction(() => {
|
||||
this.data = user;
|
||||
this.isLoading = false;
|
||||
this.isAuthenticated = true;
|
||||
});
|
||||
} else
|
||||
runInAction(() => {
|
||||
this.data = user;
|
||||
this.isLoading = false;
|
||||
this.isAuthenticated = false;
|
||||
});
|
||||
return user;
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.isAuthenticated = false;
|
||||
this.error = {
|
||||
status: "user-fetch-error",
|
||||
message: "Failed to fetch current user",
|
||||
};
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description updates the current user
|
||||
* @param data
|
||||
* @returns {Promise<IUser>}
|
||||
*/
|
||||
updateCurrentUser = async (data: Partial<IUser>): Promise<IUser> => {
|
||||
const currentUserData = this.data;
|
||||
try {
|
||||
if (currentUserData) {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
const userKey: keyof IUser = key as keyof IUser;
|
||||
if (this.data) set(this.data, userKey, data[userKey]);
|
||||
});
|
||||
}
|
||||
const user = await this.userService.updateUser(data);
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (currentUserData) {
|
||||
Object.keys(currentUserData).forEach((key: string) => {
|
||||
const userKey: keyof IUser = key as keyof IUser;
|
||||
if (this.data) set(this.data, userKey, currentUserData[userKey]);
|
||||
});
|
||||
}
|
||||
runInAction(() => {
|
||||
this.error = {
|
||||
status: "user-update-error",
|
||||
message: "Failed to update current user",
|
||||
};
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description signs out the current user
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
signOut = async (): Promise<void> => {
|
||||
// await this.authService.signOut(API_BASE_URL);
|
||||
// this.store.resetOnSignOut();
|
||||
};
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
import set from "lodash/set";
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
// services
|
||||
import { TUserProfile } from "@plane/types";
|
||||
import { UserService } from "services/user.service";
|
||||
// types
|
||||
import { RootStore } from "./root";
|
||||
// services
|
||||
import { UserService } from "@/services/user.service";
|
||||
// store types
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
type TError = {
|
||||
status: string;
|
||||
@ -13,16 +14,17 @@ type TError = {
|
||||
export interface IProfileStore {
|
||||
// observables
|
||||
isLoading: boolean;
|
||||
currentUserProfile: TUserProfile;
|
||||
error: TError | undefined;
|
||||
data: TUserProfile;
|
||||
// actions
|
||||
fetchUserProfile: () => Promise<TUserProfile | undefined>;
|
||||
updateUserProfile: (currentUserProfile: Partial<TUserProfile>) => Promise<void>;
|
||||
updateUserProfile: (data: Partial<TUserProfile>) => Promise<TUserProfile | undefined>;
|
||||
}
|
||||
|
||||
class ProfileStore implements IProfileStore {
|
||||
export class ProfileStore implements IProfileStore {
|
||||
isLoading: boolean = false;
|
||||
currentUserProfile: TUserProfile = {
|
||||
error: TError | undefined = undefined;
|
||||
data: TUserProfile = {
|
||||
id: undefined,
|
||||
user: undefined,
|
||||
role: undefined,
|
||||
@ -52,28 +54,29 @@ class ProfileStore implements IProfileStore {
|
||||
created_at: "",
|
||||
updated_at: "",
|
||||
};
|
||||
error: TError | undefined = undefined;
|
||||
// root store
|
||||
rootStore;
|
||||
|
||||
// services
|
||||
userService: UserService;
|
||||
|
||||
constructor(_rootStore: RootStore) {
|
||||
constructor(public store: RootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
isLoading: observable.ref,
|
||||
currentUserProfile: observable,
|
||||
error: observable,
|
||||
data: observable,
|
||||
// actions
|
||||
fetchUserProfile: action,
|
||||
updateUserProfile: action,
|
||||
});
|
||||
this.rootStore = _rootStore;
|
||||
// services
|
||||
this.userService = new UserService();
|
||||
}
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description fetches user profile information
|
||||
* @returns {Promise<TUserProfile | undefined>}
|
||||
*/
|
||||
fetchUserProfile = async () => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
@ -83,45 +86,49 @@ class ProfileStore implements IProfileStore {
|
||||
const userProfile = await this.userService.getCurrentUserProfile();
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.currentUserProfile = userProfile;
|
||||
this.data = userProfile;
|
||||
});
|
||||
|
||||
return userProfile;
|
||||
} catch (error) {
|
||||
console.log("Failed to fetch profile details");
|
||||
runInAction(() => {
|
||||
this.isLoading = true;
|
||||
this.isLoading = false;
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Failed to fetch instance info",
|
||||
status: "user-profile-fetch-error",
|
||||
message: "Failed to fetch user profile",
|
||||
};
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateUserProfile = async (currentUserProfile: Partial<TUserProfile>) => {
|
||||
/**
|
||||
* @description updated the user profile information
|
||||
* @param {Partial<TUserProfile>} data
|
||||
* @returns {Promise<TUserProfile | undefined>}
|
||||
*/
|
||||
updateUserProfile = async (data: Partial<TUserProfile>) => {
|
||||
const currentUserProfileData = this.data;
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.isLoading = true;
|
||||
this.error = undefined;
|
||||
});
|
||||
const userProfile = await this.userService.updateCurrentUserProfile(currentUserProfile);
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.currentUserProfile = userProfile;
|
||||
});
|
||||
if (currentUserProfileData) {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
const userKey: keyof TUserProfile = key as keyof TUserProfile;
|
||||
if (this.data) set(this.data, userKey, data[userKey]);
|
||||
});
|
||||
}
|
||||
const userProfile = await this.userService.updateCurrentUserProfile(data);
|
||||
return userProfile;
|
||||
} catch (error) {
|
||||
console.log("Failed to fetch profile details");
|
||||
if (currentUserProfileData) {
|
||||
Object.keys(currentUserProfileData).forEach((key: string) => {
|
||||
const userKey: keyof TUserProfile = key as keyof TUserProfile;
|
||||
if (this.data) set(this.data, userKey, currentUserProfileData[userKey]);
|
||||
});
|
||||
}
|
||||
runInAction(() => {
|
||||
this.isLoading = true;
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Failed to fetch instance info",
|
||||
status: "user-profile-update-error",
|
||||
message: "Failed to update user profile",
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default ProfileStore;
|
Loading…
Reference in New Issue
Block a user