refactor: MobX store structure (#3228)

* query params from router as computed

* chore: setup workspace store and sub-stores

* chore: update router query store

* chore: update store types

* fix: pages store changes

* change observables and retain object reference

* fix build errors

* chore: changed the structure of workspace, project, cycle, module and pages

* fix: pages fixes

* fix: merge conflicts resolved

* chore: fixed workspace list

* chore: update workspace store accroding to the new response

* fix: adding page details to store

* fix: adding new contexts and providers

* dev: issues store and filters in new store

* dev: optimised the issue fetching in issue base store

* chore: project views id mapped

* update lodash set to directly run inside runInaction since it mutates the object

* fix: context changes

* code refactor kanban for better mainatinability

* optimize Kanban for performance

* chore: implemented hooks for all the created stores

* chore: removed bridge id

* css change and refactor

* chore: update cycle store structure

* chore: implement the new label root store

* chore: removed object structure

* chore: implement project view hook

* Kanban new store implementation for project issues

* fix project root for kanban

* feat: workspace and project members endpoint (#3092)

* fix: merge conflicts resolved

* issue properties optimization

* chore: user stores

* chore: create new store context and update hooks

* chore: setup inbox store and implement router store

* chore: initialize and implement project estimate store

* chore: initialize global view store

* kanban and list view optimization

* chore: use new cycle and module store. (#3172)

* chore: use new cycle and module store.

* chore: minor improvements.

* Revert "chore: merge develop"

This reverts commit 9d2e0e29e7370b55b48fc2fee4fd126093a6cc48, reversing
changes made to 9595493c42be3ea0ddd17b23a0b124555075c062.

* chore: implement useGlobalView hook

* refactor: projects & inbox store instances (#3179)

* refactor: projects & inbox store instances

* fix: formatting

* fix: action usage

* chore: implement useProjectState hook. (#3185)

* dev: issue, cycle store optimiation

* fix build for code

* dev: removed dummy variables

* dev: issue store

* fix: adding todos

* chore: removing legacy store

* dev: issues store types and typos

* chore: cycle module user properties

* fix legacy store deletion issues

* chore: change POST to PATCH

* fix issues rendering for project root

* chore: removed workspace details in workpsaceinvite

* chore: created models for display properties

* chore: setup member store and implement it everywhere

* refactor: module store (#3202)

* refactor: cycle store (#3192)

* refator: cycle store

* some more improvements.

* chore: implement useLabel hook. (#3190)

* refactor: inbox & project related stores. (#3193)

* refactor: inbox -> filter, issues, inoxes & project -> publish, projects store

* refactor: workspace-project-id name

* fix kanban dropdown overlapping issue

* fix kanban layout minor re rendering

* chore: implement useMember store everywhere

* chore: create and implement editor mention store

* chore: removed the issue view user property

* chore: created at id changed

* dev: segway intgegration (#3132)

* feat: implemented rabbitmq

* dev: initialize segway with queue setup

* dev: import refactors

* dev: create communication with the segway server

* dev: create new workers

* dev: create celery node queue for consuming messages from django

* dev: node to celery connection

* dev: setup segway and django connection

* dev: refactor the structure and add database integration to the app

* dev: add external id and source added

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>

* dev: github importer (#3205)

* dev: initiate github import

* dev: github importer all issues import

* dev: github comments and links for the imported issues

* dev: update controller to use logger and spread the resultData in getAllEntities

* dev: removed console log

* dev: update code structure and sync functions

* dev: updated retry logic when exception

* dev: add imported data as well

* dev: update logger and repo fetch

* dev: update jira integration to new structure

* dev: update migrations

* dev: update the reason field

* chore: workspace object id removed

* chore: view's creation fixed

* refactor: mobx store improvements. (#3213)

* fix: state and label errors

* chore: remove legacy code

* fix: branch build fix (#3214)

* branch build fix for release-* in case of space,backend,proxy

* fixes

* chore: update store names and types

* fix - file size limit not work on plane.settings.production (#3160)

* fix - file size limit not work on plane.settings.production

* fix - file size limit not work on plane.settings.production

* fix - file size limit not work on plane.settings.production, move to common.py

---------

Co-authored-by: luanduongtel4vn <hoangluan@tel4vn.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>

* style: instance admin email settings ui & ux update. (#3186)

* refactor: use-user-auth hook (#3215)

* refactor: use-user-auth hook

* fix: user store currentUserLoader

* refactor: project-view & application related stores (#3207)

* refactor: project-view & application related stores

* rename: projectViews -> projectViewIds

* fix: project-view favourite state in store

* chore: remove unnecessary hooks and contexts (#3217)

* chore: update issue assignee property component

* chore: bug fixes & improvement (#3218)

* chore: draft issue validation added to prevent saving empty or whitespace title

* chore: resolve scrolling issue in page empty state

* chore: kanban layout quick add issue improvement

* fix: bugs & improvements (#3189)

* fix: workspace invitation modal form values reset

* fix: profile sidebar avatar letter

* [refactor] Editor code refactoring (#3194)

* removed relative imports from editor core

* Update issue widget file paths and imports to use kebab case instead of camel case, to align with coding conventions and improve consistency.

* Update Tiptap core and extensions versions to 2.1.13 and Tiptap React version to 2.1.13. Update Tiptap table imports to use the new location in package @tiptap/pm/tables. Update AlertLabel component to use the new type definition for LucideIcon.

* updated lock file

* removed default exports from editor/core

* fixed injecting css into the core package itself

* seperated css code to have single source of origin wrt to the package

* removed default imports from document editor

* all instances using index as key while mapping fixed

* Update Lite Text Editor package.json to remove @plane/editor-types as a dependency.

Update Lite Text Editor index.ts to update the import of IMentionSuggestion and IMentionHighlight from @plane/editor-types to @plane/editor-core.

Update Lite Text Editor ui/index.tsx to update the import of UploadImage, DeleteImage, IMentionSuggestion, and RestoreImage from @plane/editor-types to @plane/editor-core.

Update Lite Text Editor ui/menus/fixed-menu/index.tsx to update the import of UploadImage from @plane/editor-types to @plane/editor-core.

Update turbo.json to remove @plane/editor-types#build as a dependency for @plane/lite-text-editor#build, @plane/rich-text-editor#build, and @plane/document-editor#build.

* Remove deprecated import and adjust tippy.js usage in the slash-commands.tsx file of the editor extensions package.

* Update dependencies in `rich-text-editor/package.json`, remove `@plane/editor-types` and add `@plane/editor-core` in `rich-text-editor/src/index.ts`, and update imports in `rich-text-editor/src/ui/extensions/index.tsx` and `rich-text-editor/src/ui/index.tsx` to use `@plane/editor-core` instead of `@plane/editor-types`.

* Update package.json dependencies and add new types for image deletion, upload, restore, mention highlight, mention suggestion, and slash command item.

* Update import statements in various files to use the new package "@plane/editor-core" instead of "@plane/editor-types".

* fixed document editor to follow conventions

* Refactor imports in the Rich Text Editor package to use relative paths instead of absolute paths.

- Updated imports in `index.ts`, `ui/index.tsx`, and `ui/menus/bubble-menu/index.tsx` to use relative paths.
- Updated `tsconfig.json` to include the `baseUrl` compiler option and adjust the `include` and `exclude` paths.

* Refactor Lite Text Editor code to use relative import paths instead of absolute import paths.

* Added LucideIconType to the exports in index.ts for use in other files.
Created a new file lucide-icon.ts which contains the type LucideIconType.
Updated the icon type in HeadingOneItem in menu-items/index.tsx to use LucideIconType.
Updated the Icon type in AlertLabel in alert-label.tsx to use LucideIconType.
Updated the Icon type in VerticalDropdownItemProps in vertical-dropdown-menu.tsx to use LucideIconType.
Updated the Icon type in BubbleMenuItem in fixed-menu/index.tsx to use LucideIconType.
Deleted the file tooltip.tsx since it is no longer used.
Updated the Icon type in BubbleMenuItem in bubble-menu/index.tsx to use LucideIconType.

* ♻️ refactor: simplify rendering logic in slash-commands.tsx

The rendering logic in the file "slash-commands.tsx" has been simplified. Previously, the code used inline positioning for the popup, but it has now been removed. Instead of appending the popup to the document body, it is now appended to the element with the ID "tiptap-container". The "flip" option has also been removed. These changes have improved the readability and maintainability of the code.

* fixed build errors caused due to core's internal imports

* regression: fixed pages not saving issue and not duplicating with proper content issue

* build: Update @tiptap dependencies

Updated the @tiptap dependencies in the package.json files of `document-editor`, `extensions`, and `rich-text-editor` packages to version 2.1.13.

* 🚑 fix: Correct appendTo selector in slash-commands.tsx

Update the `appendTo` function call in `slash-commands.tsx` to use the correct selector `#editor-container` instead of `#tiptap-container`. This ensures that the component is appended to the appropriate container in the editor extension.

Note: The commit message assumes that the change is a fix for an issue or error. If it's not a fix, please provide more context so that an appropriate commit type can be determined.

* style: email placeholder changed across the platform (#3206)

* style: email placeholder changed across the platform

* fix: placeholder text

* dev: updated new filter endpoints and restructured issue and issue filters store

* implement issues and replace useMobxStore

* remove all store legacy references

* dev: updated the orderby and subgroupby filters data

* dev:added projectId in issue filters for consistency

* fix more build errors

* dev: updated profile issues

* dev: removed store legacy

* dev: active cycle issues in the cycle issue store

* fix additional build errors and memoize issueActions in each layout component

* change store enums

* remove all useMobxStore references

* fix more build errors

* dev: reverted workspace invitation

* fix: build errors and warnings

* fix: optimistic update for instant operations (#3221)

* fix: update functions failed case

* fix: typo

* chore: revert back to optimistic update approach for all `update related actions` (#3219)

* fix: merge conflicts resolved

* chore: update memberMap logic in components

* add assignees to kanban groups and properties

* dev: migration fixes

* final bit of optimization on list view

* change all TODOs that are to be done before this release to FIXME

* change base Kanban TODOs that are to be done before this release to FIXME

* dev: add fields and expand for app serializers

* dev: issue detail store

* dev: update issue serializer to return object ids

* fix: Instance key added in settings and converted issues list api to arry instead of dict

* fix: removing segway files

* dev: control expand through query parameters

* revert: github importer

* Revert "dev: segway intgegration (#3132)"

This reverts commit 1cc18a09156d1790d114061dbac8c901e0f2754c.

* dev: remove migrations for segway

* dev: issue structure change and created workspacebasemodel

* dev: issue detail serializer

* fix: changed workspace dict

* dev: updated new issue structure

* chore: build fix

* dev: issue detail store refactor

* dev: created list endpoint for issue-relation

* dev: added issue attachments in issue detail store

* dev: added issue activity computed

* fix: build error

* chore: peek overview modal context added

* chore: build error fix

* dev: added sub_issues in issue details store

* dev: added complete issue serializer for sub issues

* dev: resolved type errors in issue root store

* dev: changed the issue relation structure

* chore: new global dropdowns

* chore: build error fix

* chore: cycle and module selection if disabled

* dev: removed unnecessary code from the workspace root

* chore: build error fix

* chore: issue relation remove endpoint

* fix: build error

* dev: typos and implemented issue relation store

* fix: yarn lock updated

* style: update the UI of all the dropdowns

* fix: state store fixes

* fix: key issue

* fix: state store console logs removed

* refactor: member dropdowns

* fix: moving types to packages

* fix: dropdown arrow positioning

* dev: removed logs

* style: label dropdown

* chore: restrict description notifications

* chore: description changes

* chore: update spreadsheet layout dropdowns

* fix: build errors

* chore: duplicate key change

* fix: ui bugs

* chore: relation activity change

* chore: comment activity changes

* chore: blocking issue removal

* chore: added project_id for relation

* chore: issue relation store and component

* chore: issue redirection issue in the issue realtion in detail page

* chore: created activity changed

* chore: issue links new store implementation on the issue detail

* chore: issue relation deletion acitivity changed

* chore: issue attachments new store implementation on the issue detail

* chore: workspace level issues

* fix: build errors

---------

Co-authored-by: rahulramesha <rahulramesham@gmail.com>
Co-authored-by: gurusainath <gurusainath007@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com>
Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
Co-authored-by: Manish Gupta <59428681+mguptahub@users.noreply.github.com>
Co-authored-by: Hoang Luan <luandnh98@gmail.com>
Co-authored-by: luanduongtel4vn <hoangluan@tel4vn.com>
Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: M. Palanikannan <73993394+Palanikannan1437@users.noreply.github.com>
Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
Aaryan Khandelwal 2024-01-02 18:12:55 +05:30 committed by GitHub
parent 1539340113
commit 804b7d8663
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
940 changed files with 26378 additions and 34411 deletions

View file

@ -1,8 +1,8 @@
import React, { Fragment, ReactElement } from "react";
import { observer } from "mobx-react-lite";
import { Tab } from "@headlessui/react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useProject, useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
@ -17,22 +17,24 @@ import emptyAnalytics from "public/empty-state/empty_analytics.webp";
import { ANALYTICS_TABS } from "constants/analytics";
import { EUserWorkspaceRoles } from "constants/workspace";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const AnalyticsPage: NextPageWithLayout = observer(() => {
// store
// store hooks
const {
project: { workspaceProjects },
commandPalette: { toggleCreateProjectModal },
trackEvent: { setTrackElement },
user: { currentProjectRole },
} = useMobxStore();
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const { workspaceProjectIds } = useProject();
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
return (
<>
{workspaceProjects && workspaceProjects.length > 0 ? (
{workspaceProjectIds && workspaceProjectIds.length > 0 ? (
<div className="flex h-full flex-col overflow-hidden bg-custom-background-100">
<Tab.Group as={Fragment}>
<Tab.List as="div" className="space-x-2 border-b border-custom-border-200 px-5 py-3">

View file

@ -5,7 +5,7 @@ import { AppLayout } from "layouts/app-layout";
import { WorkspaceDashboardView } from "components/page-views";
import { WorkspaceDashboardHeader } from "components/headers/workspace-dashboard";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const WorkspacePage: NextPageWithLayout = () => <WorkspaceDashboardView />;

View file

@ -5,7 +5,7 @@ import { ProfileAuthWrapper } from "layouts/user-profile-layout";
// components
import { UserProfileHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
import { ProfileIssuesPage } from "components/profile/profile-issues";
const ProfileAssignedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="assigned" />;

View file

@ -7,7 +7,7 @@ import { ProfileAuthWrapper } from "layouts/user-profile-layout";
// components
import { UserProfileHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
import { ProfileIssuesPage } from "components/profile/profile-issues";
const ProfileCreatedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="created" />;

View file

@ -16,8 +16,8 @@ import {
ProfileWorkload,
} from "components/profile";
// types
import { IUserStateDistribution, TStateGroups } from "types";
import { NextPageWithLayout } from "types/app";
import { IUserStateDistribution, TStateGroups } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// constants
import { USER_PROFILE_DATA } from "constants/fetch-keys";
import { GROUP_CHOICES } from "constants/project";

View file

@ -7,7 +7,7 @@ import { ProfileAuthWrapper } from "layouts/user-profile-layout";
// components
import { UserProfileHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
import { ProfileIssuesPage } from "components/profile/profile-issues";
const ProfileSubscribedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="subscribed" />;

View file

@ -16,17 +16,18 @@ import { ArchiveIcon, Loader } from "@plane/ui";
// icons
import { History } from "lucide-react";
// types
import { IIssue } from "types";
import { NextPageWithLayout } from "types/app";
import { TIssue } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// fetch-keys
import { PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS } from "constants/fetch-keys";
import { useProject } from "hooks/store";
const defaultValues: Partial<IIssue> = {
const defaultValues: Partial<TIssue> = {
name: "",
description: "",
// description: "",
description_html: "",
estimate_point: null,
state: "",
state_id: "",
priority: "low",
target_date: new Date().toString(),
issue_cycle: null,
@ -45,8 +46,9 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
const [isRestoring, setIsRestoring] = useState(false);
// hooks
const { setToastAlert } = useToast();
const { getProjectById } = useProject();
const { data: issueDetails, mutate: mutateIssueDetails } = useSWR<IIssue | undefined>(
const { data: issueDetails, mutate: mutateIssueDetails } = useSWR<TIssue | undefined>(
workspaceSlug && projectId && archivedIssueId ? ISSUE_DETAILS(archivedIssueId as string) : null,
workspaceSlug && projectId && archivedIssueId
? () =>
@ -58,15 +60,15 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
: null
);
const { reset, control, watch } = useForm<IIssue>({
const { reset, control, watch } = useForm<TIssue>({
defaultValues,
});
const submitChanges = useCallback(
async (formData: Partial<IIssue>) => {
async (formData: Partial<TIssue>) => {
if (!workspaceSlug || !projectId || !archivedIssueId) return;
mutate<IIssue>(
mutate<TIssue>(
ISSUE_DETAILS(archivedIssueId as string),
(prevData) => {
if (!prevData) return prevData;
@ -79,7 +81,7 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
false
);
const payload: Partial<IIssue> = {
const payload: Partial<TIssue> = {
...formData,
};
@ -116,7 +118,11 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
setToastAlert({
type: "success",
title: "Success",
message: `${issueDetails?.project_detail?.identifier}-${issueDetails?.sequence_id} is restored successfully under the project ${issueDetails?.project_detail?.name}`,
message:
issueDetails &&
`${getProjectById(issueDetails.project_id)?.identifier}-${
issueDetails?.sequence_id
} is restored successfully under the project ${getProjectById(issueDetails.project_id)?.name}`,
});
router.push(`/${workspaceSlug}/projects/${projectId}/issues/${archivedIssueId}`);
})

View file

@ -3,7 +3,6 @@ import { useRouter } from "next/router";
// layouts
import { AppLayout } from "layouts/app-layout";
// contexts
import { IssueViewContextProvider } from "contexts/issue-view.context";
import { ArchivedIssueLayoutRoot } from "components/issues";
// ui
import { ArchiveIcon } from "@plane/ui";
@ -11,7 +10,7 @@ import { ProjectArchivedIssuesHeader } from "components/headers";
// icons
import { X } from "lucide-react";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectArchivedIssuesPage: NextPageWithLayout = () => {
const router = useRouter();
@ -37,11 +36,9 @@ const ProjectArchivedIssuesPage: NextPageWithLayout = () => {
ProjectArchivedIssuesPage.getLayout = function getLayout(page: ReactElement) {
return (
<IssueViewContextProvider>
<AppLayout header={<ProjectArchivedIssuesHeader />} withProjectWrapper>
{page}
</AppLayout>
</IssueViewContextProvider>
<AppLayout header={<ProjectArchivedIssuesHeader />} withProjectWrapper>
{page}
</AppLayout>
);
};

View file

@ -1,9 +1,8 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useCycle } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// layouts
import { AppLayout } from "layouts/app-layout";
@ -16,27 +15,26 @@ import { EmptyState } from "components/common";
// assets
import emptyCycle from "public/empty-state/cycle.svg";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const CycleDetailPage: NextPageWithLayout = () => {
// router
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query;
const { cycle: cycleStore } = useMobxStore();
// store hooks
const { fetchCycleDetails } = useCycle();
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
const { error } = useSWR(
workspaceSlug && projectId && cycleId ? `CURRENT_CYCLE_DETAILS_${cycleId.toString()}` : null,
workspaceSlug && projectId && cycleId ? `CYCLE_DETAILS_${cycleId.toString()}` : null,
workspaceSlug && projectId && cycleId
? () => cycleStore.fetchCycleWithId(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
? () => fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
: null
);
const toggleSidebar = () => {
setValue(`${!isSidebarCollapsed}`);
};
const toggleSidebar = () => setValue(`${!isSidebarCollapsed}`);
return (
<>

View file

@ -1,83 +1,58 @@
import { Fragment, useCallback, useEffect, useState, ReactElement } from "react";
import { Fragment, useCallback, useState, ReactElement } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Tab } from "@headlessui/react";
import { Plus } from "lucide-react";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useCycle, useUser } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { CyclesHeader } from "components/headers";
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
import { NewEmptyState } from "components/common/new-empty-state";
// ui
import { Tooltip } from "@plane/ui";
// images
import emptyCycle from "public/empty-state/empty_cycles.webp";
// types
import { TCycleView, TCycleLayout } from "types";
import { NextPageWithLayout } from "types/app";
import { TCycleView, TCycleLayout } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// constants
import { CYCLE_TAB_LIST, CYCLE_VIEW_LAYOUTS } from "constants/cycle";
import { EUserWorkspaceRoles } from "constants/workspace";
// lib cookie
import { setLocalStorage, getLocalStorage } from "lib/local-storage";
import { NewEmptyState } from "components/common/new-empty-state";
// TODO: use-local-storage hook instead of lib file.
const ProjectCyclesPage: NextPageWithLayout = observer(() => {
const [createModal, setCreateModal] = useState(false);
// store
// store hooks
const {
cycle: cycleStore,
user: { currentProjectRole },
} = useMobxStore();
const { projectCycles } = cycleStore;
membership: { currentProjectRole },
} = useUser();
const { currentProjectCycleIds } = useCycle();
// router
const router = useRouter();
const { workspaceSlug, projectId, peekCycle } = router.query;
// local storage
const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage<TCycleView>("cycle_tab", "active");
const { storedValue: cycleLayout, setValue: setCycleLayout } = useLocalStorage<TCycleLayout>("cycle_layout", "list");
const handleCurrentLayout = useCallback(
(_layout: TCycleLayout) => {
if (projectId) {
setLocalStorage(`cycle_layout:${projectId}`, _layout);
cycleStore.setCycleLayout(_layout);
}
setCycleLayout(_layout);
},
[cycleStore, projectId]
[setCycleLayout]
);
const handleCurrentView = useCallback(
(_view: TCycleView) => {
if (projectId) {
setLocalStorage(`cycle_view:${projectId}`, _view);
cycleStore.setCycleView(_view);
if (_view === "draft" && cycleStore.cycleLayout === "gantt") {
handleCurrentLayout("list");
}
}
setCycleTab(_view);
if (_view === "draft") handleCurrentLayout("list");
},
[cycleStore, projectId, handleCurrentLayout]
[handleCurrentLayout, setCycleTab]
);
useEffect(() => {
if (projectId) {
const _viewKey = `cycle_view:${projectId}`;
const _viewValue = getLocalStorage(_viewKey);
if (_viewValue && _viewValue !== cycleStore?.cycleView) cycleStore.setCycleView(_viewValue as TCycleView);
else handleCurrentView("all");
const _layoutKey = `cycle_layout:${projectId}`;
const _layoutValue = getLocalStorage(_layoutKey);
if (_layoutValue && _layoutValue !== cycleStore?.cycleView)
cycleStore.setCycleLayout(_layoutValue as TCycleLayout);
else handleCurrentLayout("list");
}
}, [projectId, cycleStore, handleCurrentView, handleCurrentLayout]);
const cycleView = cycleStore?.cycleView;
const cycleLayout = cycleStore?.cycleLayout;
const totalCycles = projectCycles?.length ?? 0;
const totalCycles = currentProjectCycleIds?.length ?? 0;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
@ -103,17 +78,13 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
description:
"A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle.",
}}
primaryButton={
isEditingAllowed
? {
icon: <Plus className="h-4 w-4" />,
text: "Set your first cycle",
onClick: () => {
setCreateModal(true);
},
}
: null
}
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "Set your first cycle",
onClick: () => {
setCreateModal(true);
},
}}
disabled={!isEditingAllowed}
/>
</div>
@ -121,11 +92,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
<Tab.Group
as="div"
className="flex h-full flex-col overflow-hidden"
defaultIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleStore?.cycleView)}
selectedIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleStore?.cycleView)}
onChange={(i) => {
handleCurrentView(CYCLE_TAB_LIST[i].key as TCycleView);
}}
defaultIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleTab)}
selectedIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleTab)}
onChange={(i) => handleCurrentView(CYCLE_TAB_LIST[i]?.key ?? "active")}
>
<div className="flex flex-col items-end justify-between gap-4 border-b border-custom-border-200 px-4 pb-4 sm:flex-row sm:items-center sm:px-5 sm:pb-0">
<Tab.List as="div" className="flex items-center overflow-x-scroll">
@ -142,26 +111,24 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
</Tab>
))}
</Tab.List>
{cycleStore?.cycleView != "active" && (
{cycleTab !== "active" && (
<div className="flex items-center gap-1 rounded bg-custom-background-80 p-1">
{CYCLE_VIEW_LAYOUTS.map((layout) => {
if (layout.key === "gantt" && cycleStore?.cycleView === "draft") return null;
if (layout.key === "gantt" && cycleTab === "draft") return null;
return (
<Tooltip key={layout.key} tooltipContent={layout.title}>
<button
type="button"
className={`group grid h-[22px] w-7 place-items-center overflow-hidden rounded transition-all hover:bg-custom-background-100 ${
cycleStore?.cycleLayout == layout.key
? "bg-custom-background-100 shadow-custom-shadow-2xs"
: ""
cycleLayout == layout.key ? "bg-custom-background-100 shadow-custom-shadow-2xs" : ""
}`}
onClick={() => handleCurrentLayout(layout.key as TCycleLayout)}
>
<layout.icon
strokeWidth={2}
className={`h-3.5 w-3.5 ${
cycleStore?.cycleLayout == layout.key ? "text-custom-text-100" : "text-custom-text-200"
cycleLayout == layout.key ? "text-custom-text-100" : "text-custom-text-200"
}`}
/>
</button>
@ -174,10 +141,10 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
<Tab.Panels as={Fragment}>
<Tab.Panel as="div" className="h-full overflow-y-auto">
{cycleView && cycleLayout && (
{cycleTab && cycleLayout && (
<CyclesView
filter={"all"}
layout={cycleLayout as TCycleLayout}
filter="all"
layout={cycleLayout}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
peekCycle={peekCycle?.toString()}
@ -190,9 +157,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
</Tab.Panel>
<Tab.Panel as="div" className="h-full overflow-y-auto">
{cycleView && cycleLayout && (
{cycleTab && cycleLayout && (
<CyclesView
filter={"upcoming"}
filter="upcoming"
layout={cycleLayout as TCycleLayout}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
@ -202,9 +169,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
</Tab.Panel>
<Tab.Panel as="div" className="h-full overflow-y-auto">
{cycleView && cycleLayout && workspaceSlug && projectId && (
{cycleTab && cycleLayout && workspaceSlug && projectId && (
<CyclesView
filter={"completed"}
filter="completed"
layout={cycleLayout as TCycleLayout}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
@ -214,9 +181,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
</Tab.Panel>
<Tab.Panel as="div" className="h-full overflow-y-auto">
{cycleView && cycleLayout && workspaceSlug && projectId && (
{cycleTab && cycleLayout && workspaceSlug && projectId && (
<CyclesView
filter={"draft"}
filter="draft"
layout={cycleLayout as TCycleLayout}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}

View file

@ -1,16 +1,13 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import { X, PenSquare } from "lucide-react";
// layouts
import { AppLayout } from "layouts/app-layout";
// contexts
import { IssueViewContextProvider } from "contexts/issue-view.context";
// ui
import { ProjectDraftIssueHeader } from "components/headers";
// icons
import { X, PenSquare } from "lucide-react";
// types
import { NextPageWithLayout } from "types/app";
// components
import { DraftIssueLayoutRoot } from "components/issues/issue-layouts/roots/draft-issue-layout-root";
import { ProjectDraftIssueHeader } from "components/headers";
// types
import { NextPageWithLayout } from "lib/types";
const ProjectDraftIssuesPage: NextPageWithLayout = () => {
const router = useRouter();
@ -36,11 +33,9 @@ const ProjectDraftIssuesPage: NextPageWithLayout = () => {
ProjectDraftIssuesPage.getLayout = function getLayout(page: ReactElement) {
return (
<IssueViewContextProvider>
<AppLayout header={<ProjectDraftIssueHeader />} withProjectWrapper>
{page}
</AppLayout>
</IssueViewContextProvider>
<AppLayout header={<ProjectDraftIssueHeader />} withProjectWrapper>
{page}
</AppLayout>
);
};

View file

@ -2,25 +2,25 @@ import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useInboxFilters } from "hooks/store/";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { InboxActionsHeader, InboxMainContent, InboxIssuesListSidebar } from "components/inbox";
import { ProjectInboxHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectInboxPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId, inboxId } = router.query;
const { inboxFilters: inboxFiltersStore } = useMobxStore();
const { fetchInboxFilters } = useInboxFilters();
useSWR(
workspaceSlug && projectId && inboxId ? `INBOX_FILTERS_${inboxId.toString()}` : null,
workspaceSlug && projectId && inboxId
? () => inboxFiltersStore.fetchInboxFilters(workspaceSlug.toString(), projectId.toString(), inboxId.toString())
? () => fetchInboxFilters(workspaceSlug.toString(), projectId.toString(), inboxId.toString())
: null
);

View file

@ -15,31 +15,39 @@ import { Loader } from "@plane/ui";
// images
import emptyIssue from "public/empty-state/issue.svg";
// types
import { IIssue } from "types";
import { NextPageWithLayout } from "types/app";
import { TIssue } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// fetch-keys
import { PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS } from "constants/fetch-keys";
import { observer } from "mobx-react-lite";
import { useIssueDetail } from "hooks/store";
const defaultValues: Partial<IIssue> = {
description: "",
const defaultValues: Partial<TIssue> = {
// description: "",
description_html: "",
estimate_point: null,
issue_cycle: null,
issue_module: null,
name: "",
priority: "low",
start_date: null,
state: "",
target_date: null,
start_date: undefined,
state_id: "",
target_date: undefined,
};
// services
const issueService = new IssueService();
const IssueDetailsPage: NextPageWithLayout = () => {
const IssueDetailsPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const { workspaceSlug, projectId, issueId: routeIssueId } = router.query;
const { issueId, fetchIssue } = useIssueDetail();
useEffect(() => {
if (!workspaceSlug || !projectId || !routeIssueId) return;
fetchIssue(workspaceSlug as string, projectId as string, routeIssueId as string);
}, [workspaceSlug, projectId, routeIssueId, fetchIssue]);
const {
data: issueDetails,
@ -52,15 +60,15 @@ const IssueDetailsPage: NextPageWithLayout = () => {
: null
);
const { reset, control, watch } = useForm<IIssue>({
const { reset, control, watch } = useForm<TIssue>({
defaultValues,
});
const submitChanges = useCallback(
async (formData: Partial<IIssue>) => {
async (formData: Partial<TIssue>) => {
if (!workspaceSlug || !projectId || !issueId) return;
mutate<IIssue>(
mutate<TIssue>(
ISSUE_DETAILS(issueId as string),
(prevData) => {
if (!prevData) return prevData;
@ -73,7 +81,7 @@ const IssueDetailsPage: NextPageWithLayout = () => {
false
);
const payload: Partial<IIssue> = {
const payload: Partial<TIssue> = {
...formData,
};
@ -115,7 +123,7 @@ const IssueDetailsPage: NextPageWithLayout = () => {
onClick: () => router.push(`/${workspaceSlug}/projects/${projectId}/issues`),
}}
/>
) : issueDetails && projectId ? (
) : issueDetails && projectId && issueId ? (
<div className="flex h-full overflow-hidden">
<div className="h-full w-2/3 space-y-5 divide-y-2 divide-custom-border-300 overflow-y-auto p-5">
<IssueMainContent issueDetails={issueDetails} submitChanges={submitChanges} />
@ -147,7 +155,7 @@ const IssueDetailsPage: NextPageWithLayout = () => {
)}
</>
);
};
});
IssueDetailsPage.getLayout = function getLayout(page: ReactElement) {
return (

View file

@ -3,7 +3,7 @@ import { ReactElement } from "react";
import { ProjectLayoutRoot } from "components/issues";
import { ProjectIssuesHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// layouts
import { AppLayout } from "layouts/app-layout";

View file

@ -1,9 +1,8 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useModule } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// layouts
import { AppLayout } from "layouts/app-layout";
@ -16,14 +15,14 @@ import { EmptyState } from "components/common";
// assets
import emptyModule from "public/empty-state/module.svg";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ModuleIssuesPage: NextPageWithLayout = () => {
// router
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
// store
const { module: moduleStore } = useMobxStore();
// store hooks
const { fetchModuleDetails } = useModule();
// local storage
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
@ -31,7 +30,7 @@ const ModuleIssuesPage: NextPageWithLayout = () => {
const { error } = useSWR(
workspaceSlug && projectId && moduleId ? `CURRENT_MODULE_DETAILS_${moduleId.toString()}` : null,
workspaceSlug && projectId && moduleId
? () => moduleStore.fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
? () => fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
: null
);

View file

@ -5,7 +5,7 @@ import { AppLayout } from "layouts/app-layout";
import { ModulesListView } from "components/modules";
import { ModulesListHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectModulesPage: NextPageWithLayout = () => <ModulesListView />;

View file

@ -1,18 +1,24 @@
import React, { useEffect, useRef, useState, ReactElement, useCallback } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR, { MutatorOptions } from "swr";
import { Controller, useForm } from "react-hook-form";
import { Sparkle } from "lucide-react";
import debounce from "lodash/debounce";
// hooks
import { useApplication, useIssues, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
import useReloadConfirmations from "hooks/use-reload-confirmation";
// services
import { PageService } from "services/page.service";
import { FileService } from "services/file.service";
// hooks
import useUser from "hooks/use-user";
import debounce from "lodash/debounce";
import { useMobxStore } from "lib/mobx/store-provider";
import { IssueService } from "services/issue";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { GptAssistantPopover } from "components/core";
import { PageDetailsHeader } from "components/headers/page-details";
import { IssuePeekOverview } from "components/issues/peek-overview";
import { EmptyState } from "components/common";
// ui
import { DocumentEditorWithRef, DocumentReadOnlyEditorWithRef } from "@plane/document-editor";
@ -22,18 +28,13 @@ import emptyPage from "public/empty-state/page.svg";
// helpers
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
// types
import { NextPageWithLayout } from "types/app";
import { IPage, IIssue } from "types";
import { NextPageWithLayout } from "lib/types";
import { IPage, TIssue } from "@plane/types";
// fetch-keys
import { PAGE_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
import { IssuePeekOverview } from "components/issues/peek-overview";
import { IssueService } from "services/issue";
import useToast from "hooks/use-toast";
import useReloadConfirmations from "hooks/use-reload-confirmation";
import { EUserWorkspaceRoles } from "constants/workspace";
import { GptAssistantPopover } from "components/core";
import { Sparkle } from "lucide-react";
import { observer } from "mobx-react-lite";
// constants
import { EUserProjectRoles } from "constants/project";
import { EIssuesStoreType } from "constants/issue";
// services
const fileService = new FileService();
@ -41,23 +42,29 @@ const pageService = new PageService();
const issueService = new IssueService();
const PageDetailsPage: NextPageWithLayout = observer(() => {
const {
projectIssues: { updateIssue },
appConfig: { envConfig },
user: { currentProjectRole },
} = useMobxStore();
const editorRef = useRef<any>(null);
// states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
const [gptModalOpen, setGptModal] = useState(false);
const { setShowAlert } = useReloadConfirmations();
// refs
const editorRef = useRef<any>(null);
// router
const router = useRouter();
const { workspaceSlug, projectId, pageId, peekIssueId } = router.query;
// store hooks
const {
issues: { updateIssue },
} = useIssues(EIssuesStoreType.PROJECT);
const {
config: { envConfig },
} = useApplication();
const {
currentUser,
membership: { currentProjectRole },
} = useUser();
// toast alert
const { setToastAlert } = useToast();
const { user } = useUser();
const { setShowAlert } = useReloadConfirmations();
const { handleSubmit, setValue, watch, getValues, control, reset } = useForm<IPage>({
defaultValues: { name: "", description_html: "" },
@ -101,15 +108,15 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
}
);
const handleUpdateIssue = (issueId: string, data: Partial<IIssue>) => {
if (!workspaceSlug || !projectId || !user) return;
const handleUpdateIssue = (issueId: string, data: Partial<TIssue>) => {
if (!workspaceSlug || !projectId || !currentUser) return;
updateIssue(workspaceSlug.toString(), projectId.toString(), issueId, data);
};
const fetchIssue = async (issueId: string) => {
const issue = await issueService.retrieve(workspaceSlug as string, projectId as string, issueId as string);
return issue as IIssue;
return issue as TIssue;
};
const issueWidgetClickAction = (issueId: string) => {
@ -376,15 +383,15 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
const isPageReadOnly =
pageDetails?.is_locked ||
pageDetails?.archived_at ||
(currentProjectRole && [EUserWorkspaceRoles.VIEWER, EUserWorkspaceRoles.GUEST].includes(currentProjectRole));
(currentProjectRole && [EUserProjectRoles.VIEWER, EUserProjectRoles.GUEST].includes(currentProjectRole));
const isCurrentUserOwner = pageDetails?.owned_by === user?.id;
const isCurrentUserOwner = pageDetails?.owned_by === currentUser?.id;
const userCanDuplicate =
currentProjectRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentProjectRole);
const userCanArchive = isCurrentUserOwner || currentProjectRole === EUserWorkspaceRoles.ADMIN;
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const userCanArchive = isCurrentUserOwner || currentProjectRole === EUserProjectRoles.ADMIN;
const userCanLock =
currentProjectRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentProjectRole);
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
return (
<>
@ -521,7 +528,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
projectId={projectId as string}
issueId={peekIssueId ? (peekIssueId as string) : ""}
isArchived={false}
handleIssue={async (issueToUpdate, action) => {
handleIssue={(issueToUpdate) => {
if (peekIssueId && typeof peekIssueId === "string") {
handleUpdateIssue(peekIssueId, issueToUpdate);
}

View file

@ -5,16 +5,16 @@ import { Tab } from "@headlessui/react";
import useSWR from "swr";
import { observer } from "mobx-react-lite";
// hooks
import { usePage, useUser } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
import useUserAuth from "hooks/use-user-auth";
import { useMobxStore } from "lib/mobx/store-provider";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
import { PagesHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { PAGE_TABS_LIST } from "constants/page";
@ -39,27 +39,27 @@ const SharedPagesList = dynamic<any>(() => import("components/pages").then((a) =
});
const ProjectPagesPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
// store
const {
page: { fetchPages, fetchArchivedPages },
} = useMobxStore();
const { fetchProjectPages, fetchArchivedProjectPages } = usePage();
const { currentUser, currentUserLoader } = useUser();
// hooks
const {} = useUserAuth();
const {} = useUserAuth({ user: currentUser, isLoading: currentUserLoader });
// local storage
const { storedValue: pageTab, setValue: setPageTab } = useLocalStorage("pageTab", "Recent");
// fetching pages from API
useSWR(
workspaceSlug && projectId ? `ALL_PAGES_LIST_${projectId}` : null,
workspaceSlug && projectId ? () => fetchPages(workspaceSlug.toString(), projectId.toString()) : null
workspaceSlug && projectId ? () => fetchProjectPages(workspaceSlug.toString(), projectId.toString()) : null
);
// fetching archived pages from API
useSWR(
workspaceSlug && projectId ? `ALL_ARCHIVED_PAGES_LIST_${projectId}` : null,
workspaceSlug && projectId ? () => fetchArchivedPages(workspaceSlug.toString(), projectId.toString()) : null
workspaceSlug && projectId ? () => fetchArchivedProjectPages(workspaceSlug.toString(), projectId.toString()) : null
);
const currentTabValue = (tab: string | null) => {

View file

@ -1,8 +1,8 @@
import React, { ReactElement } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useProject, useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { ProjectSettingLayout } from "layouts/settings-layout";
@ -12,21 +12,22 @@ import useToast from "hooks/use-toast";
import { AutoArchiveAutomation, AutoCloseAutomation } from "components/automation";
import { ProjectSettingHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { IProject } from "types";
import { EUserWorkspaceRoles } from "constants/workspace";
import { NextPageWithLayout } from "lib/types";
import { IProject } from "@plane/types";
// constants
import { EUserProjectRoles } from "constants/project";
const AutomationSettingsPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// toast alert
const { setToastAlert } = useToast();
// store
// store hooks
const {
user: { currentProjectRole },
project: { currentProjectDetails: projectDetails, updateProject },
} = useMobxStore();
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails: projectDetails, updateProject } = useProject();
const handleChange = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return;
@ -40,7 +41,7 @@ const AutomationSettingsPage: NextPageWithLayout = observer(() => {
});
};
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
return (
<section className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>

View file

@ -1,4 +1,7 @@
import { ReactElement } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { ProjectSettingLayout } from "layouts/settings-layout";
@ -6,17 +9,16 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
import { ProjectSettingHeader } from "components/headers";
import { EstimatesList } from "components/estimates";
// types
import { NextPageWithLayout } from "types/app";
import { useMobxStore } from "lib/mobx/store-provider";
import { EUserWorkspaceRoles } from "constants/workspace";
import { observer } from "mobx-react-lite";
import { NextPageWithLayout } from "lib/types";
// constants
import { EUserProjectRoles } from "constants/project";
const EstimatesSettingsPage: NextPageWithLayout = observer(() => {
const {
user: { currentProjectRole },
} = useMobxStore();
membership: { currentProjectRole },
} = useUser();
const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN;
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
return (
<div className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "pointer-events-none opacity-60"}`}>

View file

@ -1,8 +1,8 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { ProjectSettingLayout } from "layouts/settings-layout";
@ -10,15 +10,15 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
import { ProjectSettingHeader } from "components/headers";
import { ProjectFeaturesList } from "components/project";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const FeaturesSettingsPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
const {
user: { fetchUserProjectInfo },
} = useMobxStore();
membership: { fetchUserProjectInfo },
} = useUser();
const { data: memberDetails } = useSWR(
workspaceSlug && projectId ? `PROJECT_MEMBERS_ME_${workspaceSlug}_${projectId}` : null,

View file

@ -1,6 +1,9 @@
import { useState, ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
import { observer } from "mobx-react-lite";
// hooks
import { useProject } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { ProjectSettingLayout } from "layouts/settings-layout";
@ -13,26 +16,21 @@ import {
ProjectDetailsFormLoader,
} from "components/project";
// types
import { NextPageWithLayout } from "types/app";
// fetch-keys
import { useMobxStore } from "lib/mobx/store-provider";
import { observer } from "mobx-react-lite";
import { NextPageWithLayout } from "lib/types";
const GeneralSettingsPage: NextPageWithLayout = observer(() => {
// store
const { project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// states
const [selectProject, setSelectedProject] = useState<string | null>(null);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const { currentProjectDetails, fetchProjectDetails } = useProject();
// api call to fetch project details
// TODO: removed this API if not necessary
useSWR(
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
workspaceSlug && projectId
? () => projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString())
: null
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null
);
// const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);

View file

@ -16,8 +16,8 @@ import { Loader } from "@plane/ui";
// images
import emptyIntegration from "public/empty-state/integration.svg";
// types
import { IProject } from "types";
import { NextPageWithLayout } from "types/app";
import { IProject } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// fetch-keys
import { PROJECT_DETAILS, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";

View file

@ -6,7 +6,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
import { ProjectSettingsLabelList } from "components/labels";
import { ProjectSettingHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const LabelsSettingsPage: NextPageWithLayout = () => (
<div className="w-full gap-10 overflow-y-auto py-8 pr-9">

View file

@ -6,7 +6,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
import { ProjectSettingHeader } from "components/headers";
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "components/project";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const MembersSettingsPage: NextPageWithLayout = () => (
<section className={`w-full overflow-y-auto py-8 pr-9`}>

View file

@ -6,7 +6,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
import { ProjectSettingStateList } from "components/states";
import { ProjectSettingHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const StatesSettingsPage: NextPageWithLayout = () => (
<div className="w-full gap-10 overflow-y-auto py-8 pr-9">

View file

@ -1,8 +1,8 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useProjectView } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
@ -13,18 +13,19 @@ import { EmptyState } from "components/common";
// assets
import emptyView from "public/empty-state/view.svg";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectViewIssuesPage: NextPageWithLayout = () => {
// router
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query;
const { projectViews: projectViewsStore } = useMobxStore();
// store hooks
const { fetchViewDetails } = useProjectView();
const { error } = useSWR(
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
workspaceSlug && projectId && viewId
? () => projectViewsStore.fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString())
? () => fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString())
: null
);

View file

@ -1,31 +1,13 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { ProjectViewsHeader } from "components/headers";
import { ProjectViewsList } from "components/views";
// layouts
import { AppLayout } from "layouts/app-layout";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectViewsPage: NextPageWithLayout = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
const {
projectViews: { fetchAllViews },
} = useMobxStore();
useSWR(
workspaceSlug && projectId ? `PROJECT_VIEWS_LIST_${workspaceSlug.toString()}_${projectId.toString()}` : null,
workspaceSlug && projectId ? () => fetchAllViews(workspaceSlug.toString(), projectId.toString()) : null
);
return <ProjectViewsList />;
};
const ProjectViewsPage: NextPageWithLayout = () => <ProjectViewsList />;
ProjectViewsPage.getLayout = function getLayout(page: ReactElement) {
return (

View file

@ -1,20 +1,13 @@
import { ReactElement } from "react";
import { useRouter } from "next/router";
// components
import { ProjectCardList } from "components/project";
import { ProjectsHeader } from "components/headers";
// layouts
import { AppLayout } from "layouts/app-layout";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProjectsPage: NextPageWithLayout = () => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
return <>{workspaceSlug && <ProjectCardList workspaceSlug={workspaceSlug.toString()} />}</>;
};
const ProjectsPage: NextPageWithLayout = () => <ProjectCardList />;
ProjectsPage.getLayout = function getLayout(page: ReactElement) {
return <AppLayout header={<ProjectsHeader />}>{page}</AppLayout>;

View file

@ -2,8 +2,8 @@ import React, { useState } 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";
// store hooks
import { useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
@ -15,7 +15,7 @@ import { Button, Spinner } from "@plane/ui";
// services
import { APITokenService } from "services/api_token.service";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { API_TOKENS_LIST } from "constants/fetch-keys";
import { EUserWorkspaceRoles } from "constants/workspace";
@ -28,10 +28,10 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// mobx store
// store hooks
const {
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;

View file

@ -1,6 +1,6 @@
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
@ -9,14 +9,15 @@ import { WorkspaceSettingHeader } from "components/headers";
// ui
import { Button } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
const BillingSettingsPage: NextPageWithLayout = observer(() => {
// store hooks
const {
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;

View file

@ -1,6 +1,6 @@
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// layout
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
@ -8,14 +8,15 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
import { WorkspaceSettingHeader } from "components/headers";
import ExportGuide from "components/exporter/guide";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
const ExportsPage: NextPageWithLayout = observer(() => {
// store hooks
const {
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const hasPageAccess =
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);

View file

@ -1,6 +1,6 @@
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// layouts
import { WorkspaceSettingLayout } from "layouts/settings-layout";
import { AppLayout } from "layouts/app-layout";
@ -8,14 +8,15 @@ import { AppLayout } from "layouts/app-layout";
import IntegrationGuide from "components/integration/guide";
import { WorkspaceSettingHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
const ImportsPage: NextPageWithLayout = observer(() => {
// store hooks
const {
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;

View file

@ -6,7 +6,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
import { WorkspaceSettingHeader } from "components/headers";
import { WorkspaceDetails } from "components/workspace";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const WorkspaceSettingsPage: NextPageWithLayout = () => <WorkspaceDetails />;

View file

@ -2,8 +2,8 @@ import { ReactElement } 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";
// hooks
import { useUser } from "hooks/store";
// services
import { IntegrationService } from "services/integrations";
// layouts
@ -16,7 +16,7 @@ import { WorkspaceSettingHeader } from "components/headers";
import { IntegrationAndImportExportBanner } from "components/ui";
import { Loader } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// fetch-keys
import { APP_INTEGRATIONS } from "constants/fetch-keys";
// constants
@ -28,10 +28,10 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// mobx store
// store hooks
const {
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;

View file

@ -2,9 +2,8 @@ import { useState, ReactElement } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Search } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useMember, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// layouts
import { AppLayout } from "layouts/app-layout";
@ -15,33 +14,38 @@ import { SendWorkspaceInvitationModal, WorkspaceMembersList } from "components/w
// ui
import { Button } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { IWorkspaceBulkInviteFormData } from "types";
import { NextPageWithLayout } from "lib/types";
import { IWorkspaceBulkInviteFormData } from "@plane/types";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const {
user: { currentWorkspaceRole },
workspaceMember: { inviteMembersToWorkspace },
trackEvent: { postHogEventTracker, setTrackElement },
} = useMobxStore();
// states
const [inviteModal, setInviteModal] = useState(false);
const [searchQuery, setSearchQuery] = useState<string>("");
// hooks
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// store hooks
const {
eventTracker: { postHogEventTracker, setTrackElement },
} = useApplication();
const {
membership: { currentWorkspaceRole },
} = useUser();
const {
workspace: { inviteMembersToWorkspace },
} = useMember();
// toast alert
const { setToastAlert } = useToast();
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
if (!workspaceSlug) return;
return inviteMembersToWorkspace(workspaceSlug.toString(), data)
.then(async (res) => {
.then(() => {
setInviteModal(false);
postHogEventTracker("MEMBER_INVITED", { ...res, state: "SUCCESS" });
postHogEventTracker("MEMBER_INVITED", { state: "SUCCESS" });
setToastAlert({
type: "success",
title: "Success!",

View file

@ -1,9 +1,9 @@
import { useState } from "react";
import { useState, useEffect } 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";
// hooks
import { useUser, useWebhook } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
@ -15,8 +15,8 @@ import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "component
// ui
import { Spinner } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { IWebhook } from "types";
import { NextPageWithLayout } from "lib/types";
import { IWebhook } from "@plane/types";
const WebhookDetailsPage: NextPageWithLayout = observer(() => {
// states
@ -26,12 +26,17 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => {
const { workspaceSlug, webhookId } = router.query;
// mobx store
const {
webhook: { currentWebhook, fetchWebhookById, updateWebhook },
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const { currentWebhook, clearSecretKey, fetchWebhookById, updateWebhook } = useWebhook();
// toast
const { setToastAlert } = useToast();
// TODO: fix this error
// useEffect(() => {
// if (isCreated !== "true") clearSecretKey();
// }, [clearSecretKey, isCreated]);
const isAdmin = currentWorkspaceRole === 20;
useSWR(

View file

@ -0,0 +1,108 @@
import React from "react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// hooks
import { useUser, useWebhook, useWorkspace } from "hooks/store";
import useToast from "hooks/use-toast";
// layouts
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
// components
import { WorkspaceSettingHeader } from "components/headers";
import { WebhookForm, getCurrentHookAsCSV } from "components/web-hooks";
// types
import { NextPageWithLayout } from "lib/types";
import { IWebhook } from "@plane/types";
// helpers
import { csvDownload } from "helpers/download.helper";
const CreateWebhookPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;
const {
membership: { currentWorkspaceRole },
} = useUser();
const { createWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const { setToastAlert } = useToast();
const isAdmin = currentWorkspaceRole === 20;
const handleCreateWebhook = async (formData: IWebhook, webhookEventType: string) => {
if (!workspaceSlug) return;
let payload: Partial<IWebhook> = {
url: formData.url,
};
if (webhookEventType === "all")
payload = {
...payload,
project: true,
cycle: true,
module: true,
issue: true,
issue_comment: true,
};
else
payload = {
...payload,
project: formData.project ?? false,
cycle: formData.cycle ?? false,
module: formData.module ?? false,
issue: formData.issue ?? false,
issue_comment: formData.issue_comment ?? false,
};
await createWebhook(workspaceSlug.toString(), payload)
.then(({ webHook, secretKey }) => {
setToastAlert({
type: "success",
title: "Success!",
message: "Webhook created successfully.",
});
const csvData = getCurrentHookAsCSV(currentWorkspace, webHook, secretKey?.toString() ?? "");
csvDownload(csvData, `webhook-secret-key-${Date.now()}`);
if (webHook && webHook.id)
router.push({ pathname: `/${workspaceSlug}/settings/webhooks/${webHook.id}`, query: { isCreated: true } });
})
.catch((error) => {
setToastAlert({
type: "error",
title: "Error!",
message: error?.error ?? "Something went wrong. Please try again.",
});
});
};
const handleFormSubmit = async (formData: IWebhook, webhookEventType: string) => {
await handleCreateWebhook(formData, webhookEventType);
};
if (!isAdmin)
return (
<div className="mt-10 flex h-full w-full justify-center p-4">
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
</div>
);
return (
<div className="w-full overflow-y-auto py-8 pl-1 pr-9">
<WebhookForm onSubmit={handleFormSubmit} />
</div>
);
});
CreateWebhookPage.getLayout = function getLayout(page: React.ReactElement) {
return (
<AppLayout header={<WorkspaceSettingHeader title="Webhook settings" />}>
<WorkspaceSettingLayout>{page}</WorkspaceSettingLayout>
</AppLayout>
);
};
export default CreateWebhookPage;

View file

@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser, useWebhook, useWorkspace } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { WorkspaceSettingLayout } from "layouts/settings-layout";
@ -13,7 +13,7 @@ import { WebhooksList, WebhooksEmptyState, CreateWebhookModal } from "components
// ui
import { Button, Spinner } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const WebhooksListPage: NextPageWithLayout = observer(() => {
// states
@ -23,10 +23,10 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
const { workspaceSlug } = router.query;
const {
webhook: { fetchWebhooks, createWebhook, clearSecretKey, webhooks, webhookSecretKey },
workspace: { currentWorkspace },
user: { currentWorkspaceRole },
} = useMobxStore();
membership: { currentWorkspaceRole },
} = useUser();
const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
const isAdmin = currentWorkspaceRole === 20;

View file

@ -6,7 +6,7 @@ import { GlobalViewsHeader } from "components/workspace";
import { AllIssueLayoutRoot } from "components/issues";
import { GlobalIssuesHeader } from "components/headers";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const GlobalViewIssuesPage: NextPageWithLayout = () => (
<div className="h-full overflow-hidden bg-custom-background-100">

View file

@ -6,7 +6,7 @@ import { AllIssueLayoutRoot } from "components/issues/issue-layouts";
// layouts
import { AppLayout } from "layouts/app-layout";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const GlobalViewAllIssuesPage: NextPageWithLayout = () => (
<div className="h-full overflow-hidden bg-custom-background-100">

View file

@ -6,7 +6,7 @@ import { AllIssueLayoutRoot } from "components/issues/issue-layouts";
// layouts
import { AppLayout } from "layouts/app-layout";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const GlobalViewAssignedIssuesPage: NextPageWithLayout = () => (
<div className="h-full overflow-hidden bg-custom-background-100">

View file

@ -6,7 +6,7 @@ import { AllIssueLayoutRoot } from "components/issues";
// layouts
import { AppLayout } from "layouts/app-layout";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const GlobalViewCreatedIssuesPage: NextPageWithLayout = () => (
<div className="h-full overflow-hidden bg-custom-background-100">

View file

@ -9,7 +9,7 @@ import { Input } from "@plane/ui";
// icons
import { Search } from "lucide-react";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace";

View file

@ -6,7 +6,7 @@ import { GlobalViewsHeader } from "components/workspace";
import { GlobalIssuesHeader } from "components/headers";
import { AllIssueLayoutRoot } from "components/issues";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const GlobalViewSubscribedIssuesPage: NextPageWithLayout = () => (
<div className="h-full overflow-hidden bg-custom-background-100">

View file

@ -9,10 +9,11 @@ import "styles/react-datepicker.css";
// constants
import { SITE_TITLE } from "constants/seo-variables";
// mobx store provider
import { MobxStoreProvider } from "lib/mobx/store-provider";
import { StoreProvider } from "contexts/store-context";
import { AppProvider } from "lib/app-provider";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
@ -27,9 +28,9 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<Head>
<title>{SITE_TITLE}</title>
</Head>
<MobxStoreProvider {...pageProps}>
<StoreProvider {...pageProps}>
<AppProvider>{getLayout(<Component {...pageProps} />)}</AppProvider>
</MobxStoreProvider>
</StoreProvider>
</>
);
}

View file

@ -20,7 +20,7 @@ import latestFeatures from "public/onboarding/onboarding-pages.svg";
// helpers
import { checkEmailValidity } from "helpers/string.helper";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
type TResetPasswordFormValues = {
email: string;

View file

@ -1,11 +1,13 @@
import React, { useEffect, ReactElement } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// next-themes
import { useTheme } from "next-themes";
// services
import { AuthService } from "services/auth.service";
// hooks
import { useUser } from "hooks/store";
import useUserAuth from "hooks/use-user-auth";
import useToast from "hooks/use-toast";
// layouts
@ -15,7 +17,7 @@ import { EmailSignUpForm } from "components/account";
// images
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
type EmailPasswordFormValues = {
email: string;
@ -26,14 +28,17 @@ type EmailPasswordFormValues = {
// services
const authService = new AuthService();
const SignUpPage: NextPageWithLayout = () => {
const SignUpPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
// toast alert
const { setToastAlert } = useToast();
// next-themes
const { setTheme } = useTheme();
const { mutateUser } = useUserAuth("sign-in");
// store hooks
const { currentUser, fetchCurrentUser, currentUserLoader } = useUser();
// custom hooks
const {} = useUserAuth({ routeAuth: "sign-in", user: currentUser, isLoading: currentUserLoader });
const handleSignUp = async (formData: EmailPasswordFormValues) => {
const payload = {
@ -50,7 +55,7 @@ const SignUpPage: NextPageWithLayout = () => {
message: "Account created successfully.",
});
if (response) await mutateUser();
if (response) await fetchCurrentUser();
router.push("/onboarding");
})
.catch((err) =>
@ -84,7 +89,7 @@ const SignUpPage: NextPageWithLayout = () => {
</div>
</>
);
};
});
SignUpPage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;

View file

@ -3,8 +3,8 @@ import { useRouter } from "next/router";
import Image from "next/image";
import { useTheme } from "next-themes";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// layouts
import DefaultLayout from "layouts/default-layout";
import { UserAuthWrapper } from "layouts/auth-layout";
@ -14,16 +14,14 @@ import { CreateWorkspaceForm } from "components/workspace";
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
// types
import { IWorkspace } from "types";
import { NextPageWithLayout } from "types/app";
import { IWorkspace } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
const CreateWorkspacePage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
// store
const {
user: { currentUser, updateCurrentUser },
} = useMobxStore();
// store hooks
const { currentUser, updateCurrentUser } = useUser();
// states
const [defaultValues, setDefaultValues] = useState({
name: "",

View file

@ -4,9 +4,9 @@ import { observer } from "mobx-react-lite";
// layouts
import { InstanceAdminLayout } from "layouts/admin-layout";
// types
import { NextPageWithLayout } from "types/app";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { NextPageWithLayout } from "lib/types";
// hooks
import { useApplication } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
// icons
@ -18,7 +18,7 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
// store
const {
instance: { fetchInstanceConfigurations, formattedConfig },
} = useMobxStore();
} = useApplication();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());

View file

@ -5,9 +5,9 @@ import { observer } from "mobx-react-lite";
// layouts
import { InstanceAdminLayout } from "layouts/admin-layout";
// types
import { NextPageWithLayout } from "types/app";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { NextPageWithLayout } from "lib/types";
// hooks
import { useApplication } from "hooks/store";
// hooks
import useToast from "hooks/use-toast";
// ui
@ -19,7 +19,7 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
// store
const {
instance: { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations },
} = useMobxStore();
} = useApplication();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());

View file

@ -4,9 +4,9 @@ import { observer } from "mobx-react-lite";
// layouts
import { InstanceAdminLayout } from "layouts/admin-layout";
// types
import { NextPageWithLayout } from "types/app";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { NextPageWithLayout } from "lib/types";
// hooks
import { useApplication } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
// components
@ -16,7 +16,7 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
// store
const {
instance: { fetchInstanceConfigurations, formattedConfig },
} = useMobxStore();
} = useApplication();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());

View file

@ -4,9 +4,9 @@ import { observer } from "mobx-react-lite";
// layouts
import { InstanceAdminLayout } from "layouts/admin-layout";
// types
import { NextPageWithLayout } from "types/app";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { NextPageWithLayout } from "lib/types";
// hooks
import { useApplication } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
// components
@ -16,7 +16,7 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
// store
const {
instance: { fetchInstanceConfigurations, formattedConfig },
} = useMobxStore();
} = useApplication();
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());

View file

@ -4,19 +4,19 @@ import { observer } from "mobx-react-lite";
// layouts
import { InstanceAdminLayout } from "layouts/admin-layout";
// types
import { NextPageWithLayout } from "types/app";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { NextPageWithLayout } from "lib/types";
// hooks
import { useApplication } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
// components
import { InstanceGeneralForm } from "components/instance";
const InstanceAdminPage: NextPageWithLayout = observer(() => {
// store
// store hooks
const {
instance: { fetchInstanceInfo, instance, fetchInstanceAdmins, instanceAdmins },
} = useMobxStore();
} = useApplication();
useSWR("INSTANCE_INFO", () => fetchInstanceInfo());
useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins());

View file

@ -4,7 +4,7 @@ import DefaultLayout from "layouts/default-layout";
// components
import { SignInView } from "components/page-views";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const HomePage: NextPageWithLayout = () => <SignInView />;

View file

@ -5,7 +5,7 @@ import { AppInstallationService } from "services/app_installation.service";
// ui
import { Spinner } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// services
const appInstallationService = new AppInstallationService();

View file

@ -5,19 +5,18 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
import { useTheme } from "next-themes";
import { observer } from "mobx-react-lite";
import { CheckCircle2 } from "lucide-react";
// services
import { WorkspaceService } from "services/workspace.service";
import { UserService } from "services/user.service";
// hooks
import useUser from "hooks/use-user";
import { useApplication, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// layouts
import DefaultLayout from "layouts/default-layout";
import { UserAuthWrapper } from "layouts/auth-layout";
// ui
import { Button } from "@plane/ui";
// icons
import { CheckCircle2 } from "lucide-react";
// images
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
@ -25,44 +24,36 @@ import emptyInvitation from "public/empty-state/invitation.svg";
// helpers
import { truncateText } from "helpers/string.helper";
// types
import { NextPageWithLayout } from "types/app";
import type { IWorkspaceMemberInvitation } from "types";
import { NextPageWithLayout } from "lib/types";
import type { IWorkspaceMemberInvitation } from "@plane/types";
// constants
import { ROLE } from "constants/workspace";
// components
import { EmptyState } from "components/common";
// mobx-store
import { useMobxStore } from "lib/mobx/store-provider";
// services
const workspaceService = new WorkspaceService();
const userService = new UserService();
const UserInvitationsPage: NextPageWithLayout = observer(() => {
// states
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
const [isJoiningWorkspaces, setIsJoiningWorkspaces] = useState(false);
// store
// store hooks
const {
workspace: { workspaceSlug },
user: { currentUserSettings },
trackEvent: { postHogEventTracker },
} = useMobxStore();
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentUser, currentUserSettings } = useUser();
// router
const router = useRouter();
// next-themes
const { theme } = useTheme();
const { user } = useUser();
// toast alert
const { setToastAlert } = useToast();
const { data: invitations } = useSWR<IWorkspaceMemberInvitation[]>("USER_WORKSPACE_INVITATIONS", () =>
workspaceService.userWorkspaceInvitations()
);
const { data: invitations } = useSWR("USER_WORKSPACE_INVITATIONS", () => workspaceService.userWorkspaceInvitations());
const redirectWorkspaceSlug =
workspaceSlug ||
currentUserSettings?.workspace?.last_workspace_slug ||
currentUserSettings?.workspace?.fallback_workspace_slug ||
"";
@ -92,7 +83,7 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
.then((res) => {
mutate("USER_WORKSPACES");
const firstInviteId = invitationsRespond[0];
const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace;
const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace_detail;
postHogEventTracker("MEMBER_ACCEPTED", {
...res,
state: "SUCCESS",
@ -137,7 +128,7 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
</div>
</div>
<div className="absolute right-4 top-1/4 -translate-y-1/2 text-sm text-custom-text-100 sm:fixed sm:right-16 sm:top-12 sm:translate-y-0 sm:py-5">
{user?.email}
{currentUser?.email}
</div>
</div>
{invitations ? (
@ -162,23 +153,23 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
>
<div className="flex-shrink-0">
<div className="grid h-9 w-9 place-items-center rounded">
{invitation.workspace.logo && invitation.workspace.logo !== "" ? (
{invitation.workspace_detail.logo && invitation.workspace_detail.logo.trim() !== "" ? (
<img
src={invitation.workspace.logo}
src={invitation.workspace_detail.logo}
height="100%"
width="100%"
className="rounded"
alt={invitation.workspace.name}
alt={invitation.workspace_detail.name}
/>
) : (
<span className="grid h-9 w-9 place-items-center rounded bg-gray-700 px-3 py-1.5 uppercase text-white">
{invitation.workspace.name[0]}
{invitation.workspace_detail.name[0]}
</span>
)}
</div>
</div>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium">{truncateText(invitation.workspace.name, 30)}</div>
<div className="text-sm font-medium">{truncateText(invitation.workspace_detail.name, 30)}</div>
<p className="text-xs text-custom-text-200">{ROLE[invitation.role]}</p>
</div>
<span

View file

@ -7,8 +7,9 @@ import useSWR from "swr";
import { ChevronDown } from "lucide-react";
import { Menu, Transition } from "@headlessui/react";
import { Controller, useForm } from "react-hook-form";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useUser, useWorkspace } from "hooks/store";
import useUserAuth from "hooks/use-user-auth";
// services
import { WorkspaceService } from "services/workspace.service";
// layouts
@ -21,25 +22,29 @@ import { Avatar, Spinner } from "@plane/ui";
// images
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
// types
import { IUser, TOnboardingSteps } from "types";
import { NextPageWithLayout } from "types/app";
import { IUser, TOnboardingSteps } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
// services
const workspaceService = new WorkspaceService();
const OnboardingPage: NextPageWithLayout = observer(() => {
// states
const [step, setStep] = useState<number | null>(null);
const [showDeleteAccountModal, setShowDeleteAccountModal] = useState(false);
const {
user: { currentUser, updateCurrentUser, updateUserOnBoard },
workspace: workspaceStore,
trackEvent: { postHogEventTracker },
} = useMobxStore();
// router
const router = useRouter();
// store hooks
const {
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentUser, currentUserLoader, updateCurrentUser, updateUserOnBoard } = useUser();
const { workspaces, fetchWorkspaces } = useWorkspace();
// custom hooks
const {} = useUserAuth({ routeAuth: "onboarding", user: currentUser, isLoading: currentUserLoader });
const user = currentUser ?? undefined;
const workspaces = workspaceStore.workspaces;
const workspacesList = Object.values(workspaces ?? {});
const { setTheme } = useTheme();
@ -49,7 +54,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
},
});
useSWR(`USER_WORKSPACES_LIST`, () => workspaceStore.fetchWorkspaces(), {
useSWR(`USER_WORKSPACES_LIST`, () => fetchWorkspaces(), {
shouldRetryOnError: false,
});
@ -72,7 +77,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
};
// complete onboarding
const finishOnboarding = async () => {
if (!user || !workspaces) return;
if (!user || !workspacesList) return;
await updateUserOnBoard()
.then(() => {
@ -87,7 +92,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
console.log(error);
});
router.replace(`/${workspaces[0].slug}`);
router.replace(`/${workspacesList[0]?.slug}`);
};
useEffect(() => {
@ -100,14 +105,19 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
const onboardingStep = user.onboarding_step;
if (!onboardingStep.workspace_join && !onboardingStep.workspace_create && workspaces && workspaces?.length > 0) {
if (
!onboardingStep.workspace_join &&
!onboardingStep.workspace_create &&
workspacesList &&
workspacesList?.length > 0
) {
await updateCurrentUser({
onboarding_step: {
...user.onboarding_step,
workspace_join: true,
workspace_create: true,
},
last_workspace_id: workspaces[0].id,
last_workspace_id: workspacesList[0]?.id,
});
setStep(2);
return;
@ -128,7 +138,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
};
handleStepChange();
}, [user, invitations, step, updateCurrentUser, workspaces]);
}, [user, invitations, step, updateCurrentUser, workspacesList]);
return (
<>
@ -226,7 +236,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
finishOnboarding={finishOnboarding}
stepChange={stepChange}
user={user}
workspace={workspaces?.[0]}
workspace={workspacesList?.[0]}
/>
)}
</div>

View file

@ -17,7 +17,7 @@ import { USER_ACTIVITY } from "constants/fetch-keys";
// helper
import { calculateTimeAgo } from "helpers/date-time.helper";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const userService = new UserService();
@ -25,7 +25,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
const { data: userActivity } = useSWR(USER_ACTIVITY, () => userService.getUserActivity());
return (
<section className="mx-auto pt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
<section className="mx-auto mt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
<h3 className="text-xl font-medium">Activity</h3>
</div>
@ -180,7 +180,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
</ul>
</div>
) : (
<Loader className="space-y-5 mt-5">
<Loader className="space-y-5">
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />

View file

@ -2,8 +2,8 @@ import { ReactElement, useEffect, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Controller, useForm } from "react-hook-form";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
// services
import { UserService } from "services/user.service";
// hooks
@ -13,7 +13,7 @@ import { ProfileSettingsLayout } from "layouts/settings-layout";
// ui
import { Button, Input, Spinner } from "@plane/ui";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
interface FormValues {
old_password: string;
@ -32,9 +32,7 @@ const userService = new UserService();
const ChangePasswordPage: NextPageWithLayout = observer(() => {
const [isPageLoading, setIsPageLoading] = useState(true);
const {
user: { currentUser },
} = useMobxStore();
const { currentUser } = useUser();
const router = useRouter();
@ -90,7 +88,7 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => {
return (
<form
onSubmit={handleSubmit(handleChangePassword)}
className="mx-auto pt-16 flex h-full w-full flex-col gap-8 px-8 pb-8 lg:w-3/5"
className="mx-auto mt-16 flex h-full w-full flex-col gap-8 px-8 pb-8 lg:w-3/5"
>
<h3 className="text-xl font-medium">Change password</h3>
<div className="grid-col grid w-full grid-cols-1 items-center justify-between gap-10 xl:grid-cols-2 2xl:grid-cols-3">

View file

@ -1,10 +1,11 @@
import React, { useEffect, useState, ReactElement } from "react";
import { Controller, useForm } from "react-hook-form";
import { Disclosure, Transition } from "@headlessui/react";
import { observer } from "mobx-react-lite";
// services
import { FileService } from "services/file.service";
import { UserService } from "services/user.service";
// hooks
import { useUser } from "hooks/store";
import useUserAuth from "hooks/use-user-auth";
import useToast from "hooks/use-toast";
// layouts
@ -17,8 +18,8 @@ import { Button, CustomSelect, CustomSearchSelect, Input, Spinner } from "@plane
// icons
import { ChevronDown, User2 } from "lucide-react";
// types
import type { IUser } from "types";
import type { NextPageWithLayout } from "types/app";
import type { IUser } from "@plane/types";
import type { NextPageWithLayout } from "lib/types";
// constants
import { USER_ROLES } from "constants/workspace";
import { TIME_ZONES } from "constants/timezones";
@ -35,13 +36,12 @@ const defaultValues: Partial<IUser> = {
};
const fileService = new FileService();
const userService = new UserService();
const ProfileSettingsPage: NextPageWithLayout = () => {
const ProfileSettingsPage: NextPageWithLayout = observer(() => {
// states
const [isRemoving, setIsRemoving] = useState(false);
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
const [deactivateAccountModal, setDeactivateAccountModal] = useState(false);
// form info
const {
handleSubmit,
@ -50,9 +50,12 @@ const ProfileSettingsPage: NextPageWithLayout = () => {
control,
formState: { errors, isSubmitting },
} = useForm<IUser>({ defaultValues });
// toast alert
const { setToastAlert } = useToast();
const { user: myProfile, mutateUser } = useUserAuth();
// store hooks
const { currentUser: myProfile, updateCurrentUser, currentUserLoader } = useUser();
// custom hooks
const {} = useUserAuth({ user: myProfile, isLoading: currentUserLoader });
useEffect(() => {
reset({ ...defaultValues, ...myProfile });
@ -79,14 +82,8 @@ const ProfileSettingsPage: NextPageWithLayout = () => {
user_timezone: formData.user_timezone,
};
await userService
.updateUser(payload)
.then((res) => {
mutateUser((prevData: any) => {
if (!prevData) return prevData;
return { ...prevData, ...res };
}, false);
await updateCurrentUser(payload)
.then(() => {
setToastAlert({
type: "success",
title: "Success!",
@ -109,18 +106,13 @@ const ProfileSettingsPage: NextPageWithLayout = () => {
fileService.deleteUserFile(url).then(() => {
if (updateUser)
userService
.updateUser({ avatar: "" })
updateCurrentUser({ avatar: "" })
.then(() => {
setToastAlert({
type: "success",
title: "Success!",
message: "Profile picture removed successfully.",
});
mutateUser((prevData: any) => {
if (!prevData) return prevData;
return { ...prevData, avatar: "" };
}, false);
setIsRemoving(false);
})
.catch(() => {
@ -168,7 +160,7 @@ const ProfileSettingsPage: NextPageWithLayout = () => {
)}
/>
<DeactivateAccountModal isOpen={deactivateAccountModal} onClose={() => setDeactivateAccountModal(false)} />
<div className="mx-auto flex h-full w-full flex-col space-y-10 overflow-y-auto pt-16 px-8 pb-8 lg:w-3/5">
<div className="mx-auto mt-16 flex h-full w-full flex-col space-y-10 overflow-y-auto px-8 pb-8 lg:w-3/5">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex w-full flex-col gap-8">
<div className="relative h-44 w-full">
@ -431,7 +423,7 @@ const ProfileSettingsPage: NextPageWithLayout = () => {
</div>
</>
);
};
});
ProfileSettingsPage.getLayout = function getLayout(page: ReactElement) {
return <ProfileSettingsLayout>{page}</ProfileSettingsLayout>;

View file

@ -2,7 +2,7 @@ import { useEffect, useState, ReactElement } from "react";
import { observer } from "mobx-react-lite";
import { useTheme } from "next-themes";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// layouts
import { ProfileSettingsLayout } from "layouts/settings-layout";
@ -13,14 +13,13 @@ import { Spinner } from "@plane/ui";
// constants
import { I_THEME_OPTION, THEME_OPTIONS } from "constants/themes";
// type
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
const ProfilePreferencesPage: NextPageWithLayout = observer(() => {
const {
user: { currentUser, updateCurrentUserTheme },
} = useMobxStore();
// states
const [currentTheme, setCurrentTheme] = useState<I_THEME_OPTION | null>(null);
// store hooks
const { currentUser, updateCurrentUserTheme } = useUser();
// computed
const userTheme = currentUser?.theme;
// hooks
@ -49,7 +48,7 @@ const ProfilePreferencesPage: NextPageWithLayout = observer(() => {
return (
<>
{currentUser ? (
<div className="mx-auto pt-16 h-full w-full overflow-y-auto px-8 pb-8 lg:w-3/5">
<div className="mx-auto mt-16 h-full w-full overflow-y-auto px-8 pb-8 lg:w-3/5">
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
<h3 className="text-xl font-medium">Preferences</h3>
</div>

View file

@ -1,12 +1,12 @@
import React, { ReactElement } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// swr
import { Boxes, Check, Share2, Star, User2, X } from "lucide-react";
import { observer } from "mobx-react-lite";
// hooks
import { useUser } from "hooks/store";
// services
import { WorkspaceService } from "services/workspace.service";
// hooks
import useUser from "hooks/use-user";
// layouts
import DefaultLayout from "layouts/default-layout";
// ui
@ -14,19 +14,19 @@ import { Spinner } from "@plane/ui";
// icons
import { EmptySpace, EmptySpaceItem } from "components/ui/empty-space";
// types
import { NextPageWithLayout } from "types/app";
import { NextPageWithLayout } from "lib/types";
// constants
import { WORKSPACE_INVITATION } from "constants/fetch-keys";
// services
const workspaceService = new WorkspaceService();
const WorkspaceInvitationPage: NextPageWithLayout = () => {
const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
// router
const router = useRouter();
const { invitation_id, email, slug } = router.query;
const { user } = useUser();
// store hooks
const { currentUser } = useUser();
const { data: invitationDetail, error } = useSWR(
invitation_id && slug && WORKSPACE_INVITATION(invitation_id.toString()),
@ -38,12 +38,12 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
const handleAccept = () => {
if (!invitationDetail) return;
workspaceService
.joinWorkspace(invitationDetail.workspace.slug, invitationDetail.id, {
.joinWorkspace(invitationDetail.workspace_detail.slug, invitationDetail.id, {
accepted: true,
email: invitationDetail.email,
})
.then(() => {
if (email === user?.email) {
if (email === currentUser?.email) {
router.push("/invitations");
} else {
router.push("/");
@ -55,7 +55,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
const handleReject = () => {
if (!invitationDetail) return;
workspaceService
.joinWorkspace(invitationDetail.workspace.slug, invitationDetail.id, {
.joinWorkspace(invitationDetail.workspace_detail.slug, invitationDetail.id, {
accepted: false,
email: invitationDetail.email,
})
@ -78,7 +78,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
{invitationDetail.accepted ? (
<>
<EmptySpace
title={`You are already a member of ${invitationDetail.workspace.name}`}
title={`You are already a member of ${invitationDetail.workspace_detail.name}`}
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
>
<EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" action={() => router.push("/")} />
@ -86,7 +86,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
</>
) : (
<EmptySpace
title={`You have been invited to ${invitationDetail.workspace.name}`}
title={`You have been invited to ${invitationDetail.workspace_detail.name}`}
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
>
<EmptySpaceItem Icon={Check} title="Accept" action={handleAccept} />
@ -102,7 +102,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
link={{ text: "Or start from an empty project", href: "/" }}
>
{!user ? (
{!currentUser ? (
<EmptySpaceItem
Icon={User2}
title="Sign in to continue"
@ -141,7 +141,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => {
)}
</div>
);
};
});
WorkspaceInvitationPage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;