fix: workspace members store added and implemented across the app (#2732)
* fix: minor changes * fix: workspace members store added and implemnted across the app
This commit is contained in:
parent
556b2d2617
commit
a6567bbce4
28 changed files with 529 additions and 352 deletions
|
|
@ -41,6 +41,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||
globalViewFilters: globalViewFiltersStore,
|
||||
workspaceFilter: workspaceFilterStore,
|
||||
workspace: workspaceStore,
|
||||
workspaceMember: { workspaceMembers },
|
||||
project: projectStore,
|
||||
} = useMobxStore();
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={ISSUE_DISPLAY_FILTERS_BY_LAYOUT.my_issues.spreadsheet}
|
||||
labels={workspaceStore.workspaceLabels ?? undefined}
|
||||
members={workspaceStore.workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
members={workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
projects={workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const GlobalViewsAppliedFiltersRoot = observer(() => {
|
|||
globalViewFilters: globalViewFiltersStore,
|
||||
project: projectStore,
|
||||
workspace: workspaceStore,
|
||||
workspaceMember: { workspaceMembers },
|
||||
} = useMobxStore();
|
||||
|
||||
const viewDetails = globalViewId ? globalViewsStore.globalViewDetails[globalViewId.toString()] : undefined;
|
||||
|
|
@ -101,7 +102,7 @@ export const GlobalViewsAppliedFiltersRoot = observer(() => {
|
|||
handleClearAllFilters={handleClearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
labels={workspaceStore.workspaceLabels ?? undefined}
|
||||
members={workspaceStore.workspaceMembers?.map((m) => m.member)}
|
||||
members={workspaceMembers?.map((m) => m.member)}
|
||||
projects={workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined}
|
||||
/>
|
||||
{storedFilters && viewDetails && areFiltersDifferent(storedFilters, viewDetails.query_data.filters ?? {}) && (
|
||||
|
|
|
|||
|
|
@ -39,18 +39,19 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
|||
multiple = false,
|
||||
noLabelBorder = false,
|
||||
} = props;
|
||||
|
||||
const { workspace: workspaceStore, project: projectStore } = useMobxStore();
|
||||
// store
|
||||
const {
|
||||
workspace: workspaceStore,
|
||||
project: projectStore,
|
||||
workspaceMember: { workspaceMembers, fetchWorkspaceMembers },
|
||||
} = useMobxStore();
|
||||
const workspaceSlug = workspaceStore?.workspaceSlug;
|
||||
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<Boolean>(false);
|
||||
|
||||
const workspaceMembers = workspaceSlug ? workspaceStore?.workspaceMembers : undefined;
|
||||
|
||||
const fetchProjectMembers = () => {
|
||||
setIsLoading(true);
|
||||
if (workspaceSlug && projectId)
|
||||
|
|
@ -59,10 +60,9 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
|||
projectStore.fetchProjectMembers(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
const fetchWorkspaceMembers = () => {
|
||||
const getWorkspaceMembers = () => {
|
||||
setIsLoading(true);
|
||||
if (workspaceSlug)
|
||||
workspaceSlug && workspaceStore.fetchWorkspaceMembers(workspaceSlug).then(() => setIsLoading(false));
|
||||
if (workspaceSlug) workspaceSlug && fetchWorkspaceMembers(workspaceSlug).then(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
const options = (workspaceMembers ?? [])?.map((member) => ({
|
||||
|
|
@ -151,7 +151,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
|||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
onClick={() => !workspaceMembers && fetchWorkspaceMembers()}
|
||||
onClick={() => !workspaceMembers && getWorkspaceMembers()}
|
||||
>
|
||||
{label}
|
||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
|
|||
globalViewFilters: globalViewFiltersStore,
|
||||
workspaceFilter: workspaceFilterStore,
|
||||
workspace: workspaceStore,
|
||||
workspaceMember: { workspaceMembers },
|
||||
issueDetail: issueDetailStore,
|
||||
project: projectStore,
|
||||
} = useMobxStore();
|
||||
|
|
@ -106,7 +107,7 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
|
|||
displayFilters={workspaceFilterStore.workspaceDisplayFilters}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issues={issues}
|
||||
members={workspaceStore.workspaceMembers ? workspaceStore.workspaceMembers.map((m) => m.member) : undefined}
|
||||
members={workspaceMembers?.map((m) => m.member)}
|
||||
labels={workspaceStore.workspaceLabels ? workspaceStore.workspaceLabels : undefined}
|
||||
handleIssueAction={() => {}}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,12 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
|
|||
|
||||
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
const { isLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null,
|
||||
async () => {
|
||||
if (workspaceSlug && projectId) {
|
||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}
|
||||
useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
||||
if (workspaceSlug && projectId) {
|
||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { FC } from "react";
|
|||
import Link from "next/link";
|
||||
import { History } from "lucide-react";
|
||||
// packages
|
||||
import { Loader, Tooltip } from "@plane/ui";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// components
|
||||
import { ActivityIcon, ActivityMessage } from "components/core";
|
||||
import { IssueCommentCard } from "./comment-card";
|
||||
|
|
|
|||
|
|
@ -63,8 +63,10 @@ export interface ICreateProjectForm {
|
|||
export const CreateProjectModal: FC<Props> = observer((props) => {
|
||||
const { isOpen, onClose, setToFavorite = false, workspaceSlug } = props;
|
||||
// store
|
||||
const { project: projectStore, workspace: workspaceStore } = useMobxStore();
|
||||
const workspaceMembers = workspaceStore.members[workspaceSlug] || [];
|
||||
const {
|
||||
project: projectStore,
|
||||
workspaceMember: { workspaceMembers },
|
||||
} = useMobxStore();
|
||||
// states
|
||||
const [isChangeInIdentifierRequired, setIsChangeInIdentifierRequired] = useState(true);
|
||||
// toast
|
||||
|
|
@ -370,7 +372,7 @@ export const CreateProjectModal: FC<Props> = observer((props) => {
|
|||
<WorkspaceMemberSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
options={workspaceMembers}
|
||||
options={workspaceMembers || []}
|
||||
placeholder="Select Lead"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -72,9 +72,10 @@ export const ProjectFeaturesList: FC<Props> = observer(() => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// store
|
||||
const { project: projectStore, user: userStore } = useMobxStore();
|
||||
const { currentUser, currentProjectRole } = userStore;
|
||||
const { currentProjectDetails } = projectStore;
|
||||
const {
|
||||
project: { currentProjectDetails, updateProject },
|
||||
user: { currentUser, currentProjectRole },
|
||||
} = useMobxStore();
|
||||
const isAdmin = currentProjectRole === 20;
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
|
|
@ -86,7 +87,7 @@ export const ProjectFeaturesList: FC<Props> = observer(() => {
|
|||
title: "Success!",
|
||||
message: "Project feature updated successfully.",
|
||||
});
|
||||
projectStore.updateProject(workspaceSlug.toString(), projectId.toString(), formData);
|
||||
updateProject(workspaceSlug.toString(), projectId.toString(), formData);
|
||||
};
|
||||
|
||||
if (!currentUser) return <></>;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ export const ConfirmWorkspaceMemberRemove: React.FC<Props> = observer((props) =>
|
|||
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const user = userStore.currentUser;
|
||||
const {
|
||||
user: { currentUser },
|
||||
} = useMobxStore();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
|
|
@ -69,10 +70,10 @@ export const ConfirmWorkspaceMemberRemove: React.FC<Props> = observer((props) =>
|
|||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
{user?.id === data?.memberId ? "Leave workspace?" : `Remove ${data?.display_name}?`}
|
||||
{currentUser?.id === data?.memberId ? "Leave workspace?" : `Remove ${data?.display_name}?`}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
{user?.id === data?.memberId ? (
|
||||
{currentUser?.id === data?.memberId ? (
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to leave the workspace? You will no longer have access to this
|
||||
workspace. This action cannot be undone.
|
||||
|
|
|
|||
|
|
@ -1,28 +1,19 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { mutate } from "swr";
|
||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
import { Button, CustomSelect, Input } from "@plane/ui";
|
||||
// icons
|
||||
import { Plus, X } from "lucide-react";
|
||||
// types
|
||||
import { IUser, TUserWorkspaceRole } from "types";
|
||||
import { IWorkspaceBulkInviteFormData, TUserWorkspaceRole } from "types";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_INVITATIONS } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
workspaceSlug: string;
|
||||
user: IUser | undefined;
|
||||
onSuccess?: () => Promise<void>;
|
||||
onSubmit: (data: IWorkspaceBulkInviteFormData) => Promise<void> | undefined;
|
||||
};
|
||||
|
||||
type EmailRole = {
|
||||
|
|
@ -43,11 +34,10 @@ const defaultValues: FormValues = {
|
|||
],
|
||||
};
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export const SendWorkspaceInvitationModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, workspaceSlug, user, onSuccess } = props;
|
||||
const { isOpen, onClose, onSubmit } = props;
|
||||
|
||||
// form info
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
|
|
@ -60,8 +50,6 @@ export const SendWorkspaceInvitationModal: React.FC<Props> = (props) => {
|
|||
name: "emails",
|
||||
});
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
|
||||
|
|
@ -71,31 +59,29 @@ export const SendWorkspaceInvitationModal: React.FC<Props> = (props) => {
|
|||
}, 350);
|
||||
};
|
||||
|
||||
const onSubmit = async (formData: FormValues) => {
|
||||
if (!workspaceSlug) return;
|
||||
// const onSubmit = async (formData: FormValues) => {
|
||||
// if (!workspaceSlug) return;
|
||||
|
||||
await workspaceService
|
||||
.inviteWorkspace(workspaceSlug, formData, user)
|
||||
.then(async () => {
|
||||
if (onSuccess) await onSuccess();
|
||||
// return workspaceService
|
||||
// .inviteWorkspace(workspaceSlug, formData, user)
|
||||
|
||||
handleClose();
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "Invitations sent successfully.",
|
||||
});
|
||||
})
|
||||
.catch((err) =>
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: `${err.error ?? "Something went wrong. Please try again."}`,
|
||||
})
|
||||
)
|
||||
.finally(() => mutate(WORKSPACE_INVITATIONS));
|
||||
};
|
||||
// .then(async () => {
|
||||
// if (onSuccess) await onSuccess();
|
||||
// handleClose();
|
||||
// setToastAlert({
|
||||
// type: "success",
|
||||
// title: "Success!",
|
||||
// message: "Invitations sent successfully.",
|
||||
// });
|
||||
// })
|
||||
// .catch((err) =>
|
||||
// setToastAlert({
|
||||
// type: "error",
|
||||
// title: "Error!",
|
||||
// message: `${err.error ?? "Something went wrong. Please try again."}`,
|
||||
// })
|
||||
// );
|
||||
// };
|
||||
|
||||
const appendField = () => {
|
||||
append({ email: "", role: 15 });
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
|
|
@ -33,17 +31,16 @@ type Props = {
|
|||
};
|
||||
};
|
||||
|
||||
// services
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export const WorkspaceMembersListItem: FC<Props> = (props) => {
|
||||
const { member } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// store
|
||||
const { workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const { currentWorkspaceMemberInfo, currentWorkspaceRole } = userStore;
|
||||
const {
|
||||
workspaceMember: { removeMember, updateMember, deleteWorkspaceInvitation },
|
||||
user: { currentWorkspaceMemberInfo, currentWorkspaceRole },
|
||||
} = useMobxStore();
|
||||
const isAdmin = currentWorkspaceRole === 20;
|
||||
// states
|
||||
const [removeMemberModal, setRemoveMemberModal] = useState(false);
|
||||
|
|
@ -54,7 +51,7 @@ export const WorkspaceMembersListItem: FC<Props> = (props) => {
|
|||
if (!workspaceSlug) return;
|
||||
|
||||
if (member.member)
|
||||
await workspaceStore.removeMember(workspaceSlug.toString(), member.id).catch((err) => {
|
||||
await removeMember(workspaceSlug.toString(), member.id).catch((err) => {
|
||||
const error = err?.error;
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
|
|
@ -63,8 +60,7 @@ export const WorkspaceMembersListItem: FC<Props> = (props) => {
|
|||
});
|
||||
});
|
||||
else
|
||||
await workspaceService
|
||||
.deleteWorkspaceInvitations(workspaceSlug.toString(), member.id)
|
||||
await deleteWorkspaceInvitation(workspaceSlug.toString(), member.id)
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
|
|
@ -157,17 +153,15 @@ export const WorkspaceMembersListItem: FC<Props> = (props) => {
|
|||
onChange={(value: TUserWorkspaceRole | undefined) => {
|
||||
if (!workspaceSlug || !value) return;
|
||||
|
||||
workspaceStore
|
||||
.updateMember(workspaceSlug.toString(), member.id, {
|
||||
role: value,
|
||||
})
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "An error occurred while updating member role. Please try again.",
|
||||
});
|
||||
updateMember(workspaceSlug.toString(), member.id, {
|
||||
role: value,
|
||||
}).catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "An error occurred while updating member role. Please try again.",
|
||||
});
|
||||
});
|
||||
}}
|
||||
disabled={
|
||||
member.memberId === currentWorkspaceMemberInfo.member ||
|
||||
|
|
|
|||
|
|
@ -1,64 +1,45 @@
|
|||
import { FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// components
|
||||
import { WorkspaceMembersListItem } from "components/workspace";
|
||||
// ui
|
||||
import { Loader } from "@plane/ui";
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
export const WorkspaceMembersList: React.FC<{ searchQuery: string }> = observer(({ searchQuery }) => {
|
||||
export const WorkspaceMembersList: FC<{ searchQuery: string }> = observer(({ searchQuery }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// store
|
||||
const { workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const workspaceMembers = workspaceStore.workspaceMembers;
|
||||
const user = userStore.currentWorkspaceMemberInfo;
|
||||
const {
|
||||
workspaceMember: {
|
||||
workspaceMembers,
|
||||
workspaceMembersWithInvitations,
|
||||
workspaceMemberInvitations,
|
||||
fetchWorkspaceMemberInvitations,
|
||||
},
|
||||
user: { currentWorkspaceMemberInfo },
|
||||
} = useMobxStore();
|
||||
// fetching workspace invitations
|
||||
const { data: workspaceInvitations } = useSWR(
|
||||
useSWR(
|
||||
workspaceSlug ? `WORKSPACE_INVITATIONS_${workspaceSlug.toString()}` : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null
|
||||
workspaceSlug ? () => fetchWorkspaceMemberInvitations(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
const members = [
|
||||
...(workspaceInvitations?.map((item) => ({
|
||||
id: item.id,
|
||||
memberId: item.id,
|
||||
avatar: "",
|
||||
first_name: item.email,
|
||||
last_name: "",
|
||||
email: item.email,
|
||||
display_name: item.email,
|
||||
role: item.role,
|
||||
status: item.accepted,
|
||||
member: false,
|
||||
accountCreated: item.accepted,
|
||||
})) || []),
|
||||
...(workspaceMembers?.map((item) => ({
|
||||
id: item.id,
|
||||
memberId: item.member?.id,
|
||||
avatar: item.member?.avatar,
|
||||
first_name: item.member?.first_name,
|
||||
last_name: item.member?.last_name,
|
||||
email: item.member?.email,
|
||||
display_name: item.member?.display_name,
|
||||
role: item.role,
|
||||
status: true,
|
||||
member: true,
|
||||
accountCreated: true,
|
||||
})) || []),
|
||||
];
|
||||
const searchedMembers = members?.filter((member) => {
|
||||
const searchedMembers = workspaceMembersWithInvitations?.filter((member: any) => {
|
||||
const fullName = `${member.first_name} ${member.last_name}`.toLowerCase();
|
||||
const displayName = member.display_name.toLowerCase();
|
||||
return displayName.includes(searchQuery.toLowerCase()) || fullName.includes(searchQuery.toLowerCase());
|
||||
});
|
||||
|
||||
if (!workspaceMembers || !workspaceInvitations || !user)
|
||||
if (
|
||||
!workspaceMembers ||
|
||||
!workspaceMemberInvitations ||
|
||||
!workspaceMembersWithInvitations ||
|
||||
!currentWorkspaceMemberInfo
|
||||
)
|
||||
return (
|
||||
<Loader className="space-y-5">
|
||||
<Loader.Item height="40px" />
|
||||
|
|
@ -70,10 +51,10 @@ export const WorkspaceMembersList: React.FC<{ searchQuery: string }> = observer(
|
|||
|
||||
return (
|
||||
<div className="divide-y-[0.5px] divide-custom-border-200">
|
||||
{members.length > 0
|
||||
? searchedMembers.map((member) => <WorkspaceMembersListItem key={member.id} member={member} />)
|
||||
{workspaceMembersWithInvitations.length > 0
|
||||
? searchedMembers?.map((member) => <WorkspaceMembersListItem key={member.id} member={member} />)
|
||||
: null}
|
||||
{searchedMembers.length === 0 && (
|
||||
{searchedMembers?.length === 0 && (
|
||||
<h4 className="text-md text-custom-text-400 text-center mt-20">No matching member</h4>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
|
|
@ -29,7 +29,7 @@ const defaultValues: Partial<IWorkspace> = {
|
|||
// services
|
||||
const fileService = new FileService();
|
||||
|
||||
export const WorkspaceDetails: React.FC = observer(() => {
|
||||
export const WorkspaceDetails: FC = observer(() => {
|
||||
// states
|
||||
const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
|
@ -37,9 +37,10 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
const [isImageRemoving, setIsImageRemoving] = useState(false);
|
||||
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
||||
// store
|
||||
const { workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const activeWorkspace = workspaceStore.currentWorkspace;
|
||||
const { currentWorkspaceRole } = userStore;
|
||||
const {
|
||||
workspace: { currentWorkspace, updateWorkspace },
|
||||
user: { currentWorkspaceRole },
|
||||
} = useMobxStore();
|
||||
const isAdmin = currentWorkspaceRole === 20;
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
|
|
@ -52,11 +53,11 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
setValue,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<IWorkspace>({
|
||||
defaultValues: { ...defaultValues, ...activeWorkspace },
|
||||
defaultValues: { ...defaultValues, ...currentWorkspace },
|
||||
});
|
||||
|
||||
const onSubmit = async (formData: IWorkspace) => {
|
||||
if (!activeWorkspace) return;
|
||||
if (!currentWorkspace) return;
|
||||
|
||||
const payload: Partial<IWorkspace> = {
|
||||
logo: formData.logo,
|
||||
|
|
@ -64,8 +65,7 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
organization_size: formData.organization_size,
|
||||
};
|
||||
|
||||
await workspaceStore
|
||||
.updateWorkspace(activeWorkspace.slug, payload)
|
||||
await updateWorkspace(currentWorkspace.slug, payload)
|
||||
.then(() =>
|
||||
setToastAlert({
|
||||
title: "Success",
|
||||
|
|
@ -77,13 +77,12 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
};
|
||||
|
||||
const handleDelete = (url: string | null | undefined) => {
|
||||
if (!activeWorkspace || !url) return;
|
||||
if (!currentWorkspace || !url) return;
|
||||
|
||||
setIsImageRemoving(true);
|
||||
|
||||
fileService.deleteFile(activeWorkspace.id, url).then(() => {
|
||||
workspaceStore
|
||||
.updateWorkspace(activeWorkspace.slug, { logo: "" })
|
||||
fileService.deleteFile(currentWorkspace.id, url).then(() => {
|
||||
updateWorkspace(currentWorkspace.slug, { logo: "" })
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
|
|
@ -104,10 +103,10 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (activeWorkspace) reset({ ...activeWorkspace });
|
||||
}, [activeWorkspace, reset]);
|
||||
if (currentWorkspace) reset({ ...currentWorkspace });
|
||||
}, [currentWorkspace, reset]);
|
||||
|
||||
if (!activeWorkspace)
|
||||
if (!currentWorkspace)
|
||||
return (
|
||||
<div className="grid place-items-center h-full w-full px-4 sm:px-0">
|
||||
<Spinner />
|
||||
|
|
@ -119,13 +118,13 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
<DeleteWorkspaceModal
|
||||
isOpen={deleteWorkspaceModal}
|
||||
onClose={() => setDeleteWorkspaceModal(false)}
|
||||
data={activeWorkspace}
|
||||
data={currentWorkspace}
|
||||
/>
|
||||
<ImageUploadModal
|
||||
isOpen={isImageUploadModalOpen}
|
||||
onClose={() => setIsImageUploadModalOpen(false)}
|
||||
isRemoving={isImageRemoving}
|
||||
handleDelete={() => handleDelete(activeWorkspace?.logo)}
|
||||
handleDelete={() => handleDelete(currentWorkspace?.logo)}
|
||||
onSuccess={(imageUrl) => {
|
||||
setIsImageUploading(true);
|
||||
setValue("logo", imageUrl);
|
||||
|
|
@ -148,7 +147,7 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="relative flex h-14 w-14 items-center justify-center rounded bg-gray-700 p-4 uppercase text-white">
|
||||
{activeWorkspace?.name?.charAt(0) ?? "N"}
|
||||
{currentWorkspace?.name?.charAt(0) ?? "N"}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
|
|
@ -157,7 +156,7 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
<h3 className="text-lg font-semibold leading-6">{watch("name")}</h3>
|
||||
<span className="text-sm tracking-tight">{`${
|
||||
typeof window !== "undefined" && window.location.origin.replace("http://", "").replace("https://", "")
|
||||
}/${activeWorkspace.slug}`}</span>
|
||||
}/${currentWorkspace.slug}`}</span>
|
||||
<div className="flex item-center gap-2.5">
|
||||
<button
|
||||
className="flex items-center gap-1.5 text-xs text-left text-custom-primary-100 font-medium"
|
||||
|
|
@ -246,7 +245,7 @@ export const WorkspaceDetails: React.FC = observer(() => {
|
|||
value={`${
|
||||
typeof window !== "undefined" &&
|
||||
window.location.origin.replace("http://", "").replace("https://", "")
|
||||
}/${activeWorkspace.slug}`}
|
||||
}/${currentWorkspace.slug}`}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.url)}
|
||||
|
|
|
|||
|
|
@ -49,18 +49,19 @@ const authService = new AuthService();
|
|||
export const WorkspaceSidebarDropdown = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { theme: themeStore, workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const { workspaces, currentWorkspace: activeWorkspace } = workspaceStore;
|
||||
const user = userStore.currentUser;
|
||||
|
||||
// store
|
||||
const {
|
||||
theme: { sidebarCollapsed },
|
||||
workspace: { workspaces, currentWorkspace: activeWorkspace },
|
||||
user: { currentUser, updateCurrentUser },
|
||||
} = useMobxStore();
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleWorkspaceNavigation = (workspace: IWorkspace) => {
|
||||
userStore
|
||||
.updateCurrentUser({
|
||||
last_workspace_id: workspace?.id,
|
||||
})
|
||||
updateCurrentUser({
|
||||
last_workspace_id: workspace?.id,
|
||||
})
|
||||
.then(() => {
|
||||
router.push(`/${workspace.slug}/`);
|
||||
})
|
||||
|
|
@ -94,7 +95,7 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
|||
<Menu.Button className="text-custom-sidebar-text-200 rounded-sm text-sm font-medium focus:outline-none w-full h-full truncate">
|
||||
<div
|
||||
className={`flex items-center gap-x-2 rounded-sm bg-custom-sidebar-background-80 p-1 truncate ${
|
||||
themeStore.sidebarCollapsed ? "justify-center" : ""
|
||||
sidebarCollapsed ? "justify-center" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="relative grid h-6 w-6 place-items-center rounded bg-gray-700 uppercase text-white flex-shrink-0">
|
||||
|
|
@ -109,7 +110,7 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{!themeStore.sidebarCollapsed && (
|
||||
{!sidebarCollapsed && (
|
||||
<h4 className="text-custom-text-100 truncate">
|
||||
{activeWorkspace?.name ? activeWorkspace.name : "Loading..."}
|
||||
</h4>
|
||||
|
|
@ -198,7 +199,7 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start justify-start gap-2 border-t border-custom-sidebar-border-200 px-3 py-2 text-sm">
|
||||
{userLinks(workspaceSlug?.toString() ?? "", user?.id ?? "").map((link, index) => (
|
||||
{userLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
|
||||
<Menu.Item
|
||||
key={index}
|
||||
as="div"
|
||||
|
|
@ -224,10 +225,16 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
|||
</Transition>
|
||||
</Menu>
|
||||
|
||||
{!themeStore.sidebarCollapsed && (
|
||||
{!sidebarCollapsed && (
|
||||
<Menu as="div" className="relative flex-shrink-0">
|
||||
<Menu.Button className="grid place-items-center outline-none">
|
||||
<Avatar name={user?.display_name} src={user?.avatar} size={30} shape="square" className="!text-base" />
|
||||
<Avatar
|
||||
name={currentUser?.display_name}
|
||||
src={currentUser?.avatar}
|
||||
size={30}
|
||||
shape="square"
|
||||
className="!text-base"
|
||||
/>
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
|
|
@ -244,8 +251,8 @@ export const WorkspaceSidebarDropdown = observer(() => {
|
|||
border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 divide-y divide-custom-sidebar-border-200 shadow-lg text-xs outline-none"
|
||||
>
|
||||
<div className="flex flex-col gap-2.5 pb-2">
|
||||
<span className="px-2 text-custom-sidebar-text-200">{user?.email}</span>
|
||||
{profileLinks(workspaceSlug?.toString() ?? "", user?.id ?? "").map((link, index) => (
|
||||
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
||||
{profileLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
|
||||
<Menu.Item key={index} as="button" type="button">
|
||||
<Link href={link.link}>
|
||||
<a className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { workspace: workspaceStore, project: projectStore } = useMobxStore();
|
||||
const {
|
||||
workspace: workspaceStore,
|
||||
project: projectStore,
|
||||
workspaceMember: { workspaceMembers },
|
||||
} = useMobxStore();
|
||||
|
||||
const {
|
||||
formState: { errors, isSubmitting },
|
||||
|
|
@ -143,7 +147,7 @@ export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
|||
}}
|
||||
layoutDisplayFiltersOptions={ISSUE_DISPLAY_FILTERS_BY_LAYOUT.my_issues.spreadsheet}
|
||||
labels={workspaceStore.workspaceLabels ?? undefined}
|
||||
members={workspaceStore.workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
members={workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
projects={workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
|
|
@ -157,7 +161,7 @@ export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
|||
handleClearAllFilters={clearAllFilters}
|
||||
handleRemoveFilter={() => {}}
|
||||
labels={workspaceStore.workspaceLabels ?? undefined}
|
||||
members={workspaceStore.workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
members={workspaceMembers?.map((m) => m.member) ?? undefined}
|
||||
states={undefined}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue