mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: mobx changes
This commit is contained in:
parent
6246a740d5
commit
240acc6cd0
6
apps/space/components/accounts/index.ts
Normal file
6
apps/space/components/accounts/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from "./email-code-form";
|
||||
export * from "./email-password-form";
|
||||
export * from "./email-reset-password-form";
|
||||
export * from "./github-login-button";
|
||||
export * from "./google-login";
|
||||
export * from "./onboarding-form";
|
@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
// mobx react lite
|
||||
import { FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { IssueBlockPriority } from "components/issues/board-views/block-priority";
|
||||
@ -11,10 +10,32 @@ import { IssueBlockDueDate } from "components/issues/board-views/block-due-date"
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// interfaces
|
||||
import { IIssue } from "types/issue";
|
||||
// store
|
||||
import { RootStore } from "store/root";
|
||||
|
||||
export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
|
||||
const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
|
||||
export const IssueListBlock: FC<{ issue: IIssue }> = observer((props) => {
|
||||
const { issue } = props;
|
||||
// store
|
||||
const { project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug, board } = router.query;
|
||||
|
||||
const handleBlockClick = () => {
|
||||
issueDetailStore.setPeekId(issue.id);
|
||||
router.replace(
|
||||
{
|
||||
pathname: `/${workspace_slug?.toString()}/${project_slug}`,
|
||||
query: {
|
||||
board: board?.toString(),
|
||||
peekId: issue.id,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true }
|
||||
);
|
||||
// router.push(`/${workspace_slug?.toString()}/${project_slug}?board=${board?.toString()}&peekId=${issue.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center px-9 py-3.5 relative gap-10 border-b border-custom-border-200 bg-custom-background-100 last:border-b-0">
|
||||
@ -25,12 +46,7 @@ export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
|
||||
</div>
|
||||
{/* name */}
|
||||
<div className="h-full line-clamp-1 w-full overflow-ellipsis cursor-pointer">
|
||||
<p
|
||||
onClick={() => {
|
||||
issueDetailStore.setPeekId(issue.id);
|
||||
}}
|
||||
className="text-[0.825rem] font-medium text-sm truncate text-custom-text-100"
|
||||
>
|
||||
<p onClick={handleBlockClick} className="text-[0.825rem] font-medium text-sm truncate text-custom-text-100">
|
||||
{issue.name}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -1,6 +1,4 @@
|
||||
"use client";
|
||||
|
||||
// mobx react lite
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { IssueListHeader } from "components/issues/board-views/list/header";
|
||||
@ -9,11 +7,13 @@ import { IssueListBlock } from "components/issues/board-views/list/block";
|
||||
import { IIssueState, IIssue } from "types/issue";
|
||||
// mobx hook
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// store
|
||||
import { RootStore } from "store/root";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
export const IssueListView = observer(() => {
|
||||
const { issue: issueStore }: RootStore = useMobxStore();
|
||||
console.log("issueStore", issueStore.states);
|
||||
|
||||
return (
|
||||
<>
|
||||
{issueStore?.states &&
|
||||
|
@ -11,13 +11,7 @@ import { SecondaryButton } from "components/ui";
|
||||
// types
|
||||
import { Comment } from "types";
|
||||
// components
|
||||
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
|
||||
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>((props, ref) => (
|
||||
<Tiptap {...props} forwardedRef={ref} />
|
||||
));
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
import { TipTapEditor } from "components/tiptap";
|
||||
|
||||
const defaultValues: Partial<Comment> = {
|
||||
comment_json: "",
|
||||
@ -25,12 +19,11 @@ const defaultValues: Partial<Comment> = {
|
||||
};
|
||||
|
||||
type Props = {
|
||||
issueId: string | null;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const AddComment: React.FC<Props> = observer((props) => {
|
||||
const { issueId, disabled = false } = props;
|
||||
const { disabled = false } = props;
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
@ -44,7 +37,9 @@ export const AddComment: React.FC<Props> = observer((props) => {
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug } = router.query as { workspace_slug: string; project_slug: string };
|
||||
|
||||
const { issue: issueStore, user: userStore } = useMobxStore();
|
||||
const { issue: issueStore, user: userStore, issueDetails: issueDetailStore } = useMobxStore();
|
||||
|
||||
const issueId = issueDetailStore.peekId;
|
||||
|
||||
const editorRef = useRef<any>(null);
|
||||
|
||||
@ -61,8 +56,8 @@ export const AddComment: React.FC<Props> = observer((props) => {
|
||||
)
|
||||
return;
|
||||
|
||||
await issueStore
|
||||
.createIssueCommentAsync(workspace_slug, project_slug, issueId, formData)
|
||||
await issueDetailStore
|
||||
.addIssueComment(workspace_slug, project_slug, issueId, formData)
|
||||
.then(() => {
|
||||
reset(defaultValues);
|
||||
editorRef.current?.clearEditor();
|
||||
@ -83,7 +78,7 @@ export const AddComment: React.FC<Props> = observer((props) => {
|
||||
name="comment_html"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TiptapEditor
|
||||
<TipTapEditor
|
||||
workspaceSlug={workspace_slug as string}
|
||||
ref={editorRef}
|
||||
value={
|
||||
|
@ -17,13 +17,7 @@ import { ChatBubbleLeftEllipsisIcon, CheckIcon, XMarkIcon, EllipsisVerticalIcon
|
||||
import { timeAgo } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { Comment } from "types";
|
||||
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
|
||||
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>((props, ref) => (
|
||||
<Tiptap {...props} forwardedRef={ref} />
|
||||
));
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
import { TipTapEditor } from "components/tiptap";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
@ -124,7 +118,7 @@ export const CommentCard: React.FC<Props> = observer((props) => {
|
||||
className={`flex-col gap-2 ${isEditing ? "flex" : "hidden"}`}
|
||||
>
|
||||
<div>
|
||||
<TiptapEditor
|
||||
<TipTapEditor
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
ref={editorRef}
|
||||
value={watch("comment_html")}
|
||||
@ -154,7 +148,7 @@ export const CommentCard: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
</form>
|
||||
<div className={`${isEditing ? "hidden" : ""}`}>
|
||||
<TiptapEditor
|
||||
<TipTapEditor
|
||||
workspaceSlug={workspaceSlug as string}
|
||||
ref={showEditorRef}
|
||||
value={comment.comment_html}
|
||||
|
@ -14,25 +14,24 @@ type Props = {
|
||||
|
||||
export const PeekOverviewIssueActivity: React.FC<Props> = observer((props) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
const { workspace_slug } = router.query;
|
||||
|
||||
const { issueDetails: issueDetailStore, user: userStore } = useMobxStore();
|
||||
|
||||
const issueId = issueDetailStore?.peekId;
|
||||
const comments = issueDetailStore?.details[issueId ?? ""]?.comments ?? [];
|
||||
const comments = issueDetailStore.details[issueDetailStore.peekId || ""]?.comments || [];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="font-medium">Activity</h4>
|
||||
{workspaceSlug && (
|
||||
{workspace_slug && (
|
||||
<div className="mt-4">
|
||||
<div className="space-y-4">
|
||||
{comments.map((comment: any) => (
|
||||
<CommentCard comment={comment} workspaceSlug={workspaceSlug?.toString()} />
|
||||
<CommentCard comment={comment} workspaceSlug={workspace_slug?.toString()} />
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<AddComment disabled={!userStore.currentUser} issueId={issueId} />
|
||||
<AddComment disabled={!userStore.currentUser} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// components
|
||||
import { IssueReactions } from "components/issues/peek-overview";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
@ -9,22 +9,21 @@ import { groupReactions, renderEmoji } from "helpers/emoji.helper";
|
||||
import { ReactionSelector } from "components/ui";
|
||||
|
||||
export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
|
||||
const { workspace_slug, project_slug } = router.query as { workspace_slug: string; project_slug: string };
|
||||
|
||||
const { user: userStore, issue: issueStore } = useMobxStore();
|
||||
// store
|
||||
const { user: userStore, issue: issueStore, issueDetails: issueDetailsStore } = useMobxStore();
|
||||
|
||||
const user = userStore?.currentUser;
|
||||
const issueId = issueStore.activePeekOverviewIssueId;
|
||||
|
||||
const reactions = issueId ? issueStore.issue_detail[issueId]?.reactions || [] : [];
|
||||
const issueId = issueDetailsStore.peekId;
|
||||
const reactions = issueId ? issueDetailsStore.details[issueId]?.reactions || [] : [];
|
||||
const groupedReactions = groupReactions(reactions, "reaction");
|
||||
|
||||
const handleReactionClick = (reactionHexa: string) => {
|
||||
if (!workspace_slug || !project_slug || !issueId) return;
|
||||
|
||||
const userReaction = reactions?.find((r) => r.created_by === user?.id && r.reaction === reactionHexa);
|
||||
const userReaction = reactions?.find((r: any) => r.created_by === user?.id && r.reaction === reactionHexa);
|
||||
|
||||
if (userReaction)
|
||||
issueStore.deleteIssueReactionAsync(workspace_slug, userReaction.project, userReaction.issue, reactionHexa);
|
||||
@ -34,12 +33,6 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (user) return;
|
||||
|
||||
userStore.getUserAsync();
|
||||
}, [user, userStore]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReactionSelector
|
||||
@ -63,7 +56,7 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
}}
|
||||
key={reaction}
|
||||
className={`flex items-center gap-1 text-custom-text-100 text-sm h-full px-2 py-1 rounded-md border ${
|
||||
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
|
||||
reactions?.some((r: any) => r.actor === user?.id && r.reaction === reaction)
|
||||
? "bg-custom-primary-100/10 border-custom-primary-100"
|
||||
: "bg-custom-background-80 border-transparent"
|
||||
}`}
|
||||
@ -71,7 +64,7 @@ export const IssueEmojiReactions: React.FC = observer(() => {
|
||||
<span>{renderEmoji(reaction)}</span>
|
||||
<span
|
||||
className={
|
||||
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
|
||||
reactions?.some((r: any) => r.actor === user?.id && r.reaction === reaction)
|
||||
? "text-custom-primary-100"
|
||||
: ""
|
||||
}
|
||||
|
@ -1,18 +1,25 @@
|
||||
"use client";
|
||||
|
||||
// ui
|
||||
import { IssueEmojiReactions, IssueVotes } from "components/issues/peek-overview";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
export const IssueReactions: React.FC = () => (
|
||||
<div className="flex gap-3 items-center mt-4">
|
||||
<div className="flex gap-2 items-center">
|
||||
<IssueVotes />
|
||||
export const IssueReactions: React.FC = () => {
|
||||
const { project: projectStore } = useMobxStore();
|
||||
return (
|
||||
<div className="flex gap-3 items-center mt-4">
|
||||
{projectStore?.deploySettings?.votes && (
|
||||
<>
|
||||
<div className="flex gap-2 items-center">
|
||||
<IssueVotes />
|
||||
</div>
|
||||
|
||||
<div className="w-0.5 h-8 bg-custom-background-200" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{projectStore?.deploySettings?.reactions && (
|
||||
<div className="flex gap-2 items-center">
|
||||
<IssueEmojiReactions />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-0.5 h-8 bg-custom-background-200" />
|
||||
|
||||
<div className="flex gap-2 items-center">
|
||||
<IssueEmojiReactions />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
@ -18,20 +18,19 @@ export const IssuePeekOverview: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, onClose } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug } = router.query;
|
||||
const { workspace_slug, project_slug, peekId } = router.query;
|
||||
// store
|
||||
const { issueDetails: issueDetailStore } = useMobxStore();
|
||||
const { issueDetails: issueDetailStore, issue: issueStore } = useMobxStore();
|
||||
|
||||
const issueDetails = issueDetailStore.peekId ? issueDetailStore.details[issueDetailStore.peekId] : null;
|
||||
console.log("issueDetails", issueDetails);
|
||||
const issueDetails = issueDetailStore.peekId && peekId ? issueDetailStore.details[peekId.toString()] : null;
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace_slug && project_slug && issueDetailStore.peekId) {
|
||||
if (workspace_slug && project_slug && peekId && issueStore.issues && issueStore.issues.length > 0) {
|
||||
if (!issueDetails) {
|
||||
issueDetailStore.fetchIssueDetails(workspace_slug.toString(), project_slug.toString(), issueDetailStore.peekId);
|
||||
issueDetailStore.fetchIssueDetails(workspace_slug.toString(), project_slug.toString(), peekId.toString());
|
||||
}
|
||||
}
|
||||
}, [workspace_slug, project_slug, issueDetailStore, issueDetails]);
|
||||
}, [workspace_slug, project_slug, issueDetailStore, issueDetails, peekId, issueStore.issues]);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
} from "components/issues/peek-overview";
|
||||
// types
|
||||
import { IIssue } from "types/issue";
|
||||
import { RootStore } from "store/root";
|
||||
|
||||
type Props = {
|
||||
handleClose: () => void;
|
||||
@ -20,6 +21,8 @@ type Props = {
|
||||
export const SidePeekView: React.FC<Props> = observer((props) => {
|
||||
const { handleClose, issueDetails } = props;
|
||||
|
||||
const { project: projectStore } = useMobxStore();
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col overflow-hidden">
|
||||
<div className="w-full p-5">
|
||||
@ -38,9 +41,11 @@ export const SidePeekView: React.FC<Props> = observer((props) => {
|
||||
{/* divider */}
|
||||
<div className="h-[1] w-full border-t border-custom-border-200 my-5" />
|
||||
{/* issue activity/comments */}
|
||||
<div className="w-full pb-5">
|
||||
<PeekOverviewIssueActivity issueDetails={issueDetails} />
|
||||
</div>
|
||||
{projectStore?.deploySettings?.comments && (
|
||||
<div className="w-full pb-5">
|
||||
<PeekOverviewIssueActivity issueDetails={issueDetails} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -3,10 +3,10 @@ import { useDebouncedCallback } from "use-debounce";
|
||||
import { EditorBubbleMenu } from "./bubble-menu";
|
||||
import { TiptapExtensions } from "./extensions";
|
||||
import { TiptapEditorProps } from "./props";
|
||||
import { useImperativeHandle, useRef } from "react";
|
||||
import { useImperativeHandle, useRef, forwardRef } from "react";
|
||||
import { ImageResizer } from "./extensions/image-resize";
|
||||
|
||||
export interface ITiptapRichTextEditor {
|
||||
export interface ITipTapRichTextEditor {
|
||||
value: string;
|
||||
noBorder?: boolean;
|
||||
borderOnFocus?: boolean;
|
||||
@ -21,7 +21,7 @@ export interface ITiptapRichTextEditor {
|
||||
debouncedUpdatesEnabled?: boolean;
|
||||
}
|
||||
|
||||
const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
const Tiptap = (props: ITipTapRichTextEditor) => {
|
||||
const {
|
||||
onChange,
|
||||
debouncedUpdatesEnabled,
|
||||
@ -73,9 +73,10 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
}, 500);
|
||||
}, 1000);
|
||||
|
||||
const editorClassNames = `relative w-full max-w-screen-lg sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md
|
||||
${noBorder ? "" : "border border-custom-border-200"} ${borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
|
||||
} ${customClassName}`;
|
||||
const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md
|
||||
${noBorder ? "" : "border border-custom-border-200"} ${
|
||||
borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
|
||||
} ${customClassName}`;
|
||||
|
||||
if (!editor) return null;
|
||||
editorRef.current = editor;
|
||||
@ -97,4 +98,10 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Tiptap;
|
||||
const TipTapEditor = forwardRef<ITipTapRichTextEditor, ITipTapRichTextEditor>((props, ref) => (
|
||||
<Tiptap {...props} forwardedRef={ref} />
|
||||
));
|
||||
|
||||
TipTapEditor.displayName = "TipTapEditor";
|
||||
|
||||
export { TipTapEditor };
|
||||
|
@ -14,12 +14,23 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
export const ProjectDetailsView = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspace_slug, project_slug, states, labels, priorities } = router.query;
|
||||
const { workspace_slug, project_slug, states, labels, priorities, board, peekId } = router.query;
|
||||
|
||||
const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
|
||||
const {
|
||||
issue: issueStore,
|
||||
project: projectStore,
|
||||
issueDetails: issueDetailStore,
|
||||
user: userStore,
|
||||
}: RootStore = useMobxStore();
|
||||
|
||||
const activeIssueId = issueDetailStore.peekId;
|
||||
|
||||
useEffect(() => {
|
||||
if (!userStore.currentUser) {
|
||||
userStore.fetchCurrentUser();
|
||||
}
|
||||
}, [userStore]);
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace_slug && project_slug) {
|
||||
const params = {
|
||||
@ -31,11 +42,29 @@ export const ProjectDetailsView = observer(() => {
|
||||
}
|
||||
}, [workspace_slug, project_slug, issueStore, states, labels, priorities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (peekId && workspace_slug && project_slug) {
|
||||
issueDetailStore.setPeekId(peekId.toString());
|
||||
}
|
||||
}, [peekId, issueDetailStore, project_slug, workspace_slug]);
|
||||
|
||||
const handlePeekClose = () => {
|
||||
issueDetailStore.setPeekId(null);
|
||||
router.replace(
|
||||
{
|
||||
pathname: `/${workspace_slug?.toString()}/${project_slug}`,
|
||||
query: {
|
||||
...(board && { board: board.toString() }),
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true }
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full overflow-hidden">
|
||||
{workspace_slug && (
|
||||
<IssuePeekOverview isOpen={Boolean(activeIssueId)} onClose={() => issueDetailStore.setPeekId(null)} />
|
||||
)}
|
||||
{workspace_slug && <IssuePeekOverview isOpen={Boolean(activeIssueId)} onClose={handlePeekClose} />}
|
||||
|
||||
{issueStore?.loader && !issueStore.issues ? (
|
||||
<div className="text-sm text-center py-10 text-custom-text-100">Loading...</div>
|
||||
|
@ -1,7 +1,157 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
// assets
|
||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
// mobx
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import authenticationService from "services/authentication.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
import { EmailPasswordForm, GithubLoginButton, GoogleLoginButton, EmailCodeForm } from "components/accounts";
|
||||
|
||||
const HomePage = () => (
|
||||
<div className="relative w-screen h-screen flex justify-center items-center text-5xl">Plane Deploy</div>
|
||||
);
|
||||
const HomePage = () => {
|
||||
const { user: userStore } = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { next_path = "/" } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const onSignInError = (error: any) => {
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: error?.error || "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
};
|
||||
|
||||
const onSignInSuccess = (response: any) => {
|
||||
const isOnboarded = response?.user?.onboarding_step?.profile_complete || false;
|
||||
|
||||
userStore.setCurrentUser(response?.user);
|
||||
|
||||
if (!isOnboarded) {
|
||||
router.push(`/onboarding?next_path=${next_path}`);
|
||||
return;
|
||||
}
|
||||
console.log("hello");
|
||||
router.push(next_path.toString());
|
||||
};
|
||||
|
||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||
try {
|
||||
if (clientId && credential) {
|
||||
const socialAuthPayload = {
|
||||
medium: "google",
|
||||
credential,
|
||||
clientId,
|
||||
};
|
||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
|
||||
onSignInSuccess(response);
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (err: any) {
|
||||
onSignInError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGitHubSignIn = async (credential: string) => {
|
||||
try {
|
||||
if (process.env.NEXT_PUBLIC_GITHUB_ID && credential) {
|
||||
const socialAuthPayload = {
|
||||
medium: "github",
|
||||
credential,
|
||||
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
||||
};
|
||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
onSignInSuccess(response);
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (err: any) {
|
||||
onSignInError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordSignIn = async (formData: any) => {
|
||||
await authenticationService
|
||||
.emailLogin(formData)
|
||||
.then((response) => {
|
||||
try {
|
||||
if (response) {
|
||||
onSignInSuccess(response);
|
||||
}
|
||||
} catch (err: any) {
|
||||
onSignInError(err);
|
||||
}
|
||||
})
|
||||
.catch((err) => onSignInError(err));
|
||||
};
|
||||
|
||||
const handleEmailCodeSignIn = async (response: any) => {
|
||||
try {
|
||||
if (response) {
|
||||
onSignInSuccess(response);
|
||||
}
|
||||
} catch (err: any) {
|
||||
onSignInError(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full overflow-hidden">
|
||||
<div className="hidden sm:block sm:fixed border-r-[0.5px] border-custom-border-200 h-screen w-[0.5px] top-0 left-20 lg:left-32" />
|
||||
<div className="fixed grid place-items-center bg-custom-background-100 sm:py-5 top-11 sm:top-12 left-7 sm:left-16 lg:left-28">
|
||||
<div className="grid place-items-center bg-custom-background-100">
|
||||
<div className="h-[30px] w-[30px]">
|
||||
<Image src={BluePlaneLogoWithoutText} alt="Plane Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid place-items-center h-full overflow-y-auto py-5 px-7">
|
||||
<div>
|
||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
||||
<>
|
||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
||||
Sign in to Plane
|
||||
</h1>
|
||||
<div className="flex flex-col divide-y divide-custom-border-200">
|
||||
<div className="pb-7">
|
||||
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
|
||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
||||
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<EmailPasswordForm onSubmit={handlePasswordSignIn} />
|
||||
)}
|
||||
|
||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
||||
<p className="pt-16 text-custom-text-200 text-sm text-center">
|
||||
By signing up, you agree to the{" "}
|
||||
<a
|
||||
href="https://plane.so/terms-and-conditions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-medium underline"
|
||||
>
|
||||
Terms & Conditions
|
||||
</a>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomePage;
|
||||
|
@ -13,10 +13,20 @@ export interface IIssueDetailStore {
|
||||
peekId: string | null;
|
||||
peekMode: IPeekMode;
|
||||
details: any;
|
||||
// actions
|
||||
// peek actions
|
||||
setPeekId: (issueId: string | null) => void;
|
||||
setPeekMode: (mode: IPeekMode) => void;
|
||||
// issue details
|
||||
fetchIssueDetails: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
// issue comments
|
||||
addIssueComment: (workspaceId: string, projectId: string, issueId: string, data: any) => void;
|
||||
deleteIssueComment: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
// issue reactions
|
||||
addIssueReaction: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
removeIssueReaction: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
// issue votes
|
||||
addIssueVote: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
removeIssueVote: (workspaceId: string, projectId: string, issueId: string) => void;
|
||||
}
|
||||
|
||||
class IssueDetailStore implements IssueDetailStore {
|
||||
@ -59,9 +69,7 @@ class IssueDetailStore implements IssueDetailStore {
|
||||
this.error = null;
|
||||
|
||||
const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
|
||||
const reactionsResponse = await this.issueService.getIssueReactions(workspaceSlug, projectId, issueId);
|
||||
const commentsResponse = await this.issueService.getIssueComments(workspaceSlug, projectId, issueId);
|
||||
const votesResponse = await this.issueService.getIssueVotes(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueDetails) {
|
||||
runInAction(() => {
|
||||
@ -70,8 +78,6 @@ class IssueDetailStore implements IssueDetailStore {
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
comments: commentsResponse,
|
||||
reactions: reactionsResponse,
|
||||
votes: votesResponse,
|
||||
},
|
||||
};
|
||||
});
|
||||
@ -79,20 +85,148 @@ class IssueDetailStore implements IssueDetailStore {
|
||||
} catch (error) {
|
||||
this.loader = false;
|
||||
this.error = error;
|
||||
}
|
||||
};
|
||||
|
||||
addIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
|
||||
try {
|
||||
const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
|
||||
const issueCommentResponse = await this.issueService.createIssueComment(workspaceSlug, projectId, issueId, data);
|
||||
console.log("issueCommentResponse", issueCommentResponse);
|
||||
if (issueDetails) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
comments: [...this.details[issueId].comments, issueCommentResponse],
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
return issueCommentResponse;
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue comment");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
comments: [],
|
||||
reactions: [],
|
||||
votes: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
updateIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.updateIssueComment(workspaceSlug, projectId, issueId, data);
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
comments: commentsResponse,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue comment");
|
||||
}
|
||||
};
|
||||
|
||||
deleteIssueComment = async () => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.deleteIssueComment(workspaceSlug, projectId, issueId, data);
|
||||
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue vote");
|
||||
}
|
||||
};
|
||||
|
||||
addIssueReaction = async () => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.createIssueReaction(workspaceSlug, projectId, issueId, data);
|
||||
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue vote");
|
||||
}
|
||||
};
|
||||
|
||||
removeIssueReaction = async () => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.deleteIssueReaction(workspaceSlug, projectId, issueId, data);
|
||||
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue vote");
|
||||
}
|
||||
};
|
||||
|
||||
addIssueVote = async (workspaceSlug: string, projectId: string, issueId: string, data: { vote: 1 | -1 }) => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.createIssueVote(workspaceSlug, projectId, issueId, data);
|
||||
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to add issue vote");
|
||||
}
|
||||
};
|
||||
|
||||
removeIssueVote = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
const issueVoteResponse = await this.issueService.deleteIssueVote(workspaceSlug, projectId, issueId, data);
|
||||
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
|
||||
|
||||
if (issueVoteResponse) {
|
||||
runInAction(() => {
|
||||
this.details = {
|
||||
...this.details,
|
||||
[issueId]: {
|
||||
...issueDetails,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to remove issue vote");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export interface IProjectStore {
|
||||
error: any | null;
|
||||
workspace: IWorkspace | null;
|
||||
project: IProject | null;
|
||||
projectDeploySettings: IProjectSettings | null;
|
||||
deploySettings: IProjectSettings | null;
|
||||
viewOptions: any;
|
||||
activeBoard: string | null;
|
||||
fetchProjectSettings: (workspace_slug: string, project_slug: string) => Promise<void>;
|
||||
@ -23,7 +23,7 @@ class ProjectStore implements IProjectStore {
|
||||
// data
|
||||
workspace: IWorkspace | null = null;
|
||||
project: IProject | null = null;
|
||||
projectDeploySettings: IProjectSettings | null = null;
|
||||
deploySettings: IProjectSettings | null = null;
|
||||
viewOptions: any = null;
|
||||
activeBoard: string | null = null;
|
||||
// root store
|
||||
@ -39,7 +39,7 @@ class ProjectStore implements IProjectStore {
|
||||
// observable
|
||||
workspace: observable.ref,
|
||||
project: observable.ref,
|
||||
projectDeploySettings: observable.ref,
|
||||
deploySettings: observable.ref,
|
||||
viewOptions: observable.ref,
|
||||
activeBoard: observable.ref,
|
||||
// actions
|
||||
@ -63,10 +63,12 @@ class ProjectStore implements IProjectStore {
|
||||
const _project: IProject = { ...response?.project_details };
|
||||
const _workspace: IWorkspace = { ...response?.workspace_detail };
|
||||
const _viewOptions = { ...response?.views };
|
||||
const _deploySettings = { ...response };
|
||||
runInAction(() => {
|
||||
this.project = _project;
|
||||
this.workspace = _workspace;
|
||||
this.viewOptions = _viewOptions;
|
||||
this.deploySettings = _deploySettings;
|
||||
this.loader = false;
|
||||
});
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
||||
// service
|
||||
import UserService from "services/user.service";
|
||||
// types
|
||||
import { IUserStore } from "../types";
|
||||
|
||||
export interface IUserStore {
|
||||
currentUser: any | null;
|
||||
fetchCurrentUser: () => void;
|
||||
}
|
||||
|
||||
class UserStore implements IUserStore {
|
||||
currentUser: any | null = null;
|
||||
@ -42,7 +45,7 @@ class UserStore implements IUserStore {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getUserAsync()
|
||||
this.fetchCurrentUser()
|
||||
.then(() => {
|
||||
if (!this.currentUser) {
|
||||
const currentPath = window.location.pathname;
|
||||
@ -55,7 +58,7 @@ class UserStore implements IUserStore {
|
||||
});
|
||||
};
|
||||
|
||||
getUserAsync = async () => {
|
||||
fetchCurrentUser = async () => {
|
||||
try {
|
||||
const response = await this.userService.currentUser();
|
||||
if (response) {
|
||||
@ -64,10 +67,7 @@ class UserStore implements IUserStore {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("error", error);
|
||||
runInAction(() => {
|
||||
// render error actions
|
||||
});
|
||||
console.error("Failed to fetch current user", error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
export interface IUserStore {
|
||||
currentUser: any | null;
|
||||
getUserAsync: () => void;
|
||||
}
|
Loading…
Reference in New Issue
Block a user