merge conflicts resolved
This commit is contained in:
commit
9003c58d89
360 changed files with 13916 additions and 7344 deletions
|
|
@ -22,7 +22,6 @@ const MyIssuesPage: NextPage = () => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { projects } = useProjects();
|
||||
const { user } = useUser();
|
||||
|
||||
const { filters, setFilters } = useMyIssuesFilters(workspaceSlug?.toString());
|
||||
|
|
@ -30,23 +29,37 @@ const MyIssuesPage: NextPage = () => {
|
|||
const tabsList = [
|
||||
{
|
||||
key: "assigned",
|
||||
label: "Assigned to me",
|
||||
label: "Assigned",
|
||||
selected: (filters?.assignees ?? []).length > 0,
|
||||
onClick: () => {
|
||||
setFilters({
|
||||
assignees: [user?.id ?? ""],
|
||||
created_by: null,
|
||||
subscriber: null,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "created",
|
||||
label: "Created by me",
|
||||
label: "Created",
|
||||
selected: (filters?.created_by ?? []).length > 0,
|
||||
onClick: () => {
|
||||
setFilters({
|
||||
created_by: [user?.id ?? ""],
|
||||
assignees: null,
|
||||
created_by: [user?.id ?? ""],
|
||||
subscriber: null,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "subscribed",
|
||||
label: "Subscribed",
|
||||
selected: (filters?.subscriber ?? []).length > 0,
|
||||
onClick: () => {
|
||||
setFilters({
|
||||
assignees: null,
|
||||
created_by: null,
|
||||
subscriber: [user?.id ?? ""],
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
@ -55,7 +68,7 @@ const MyIssuesPage: NextPage = () => {
|
|||
useEffect(() => {
|
||||
if (!filters || !user) return;
|
||||
|
||||
if (!filters.assignees && !filters.created_by) {
|
||||
if (!filters.assignees && !filters.created_by && !filters.subscriber) {
|
||||
setFilters({
|
||||
assignees: [user.id],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
|||
import SettingsNavbar from "layouts/settings-navbar";
|
||||
// components
|
||||
import { ActivityIcon, ActivityMessage } from "components/core";
|
||||
import RemirrorRichTextEditor from "components/rich-text-editor";
|
||||
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
|
||||
// icons
|
||||
import { ArrowTopRightOnSquareIcon, ChatBubbleLeftEllipsisIcon } from "@heroicons/react/24/outline";
|
||||
// ui
|
||||
|
|
@ -73,7 +73,7 @@ const ProfileActivity = () => {
|
|||
activityItem.actor_detail.avatar !== "" ? (
|
||||
<img
|
||||
src={activityItem.actor_detail.avatar}
|
||||
alt={activityItem.actor_detail.first_name}
|
||||
alt={activityItem.actor_detail.display_name}
|
||||
height={30}
|
||||
width={30}
|
||||
className="grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-500 text-white"
|
||||
|
|
@ -82,7 +82,7 @@ const ProfileActivity = () => {
|
|||
<div
|
||||
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-500 text-white`}
|
||||
>
|
||||
{activityItem.actor_detail.first_name.charAt(0)}
|
||||
{activityItem.actor_detail.display_name?.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -96,25 +96,25 @@ const ProfileActivity = () => {
|
|||
<div className="min-w-0 flex-1">
|
||||
<div>
|
||||
<div className="text-xs">
|
||||
{activityItem.actor_detail.first_name}
|
||||
{activityItem.actor_detail.is_bot
|
||||
? "Bot"
|
||||
: " " + activityItem.actor_detail.last_name}
|
||||
? activityItem.actor_detail.first_name + " Bot"
|
||||
: activityItem.actor_detail.display_name}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">
|
||||
Commented {timeAgo(activityItem.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="issue-comments-section p-0">
|
||||
<RemirrorRichTextEditor
|
||||
<Tiptap
|
||||
value={
|
||||
activityItem.new_value && activityItem.new_value !== ""
|
||||
activityItem?.new_value !== ""
|
||||
? activityItem.new_value
|
||||
: activityItem.old_value
|
||||
}
|
||||
editable={false}
|
||||
noBorder
|
||||
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
|
||||
noBorder
|
||||
borderOnFocus={false}
|
||||
editable={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -176,7 +176,7 @@ const ProfileActivity = () => {
|
|||
activityItem.actor_detail.avatar !== "" ? (
|
||||
<img
|
||||
src={activityItem.actor_detail.avatar}
|
||||
alt={activityItem.actor_detail.first_name}
|
||||
alt={activityItem.actor_detail.display_name}
|
||||
height={24}
|
||||
width={24}
|
||||
className="rounded-full"
|
||||
|
|
@ -185,7 +185,7 @@ const ProfileActivity = () => {
|
|||
<div
|
||||
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-700 text-xs text-white`}
|
||||
>
|
||||
{activityItem.actor_detail.first_name.charAt(0)}
|
||||
{activityItem.actor_detail.display_name?.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -206,8 +206,7 @@ const ProfileActivity = () => {
|
|||
href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}
|
||||
>
|
||||
<a className="text-gray font-medium">
|
||||
{activityItem.actor_detail.first_name}{" "}
|
||||
{activityItem.actor_detail.last_name}
|
||||
{activityItem.actor_detail.display_name}
|
||||
</a>
|
||||
</Link>
|
||||
)}{" "}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import useToast from "hooks/use-toast";
|
|||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import SettingsNavbar from "layouts/settings-navbar";
|
||||
// components
|
||||
import { ImageUploadModal } from "components/core";
|
||||
import { ImagePickerPopover, ImageUploadModal } from "components/core";
|
||||
// ui
|
||||
import { CustomSelect, DangerButton, Input, SecondaryButton, Spinner } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
|
|
@ -26,6 +26,7 @@ import { USER_ROLES } from "constants/workspace";
|
|||
|
||||
const defaultValues: Partial<IUser> = {
|
||||
avatar: "",
|
||||
cover_image: "",
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
email: "",
|
||||
|
|
@ -68,13 +69,15 @@ const Profile: NextPage = () => {
|
|||
first_name: formData.first_name,
|
||||
last_name: formData.last_name,
|
||||
avatar: formData.avatar,
|
||||
cover_image: formData.cover_image,
|
||||
role: formData.role,
|
||||
display_name: formData.display_name,
|
||||
};
|
||||
|
||||
await userService
|
||||
.updateUser(payload)
|
||||
.then((res) => {
|
||||
mutateUser((prevData) => {
|
||||
mutateUser((prevData: any) => {
|
||||
if (!prevData) return prevData;
|
||||
|
||||
return { ...prevData, ...res };
|
||||
|
|
@ -109,7 +112,7 @@ const Profile: NextPage = () => {
|
|||
title: "Success!",
|
||||
message: "Profile picture removed successfully.",
|
||||
});
|
||||
mutateUser((prevData) => {
|
||||
mutateUser((prevData: any) => {
|
||||
if (!prevData) return prevData;
|
||||
return { ...prevData, avatar: "" };
|
||||
}, false);
|
||||
|
|
@ -176,7 +179,7 @@ const Profile: NextPage = () => {
|
|||
src={watch("avatar")}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded-md"
|
||||
onClick={() => setIsImageUploadModalOpen(true)}
|
||||
alt={myProfile.first_name}
|
||||
alt={myProfile.display_name}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -203,11 +206,42 @@ const Profile: NextPage = () => {
|
|||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold text-custom-text-100">Full Name</h4>
|
||||
<h4 className="text-lg font-semibold">Cover Photo</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
This name will be reflected on all the projects you are working on.
|
||||
Select your cover photo from the given library.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<div className="h-32 w-full rounded border border-custom-border-200 p-1">
|
||||
<div className="relative h-full w-full rounded">
|
||||
<img
|
||||
src={
|
||||
watch("cover_image") ??
|
||||
"https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
|
||||
}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt={myProfile?.name ?? "Cover image"}
|
||||
/>
|
||||
<div className="absolute bottom-0 flex w-full justify-end">
|
||||
<ImagePickerPopover
|
||||
label={"Change cover"}
|
||||
onChange={(imageUrl) => {
|
||||
setValue("cover_image", imageUrl);
|
||||
}}
|
||||
value={
|
||||
watch("cover_image") ??
|
||||
"https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold text-custom-text-100">Full Name</h4>
|
||||
</div>
|
||||
<div className="col-span-12 flex items-center gap-2 sm:col-span-6">
|
||||
<Input
|
||||
name="first_name"
|
||||
|
|
@ -227,6 +261,43 @@ const Profile: NextPage = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold text-custom-text-100">Display Name</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
This could be your first name, or a nickname — however you{"'"}d like people to
|
||||
refer to you in Plane.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<Input
|
||||
id="display_name"
|
||||
name="display_name"
|
||||
autoComplete="off"
|
||||
register={register}
|
||||
error={errors.display_name}
|
||||
className="w-full"
|
||||
placeholder="Enter your display name"
|
||||
validations={{
|
||||
required: "Display name is required.",
|
||||
validate: (value) => {
|
||||
if (value.trim().length < 1) return "Display name can't be empty.";
|
||||
|
||||
if (value.split(" ").length > 1)
|
||||
return "Display name can't have two consecutive spaces.";
|
||||
|
||||
if (value.replace(/\s/g, "").length < 1)
|
||||
return "Display name must be at least 1 characters long.";
|
||||
|
||||
if (value.replace(/\s/g, "").length > 20)
|
||||
return "Display name must be less than 20 characters long.";
|
||||
|
||||
return true;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold text-custom-text-100">Email</h4>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
// next-themes
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// layouts
|
||||
|
|
@ -14,37 +11,47 @@ import { Spinner } from "components/ui";
|
|||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// types
|
||||
import { ICustomTheme } from "types";
|
||||
// mobx react lite
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// next themes
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
const ProfilePreferences = () => {
|
||||
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
||||
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const ProfilePreferences = observer(() => {
|
||||
const { user: myProfile } = useUserAuth();
|
||||
|
||||
const store: any = useMobxStore();
|
||||
const { theme } = useTheme();
|
||||
|
||||
console.log("store", store?.theme?.theme);
|
||||
console.log("theme", theme);
|
||||
|
||||
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
||||
|
||||
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme === "custom") {
|
||||
if (myProfile?.theme.palette)
|
||||
if (store?.user && store?.theme?.theme === "custom") {
|
||||
const currentTheme = store?.user?.currentUserSettings?.theme;
|
||||
if (currentTheme.palette)
|
||||
setPreLoadedData({
|
||||
background: myProfile.theme.background !== "" ? myProfile.theme.background : "#0d101b",
|
||||
text: myProfile.theme.text !== "" ? myProfile.theme.text : "#c5c5c5",
|
||||
primary: myProfile.theme.primary !== "" ? myProfile.theme.primary : "#3f76ff",
|
||||
background: currentTheme.background !== "" ? currentTheme.background : "#0d101b",
|
||||
text: currentTheme.text !== "" ? currentTheme.text : "#c5c5c5",
|
||||
primary: currentTheme.primary !== "" ? currentTheme.primary : "#3f76ff",
|
||||
sidebarBackground:
|
||||
myProfile.theme.sidebarBackground !== ""
|
||||
? myProfile.theme.sidebarBackground
|
||||
: "#0d101b",
|
||||
sidebarText: myProfile.theme.sidebarText !== "" ? myProfile.theme.sidebarText : "#c5c5c5",
|
||||
currentTheme.sidebarBackground !== "" ? currentTheme.sidebarBackground : "#0d101b",
|
||||
sidebarText: currentTheme.sidebarText !== "" ? currentTheme.sidebarText : "#c5c5c5",
|
||||
darkPalette: false,
|
||||
palette:
|
||||
myProfile.theme.palette !== ",,,,"
|
||||
? myProfile.theme.palette
|
||||
currentTheme.palette !== ",,,,"
|
||||
? currentTheme.palette
|
||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||
theme: "custom",
|
||||
});
|
||||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||
setCustomThemeSelectorOptions((prevData) => true);
|
||||
}
|
||||
}, [myProfile, theme, customThemeSelectorOptions]);
|
||||
}, [store, store?.theme?.theme]);
|
||||
|
||||
return (
|
||||
<WorkspaceAuthorizationLayout
|
||||
|
|
@ -91,6 +98,6 @@ const ProfilePreferences = () => {
|
|||
)}
|
||||
</WorkspaceAuthorizationLayout>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default ProfilePreferences;
|
||||
|
|
|
|||
|
|
@ -28,22 +28,24 @@ import { PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS } from "constants/fetch-keys";
|
|||
import { truncateText } from "helpers/string.helper";
|
||||
|
||||
const defaultValues = {
|
||||
name: "",
|
||||
assignees_list: [],
|
||||
description: "",
|
||||
description_html: "",
|
||||
estimate_point: null,
|
||||
state: "",
|
||||
assignees_list: [],
|
||||
priority: "low",
|
||||
target_date: new Date().toString(),
|
||||
issue_cycle: null,
|
||||
issue_module: null,
|
||||
labels_list: [],
|
||||
name: "",
|
||||
priority: "low",
|
||||
start_date: null,
|
||||
state: "",
|
||||
target_date: null,
|
||||
};
|
||||
|
||||
const IssueDetailsPage: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
// console.log(workspaceSlug, "workspaceSlug")
|
||||
|
||||
const { user } = useUserAuth();
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const ProjectIssues: NextPage = () => {
|
|||
<IssuesFilterView />
|
||||
<SecondaryButton
|
||||
onClick={() => setAnalyticsModal(true)}
|
||||
className="!py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-sidebar-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
|
||||
className="!py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
|
||||
outline
|
||||
>
|
||||
Analytics
|
||||
|
|
@ -72,7 +72,7 @@ const ProjectIssues: NextPage = () => {
|
|||
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`}>
|
||||
<a>
|
||||
<SecondaryButton
|
||||
className="relative !py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-sidebar-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
|
||||
className="relative !py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
|
||||
outline
|
||||
>
|
||||
<span>Inbox</span>
|
||||
|
|
@ -97,6 +97,7 @@ const ProjectIssues: NextPage = () => {
|
|||
</PrimaryButton>
|
||||
</div>
|
||||
}
|
||||
bg="secondary"
|
||||
>
|
||||
<AnalyticsProjectModal isOpen={analyticsModal} onClose={() => setAnalyticsModal(false)} />
|
||||
<div className="h-full w-full flex flex-col">
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const ProjectModules: NextPage = () => {
|
|||
: null
|
||||
);
|
||||
|
||||
const { data: modules } = useSWR<IModule[]>(
|
||||
const { data: modules, mutate: mutateModules } = useSWR(
|
||||
workspaceSlug && projectId ? MODULE_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => modulesService.getModules(workspaceSlug as string, projectId as string)
|
||||
|
|
@ -139,7 +139,9 @@ const ProjectModules: NextPage = () => {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{modulesView === "gantt_chart" && <ModulesListGanttChartView modules={modules} />}
|
||||
{modulesView === "gantt_chart" && (
|
||||
<ModulesListGanttChartView modules={modules} mutateModules={mutateModules} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -629,17 +629,19 @@ const SinglePage: NextPage = () => {
|
|||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{pageBlocks.map((block, index) => (
|
||||
<SinglePageBlock
|
||||
key={block.id}
|
||||
block={block}
|
||||
projectDetails={projectDetails}
|
||||
showBlockDetails={showBlock}
|
||||
index={index}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
<>
|
||||
{pageBlocks.map((block, index) => (
|
||||
<SinglePageBlock
|
||||
key={block.id}
|
||||
block={block}
|
||||
projectDetails={projectDetails}
|
||||
showBlockDetails={showBlock}
|
||||
index={index}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ const ControlSettings: NextPage = () => {
|
|||
{...field}
|
||||
label={
|
||||
people?.find((person) => person.member.id === field.value)?.member
|
||||
.first_name ?? <span className="text-custom-text-200">Select lead</span>
|
||||
.display_name ?? <span className="text-custom-text-200">Select lead</span>
|
||||
}
|
||||
width="w-full"
|
||||
input
|
||||
|
|
@ -153,14 +153,10 @@ const ControlSettings: NextPage = () => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 capitalize text-white">
|
||||
{person.member.first_name && person.member.first_name !== ""
|
||||
? person.member.first_name.charAt(0)
|
||||
: person.member.email.charAt(0)}
|
||||
{person.member.display_name?.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
{person.member.first_name !== ""
|
||||
? person.member.first_name
|
||||
: person.member.email}
|
||||
{person.member.display_name}
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
|
|
@ -190,7 +186,7 @@ const ControlSettings: NextPage = () => {
|
|||
<CustomSelect
|
||||
{...field}
|
||||
label={
|
||||
people?.find((p) => p.member.id === field.value)?.member.first_name ?? (
|
||||
people?.find((p) => p.member.id === field.value)?.member.display_name ?? (
|
||||
<span className="text-custom-text-200">Select default assignee</span>
|
||||
)
|
||||
}
|
||||
|
|
@ -214,14 +210,10 @@ const ControlSettings: NextPage = () => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 capitalize text-white">
|
||||
{person.member.first_name && person.member.first_name !== ""
|
||||
? person.member.first_name.charAt(0)
|
||||
: person.member.email.charAt(0)}
|
||||
{person.member.display_name?.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
{person.member.first_name !== ""
|
||||
? person.member.first_name
|
||||
: person.member.email}
|
||||
{person.member.display_name}
|
||||
</div>
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import { truncateText } from "helpers/string.helper";
|
|||
import { IProject, IWorkspace } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
import { PROJECTS_LIST, PROJECT_DETAILS, USER_PROJECT_VIEW } from "constants/fetch-keys";
|
||||
// constants
|
||||
import { NETWORK_CHOICES } from "constants/project";
|
||||
|
||||
|
|
@ -62,6 +62,13 @@ const GeneralSettings: NextPage = () => {
|
|||
: null
|
||||
);
|
||||
|
||||
const { data: memberDetails, error } = useSWR(
|
||||
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
|
|
@ -157,6 +164,8 @@ const GeneralSettings: NextPage = () => {
|
|||
|
||||
const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
|
||||
|
||||
const isAdmin = memberDetails?.role === 20;
|
||||
|
||||
return (
|
||||
<ProjectAuthorizationWrapper
|
||||
breadcrumbs={
|
||||
|
|
@ -355,7 +364,7 @@ const GeneralSettings: NextPage = () => {
|
|||
</div>
|
||||
<div className="sm:text-right">
|
||||
{projectDetails ? (
|
||||
<SecondaryButton type="submit" loading={isSubmitting}>
|
||||
<SecondaryButton type="submit" loading={isSubmitting} disabled={!isAdmin}>
|
||||
{isSubmitting ? "Updating Project..." : "Update Project"}
|
||||
</SecondaryButton>
|
||||
) : (
|
||||
|
|
@ -364,32 +373,34 @@ const GeneralSettings: NextPage = () => {
|
|||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold">Danger Zone</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
The danger zone of the project delete page is a critical area that requires careful
|
||||
consideration and attention. When deleting a project, all of the data and resources
|
||||
within that project will be permanently removed and cannot be recovered.
|
||||
</p>
|
||||
{memberDetails?.role === 20 && (
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold">Danger Zone</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
The danger zone of the project delete page is a critical area that requires
|
||||
careful consideration and attention. When deleting a project, all of the data and
|
||||
resources within that project will be permanently removed and cannot be recovered.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
{projectDetails ? (
|
||||
<div>
|
||||
<DangerButton
|
||||
onClick={() => setSelectedProject(projectDetails.id ?? null)}
|
||||
outline
|
||||
>
|
||||
Delete Project
|
||||
</DangerButton>
|
||||
</div>
|
||||
) : (
|
||||
<Loader className="mt-2 w-full">
|
||||
<Loader.Item height="46px" width="100px" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
{projectDetails ? (
|
||||
<div>
|
||||
<DangerButton
|
||||
onClick={() => setSelectedProject(projectDetails.id ?? null)}
|
||||
outline
|
||||
>
|
||||
Delete Project
|
||||
</DangerButton>
|
||||
</div>
|
||||
) : (
|
||||
<Loader className="mt-2 w-full">
|
||||
<Loader.Item height="46px" width="100px" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</ProjectAuthorizationWrapper>
|
||||
|
|
|
|||
|
|
@ -133,6 +133,11 @@ const LabelsSettings: NextPage = () => {
|
|||
setLabelForm={setLabelForm}
|
||||
isUpdating={isUpdating}
|
||||
labelToUpdate={labelToUpdate}
|
||||
onClose={() => {
|
||||
setLabelForm(false);
|
||||
setIsUpdating(false);
|
||||
setLabelToUpdate(null);
|
||||
}}
|
||||
ref={scrollToRef}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ import projectService from "services/project.service";
|
|||
import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
import useUser from "hooks/use-user";
|
||||
import useProjectMembers from "hooks/use-project-members";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
// layouts
|
||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
// components
|
||||
|
|
@ -26,7 +27,11 @@ import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
|||
// types
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECT_INVITATIONS, PROJECT_MEMBERS, WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||
import {
|
||||
PROJECT_INVITATIONS_WITH_EMAIL,
|
||||
PROJECT_MEMBERS_WITH_EMAIL,
|
||||
WORKSPACE_DETAILS,
|
||||
} from "constants/fetch-keys";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
// helper
|
||||
|
|
@ -44,6 +49,11 @@ const MembersSettings: NextPage = () => {
|
|||
|
||||
const { user } = useUser();
|
||||
const { projectDetails } = useProjectDetails();
|
||||
const { isOwner } = useProjectMembers(
|
||||
workspaceSlug?.toString(),
|
||||
projectId?.toString(),
|
||||
Boolean(workspaceSlug && projectId)
|
||||
);
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
|
|
@ -51,16 +61,21 @@ const MembersSettings: NextPage = () => {
|
|||
);
|
||||
|
||||
const { data: projectMembers, mutate: mutateMembers } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
|
||||
? PROJECT_MEMBERS_WITH_EMAIL(workspaceSlug.toString(), projectId.toString())
|
||||
: null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectService.projectMembersWithEmail(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: projectInvitations, mutate: mutateInvitations } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_INVITATIONS : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectService.projectInvitations(workspaceSlug as string, projectId as string)
|
||||
? PROJECT_INVITATIONS_WITH_EMAIL(workspaceSlug.toString(), projectId.toString())
|
||||
: null,
|
||||
workspaceSlug && projectId
|
||||
? () =>
|
||||
projectService.projectInvitationsWithEmail(workspaceSlug as string, projectId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
|
|
@ -72,6 +87,7 @@ const MembersSettings: NextPage = () => {
|
|||
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,
|
||||
|
|
@ -83,6 +99,7 @@ const MembersSettings: NextPage = () => {
|
|||
first_name: item.first_name ?? item.email,
|
||||
last_name: item.last_name ?? "",
|
||||
email: item.email,
|
||||
display_name: item.email,
|
||||
role: item.role,
|
||||
status: item.accepted,
|
||||
member: false,
|
||||
|
|
@ -122,7 +139,7 @@ const MembersSettings: NextPage = () => {
|
|||
selectedRemoveMember
|
||||
);
|
||||
mutateMembers(
|
||||
(prevData) => prevData?.filter((item: any) => item.id !== selectedRemoveMember),
|
||||
(prevData: any) => prevData?.filter((item: any) => item.id !== selectedRemoveMember),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
|
@ -133,7 +150,8 @@ const MembersSettings: NextPage = () => {
|
|||
selectedInviteRemoveMember
|
||||
);
|
||||
mutateInvitations(
|
||||
(prevData) => prevData?.filter((item: any) => item.id !== selectedInviteRemoveMember),
|
||||
(prevData: any) =>
|
||||
prevData?.filter((item: any) => item.id !== selectedInviteRemoveMember),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
|
@ -177,32 +195,41 @@ const MembersSettings: NextPage = () => {
|
|||
? members.map((member) => (
|
||||
<div key={member.id} className="flex items-center justify-between py-6">
|
||||
<div className="flex items-center gap-x-6 gap-y-2">
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
||||
{member.avatar && member.avatar !== "" ? (
|
||||
{member.avatar && member.avatar !== "" ? (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg p-4 capitalize text-white">
|
||||
<img
|
||||
src={member.avatar}
|
||||
alt={member.first_name}
|
||||
alt={member.display_name}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded-lg"
|
||||
/>
|
||||
) : member.first_name !== "" ? (
|
||||
member.first_name.charAt(0)
|
||||
) : (
|
||||
member.email.charAt(0)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : member.display_name || member.email ? (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
||||
{(member.display_name || member.email)?.charAt(0)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
||||
?
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{member.member ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||
<a className="text-sm">
|
||||
{member.first_name} {member.last_name}
|
||||
<span>
|
||||
{member.first_name} {member.last_name}
|
||||
</span>
|
||||
<span className="text-custom-text-300 text-sm ml-2">
|
||||
({member.display_name})
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<h4 className="text-sm">
|
||||
{member.first_name} {member.last_name}
|
||||
</h4>
|
||||
<h4 className="text-sm">{member.display_name || member.email}</h4>
|
||||
)}
|
||||
{isOwner && (
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">{member.email}</p>
|
||||
)}
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">{member.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
|||
import { JoinProjectModal } from "components/project/join-project-modal";
|
||||
import { DeleteProjectModal, SingleProjectCard } from "components/project";
|
||||
// ui
|
||||
import { EmptyState, Loader, PrimaryButton } from "components/ui";
|
||||
import { EmptyState, Icon, Loader, PrimaryButton } from "components/ui";
|
||||
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
|
||||
// icons
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
|
|
@ -34,6 +34,8 @@ const ProjectsPage: NextPage = () => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const { user } = useUserAuth();
|
||||
// context data
|
||||
const { activeWorkspace } = useWorkspaces();
|
||||
|
|
@ -42,6 +44,15 @@ const ProjectsPage: NextPage = () => {
|
|||
const [deleteProject, setDeleteProject] = useState<string | null>(null);
|
||||
const [selectedProjectToJoin, setSelectedProjectToJoin] = useState<string | null>(null);
|
||||
|
||||
const filteredProjectList =
|
||||
query === ""
|
||||
? projects
|
||||
: projects?.filter(
|
||||
(project) =>
|
||||
project.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||
project.identifier.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<WorkspaceAuthorizationLayout
|
||||
breadcrumbs={
|
||||
|
|
@ -53,16 +64,28 @@ const ProjectsPage: NextPage = () => {
|
|||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<PrimaryButton
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", { key: "p" });
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Project
|
||||
</PrimaryButton>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex w-full gap-1 items-center justify-start rounded-md px-2 py-1.5 border border-custom-border-300 bg-custom-background-90">
|
||||
<Icon iconName="search" className="!text-xl !leading-5 !text-custom-sidebar-text-400" />
|
||||
<input
|
||||
className="w-full border-none bg-transparent text-xs text-custom-text-200 focus:outline-none"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Search"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PrimaryButton
|
||||
className="flex items-center gap-2 flex-shrink-0"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", { key: "p" });
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Project
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<JoinProjectModal
|
||||
|
|
@ -91,12 +114,12 @@ const ProjectsPage: NextPage = () => {
|
|||
data={projects?.find((item) => item.id === deleteProject) ?? null}
|
||||
user={user}
|
||||
/>
|
||||
{projects ? (
|
||||
{filteredProjectList ? (
|
||||
<div className="h-full w-full overflow-hidden">
|
||||
{projects.length > 0 ? (
|
||||
{filteredProjectList.length > 0 ? (
|
||||
<div className="h-full p-8 overflow-y-auto">
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{projects.map((project) => (
|
||||
{filteredProjectList.map((project) => (
|
||||
<SingleProjectCard
|
||||
key={project.id}
|
||||
project={project}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import workspaceService from "services/workspace.service";
|
|||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import { SettingsHeader } from "components/workspace";
|
||||
// components
|
||||
import IntegrationGuide from "components/integration/guide";
|
||||
import ExportGuide from "components/exporter/guide";
|
||||
import { IntegrationAndImportExportBanner } from "components/ui";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
|
|
@ -37,14 +37,14 @@ const ImportExport: NextPage = () => {
|
|||
link={`/${workspaceSlug}`}
|
||||
linkTruncate
|
||||
/>
|
||||
<BreadcrumbItem title="Import/ Export Settings" unshrinkTitle />
|
||||
<BreadcrumbItem title="Export Settings" unshrinkTitle />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="p-8 space-y-4">
|
||||
<SettingsHeader />
|
||||
<IntegrationAndImportExportBanner bannerName="Import/ Export" />
|
||||
<IntegrationGuide />
|
||||
<IntegrationAndImportExportBanner bannerName="Export" />
|
||||
<ExportGuide />
|
||||
</div>
|
||||
</WorkspaceAuthorizationLayout>
|
||||
);
|
||||
58
apps/app/pages/[workspaceSlug]/settings/imports.tsx
Normal file
58
apps/app/pages/[workspaceSlug]/settings/imports.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { useRouter } from "next/router";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
// layouts
|
||||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import { SettingsHeader } from "components/workspace";
|
||||
// components
|
||||
import IntegrationGuide from "components/integration/guide";
|
||||
import { IntegrationAndImportExportBanner } from "components/ui";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
// types
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||
// helper
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
|
||||
const ImportExport: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null)
|
||||
);
|
||||
|
||||
return (
|
||||
<WorkspaceAuthorizationLayout
|
||||
breadcrumbs={
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbItem
|
||||
title={`${truncateText(activeWorkspace?.name ?? "Workspace", 32)}`}
|
||||
link={`/${workspaceSlug}`}
|
||||
linkTruncate
|
||||
/>
|
||||
<BreadcrumbItem title="Import/ Export Settings" unshrinkTitle />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="p-8 space-y-4">
|
||||
<SettingsHeader />
|
||||
<IntegrationAndImportExportBanner
|
||||
bannerName="Import/ Export"
|
||||
description="Integrations and importers are only available on the cloud version. We plan to open-source
|
||||
our SDKs in the near future so that the community can request or contribute integrations as
|
||||
needed."
|
||||
/>
|
||||
<IntegrationGuide />
|
||||
</div>
|
||||
</WorkspaceAuthorizationLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImportExport;
|
||||
|
|
@ -28,7 +28,7 @@ import { copyTextToClipboard, truncateText } from "helpers/string.helper";
|
|||
import type { IWorkspace } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_DETAILS, USER_WORKSPACES } from "constants/fetch-keys";
|
||||
import { WORKSPACE_DETAILS, USER_WORKSPACES, WORKSPACE_MEMBERS_ME } from "constants/fetch-keys";
|
||||
// constants
|
||||
import { ORGANIZATION_SIZE } from "constants/workspace";
|
||||
|
||||
|
|
@ -50,6 +50,11 @@ const WorkspaceSettings: NextPage = () => {
|
|||
|
||||
const { user } = useUserAuth();
|
||||
|
||||
const { data: memberDetails } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug.toString()) : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceMemberMe(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
|
|
@ -142,6 +147,8 @@ const WorkspaceSettings: NextPage = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const isAdmin = memberDetails?.role === 20;
|
||||
|
||||
return (
|
||||
<WorkspaceAuthorizationLayout
|
||||
breadcrumbs={
|
||||
|
|
@ -314,26 +321,32 @@ const WorkspaceSettings: NextPage = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="sm:text-right">
|
||||
<SecondaryButton onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
<SecondaryButton
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
loading={isSubmitting}
|
||||
disabled={!isAdmin}
|
||||
>
|
||||
{isSubmitting ? "Updating..." : "Update Workspace"}
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold">Danger Zone</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
The danger zone of the workspace delete page is a critical area that requires
|
||||
careful consideration and attention. When deleting a workspace, all of the data
|
||||
and resources within that workspace will be permanently removed and cannot be
|
||||
recovered.
|
||||
</p>
|
||||
{memberDetails?.role === 20 && (
|
||||
<div className="grid grid-cols-12 gap-4 sm:gap-16">
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<h4 className="text-lg font-semibold">Danger Zone</h4>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
The danger zone of the workspace delete page is a critical area that requires
|
||||
careful consideration and attention. When deleting a workspace, all of the data
|
||||
and resources within that workspace will be permanently removed and cannot be
|
||||
recovered.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<DangerButton onClick={() => setIsOpen(true)} outline>
|
||||
Delete the workspace
|
||||
</DangerButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<DangerButton onClick={() => setIsOpen(true)} outline>
|
||||
Delete the workspace
|
||||
</DangerButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid h-full w-full place-items-center px-4 sm:px-0">
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import workspaceService from "services/workspace.service";
|
|||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUser from "hooks/use-user";
|
||||
import useWorkspaceMembers from "hooks/use-workspace-members";
|
||||
// layouts
|
||||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import { SettingsHeader } from "components/workspace";
|
||||
|
|
@ -24,7 +25,11 @@ import { PlusIcon } from "@heroicons/react/24/outline";
|
|||
// types
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_DETAILS, WORKSPACE_INVITATIONS, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||
import {
|
||||
WORKSPACE_DETAILS,
|
||||
WORKSPACE_INVITATION_WITH_EMAIL,
|
||||
WORKSPACE_MEMBERS_WITH_EMAIL,
|
||||
} from "constants/fetch-keys";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
// helper
|
||||
|
|
@ -42,19 +47,25 @@ const MembersSettings: NextPage = () => {
|
|||
|
||||
const { user } = useUser();
|
||||
|
||||
const { isOwner } = useWorkspaceMembers(workspaceSlug?.toString(), Boolean(workspaceSlug));
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug.toString()) : null,
|
||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug.toString()) : null)
|
||||
);
|
||||
|
||||
const { data: workspaceMembers, mutate: mutateMembers } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug.toString()) : null
|
||||
workspaceSlug ? WORKSPACE_MEMBERS_WITH_EMAIL(workspaceSlug.toString()) : null,
|
||||
workspaceSlug
|
||||
? () => workspaceService.workspaceMembersWithEmail(workspaceSlug.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: workspaceInvitations, mutate: mutateInvitations } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_INVITATIONS : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null
|
||||
workspaceSlug ? WORKSPACE_INVITATION_WITH_EMAIL(workspaceSlug.toString()) : null,
|
||||
workspaceSlug
|
||||
? () => workspaceService.workspaceInvitationsWithEmail(workspaceSlug.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
const members = [
|
||||
|
|
@ -65,6 +76,7 @@ const MembersSettings: NextPage = () => {
|
|||
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,
|
||||
|
|
@ -77,6 +89,7 @@ const MembersSettings: NextPage = () => {
|
|||
first_name: item.email,
|
||||
last_name: "",
|
||||
email: item.email,
|
||||
display_name: item.email,
|
||||
role: item.role,
|
||||
status: item.accepted,
|
||||
member: false,
|
||||
|
|
@ -126,14 +139,15 @@ const MembersSettings: NextPage = () => {
|
|||
});
|
||||
})
|
||||
.finally(() => {
|
||||
mutateMembers((prevData) =>
|
||||
prevData?.filter((item) => item.id !== selectedRemoveMember)
|
||||
mutateMembers((prevData: any) =>
|
||||
prevData?.filter((item: any) => item.id !== selectedRemoveMember)
|
||||
);
|
||||
});
|
||||
}
|
||||
if (selectedInviteRemoveMember) {
|
||||
mutateInvitations(
|
||||
(prevData) => prevData?.filter((item) => item.id !== selectedInviteRemoveMember),
|
||||
(prevData: any) =>
|
||||
prevData?.filter((item: any) => item.id !== selectedInviteRemoveMember),
|
||||
false
|
||||
);
|
||||
workspaceService
|
||||
|
|
@ -194,32 +208,41 @@ const MembersSettings: NextPage = () => {
|
|||
? members.map((member) => (
|
||||
<div key={member.id} className="flex items-center justify-between py-6">
|
||||
<div className="flex items-center gap-x-8 gap-y-2">
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg bg-gray-700 p-4 capitalize text-white">
|
||||
{member.avatar && member.avatar !== "" ? (
|
||||
{member.avatar && member.avatar !== "" ? (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg p-4 capitalize text-white">
|
||||
<img
|
||||
src={member.avatar}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded-lg"
|
||||
alt={member.first_name}
|
||||
alt={member.display_name || member.email}
|
||||
/>
|
||||
) : member.first_name !== "" ? (
|
||||
member.first_name.charAt(0)
|
||||
) : (
|
||||
member.email.charAt(0)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : member.display_name || member.email ? (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg p-4 capitalize bg-gray-700 text-white">
|
||||
{(member.display_name || member.email)?.charAt(0)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative flex h-10 w-10 items-center justify-center rounded-lg p-4 capitalize bg-gray-700 text-white">
|
||||
?
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{member.member ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||
<a className="text-sm">
|
||||
{member.first_name} {member.last_name}
|
||||
<span>
|
||||
{member.first_name} {member.last_name}
|
||||
</span>
|
||||
<span className="text-custom-text-300 text-sm ml-2">
|
||||
({member.display_name})
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<h4 className="text-sm">
|
||||
{member.first_name} {member.last_name}
|
||||
</h4>
|
||||
<h4 className="text-sm">{member.display_name || member.email}</h4>
|
||||
)}
|
||||
{isOwner && (
|
||||
<p className="text-xs text-custom-text-200">{member.email}</p>
|
||||
)}
|
||||
<p className="text-xs text-custom-text-200">{member.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
|
|
@ -240,8 +263,8 @@ const MembersSettings: NextPage = () => {
|
|||
if (!workspaceSlug) return;
|
||||
|
||||
mutateMembers(
|
||||
(prevData) =>
|
||||
prevData?.map((m) =>
|
||||
(prevData: any) =>
|
||||
prevData?.map((m: any) =>
|
||||
m.id === member.id ? { ...m, role: value } : m
|
||||
),
|
||||
false
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ import {
|
|||
SITE_KEYWORDS,
|
||||
SITE_TITLE,
|
||||
} from "constants/seo-variables";
|
||||
// mobx store provider
|
||||
import { MobxStoreProvider } from "lib/mobx/store-provider";
|
||||
import MobxStoreInit from "lib/mobx/store-init";
|
||||
|
||||
const CrispWithNoSSR = dynamic(() => import("constants/crisp"), { ssr: false });
|
||||
|
||||
|
|
@ -45,9 +48,10 @@ Router.events.on("routeChangeComplete", NProgress.done);
|
|||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
// <UserProvider>
|
||||
<ThemeProvider themes={THEMES} defaultTheme="system">
|
||||
<ToastContextProvider>
|
||||
<ThemeContextProvider>
|
||||
// mobx root provider
|
||||
<MobxStoreProvider {...pageProps}>
|
||||
<ThemeProvider themes={THEMES} defaultTheme="system">
|
||||
<ToastContextProvider>
|
||||
<CrispWithNoSSR />
|
||||
<Head>
|
||||
<title>{SITE_TITLE}</title>
|
||||
|
|
@ -64,10 +68,11 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||
<link rel="manifest" href="/site.webmanifest.json" />
|
||||
<link rel="shortcut icon" href="/favicon/favicon.ico" />
|
||||
</Head>
|
||||
<MobxStoreInit />
|
||||
<Component {...pageProps} />
|
||||
</ThemeContextProvider>
|
||||
</ToastContextProvider>
|
||||
</ThemeProvider>
|
||||
</ToastContextProvider>
|
||||
</ThemeProvider>
|
||||
</MobxStoreProvider>
|
||||
// </UserProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
email: user.email,
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
display_name: user?.display_name,
|
||||
})
|
||||
.then(() => {
|
||||
jitsu.track(eventName, {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,14 @@ import {
|
|||
import { Spinner } from "components/ui";
|
||||
// images
|
||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
// mobx react lite
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// next themes
|
||||
import { useTheme } from "next-themes";
|
||||
import { ICurrentUserResponse, IUser } from "types";
|
||||
import { IUser } from "types";
|
||||
|
||||
// types
|
||||
type EmailPasswordFormValues = {
|
||||
email: string;
|
||||
|
|
@ -31,15 +37,18 @@ type EmailPasswordFormValues = {
|
|||
medium?: string;
|
||||
};
|
||||
|
||||
const HomePage: NextPage = () => {
|
||||
const HomePage: NextPage = observer(() => {
|
||||
const store: any = useMobxStore();
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const { isLoading, mutateUser } = useUserAuth("sign-in");
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const changeTheme = (user: IUser) => {
|
||||
setTheme(user.theme.theme ?? "system");
|
||||
const handleTheme = (user: IUser) => {
|
||||
const currentTheme = user.theme.theme ?? "system";
|
||||
setTheme(currentTheme);
|
||||
store?.user?.setCurrentUserSettings();
|
||||
};
|
||||
|
||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||
|
|
@ -53,7 +62,7 @@ const HomePage: NextPage = () => {
|
|||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
if (response && response?.user) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
handleTheme(response?.user);
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
|
|
@ -79,7 +88,7 @@ const HomePage: NextPage = () => {
|
|||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||
if (response && response?.user) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
handleTheme(response?.user);
|
||||
}
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
|
|
@ -101,7 +110,7 @@ const HomePage: NextPage = () => {
|
|||
try {
|
||||
if (response) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
handleTheme(response?.user);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
|
|
@ -128,7 +137,7 @@ const HomePage: NextPage = () => {
|
|||
try {
|
||||
if (response) {
|
||||
mutateUser();
|
||||
changeTheme(response.user);
|
||||
handleTheme(response?.user);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setToastAlert({
|
||||
|
|
@ -202,6 +211,6 @@ const HomePage: NextPage = () => {
|
|||
)}
|
||||
</DefaultLayout>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default HomePage;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue