From 43a0d3b14c36b0a2cba3ef4300c29f44dbbd27b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20B=C3=A9gaudeau?= Date: Tue, 10 Oct 2023 04:43:20 +0200 Subject: [PATCH] [261] Ensure that the workspace view is located in the project router MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/svalyn/svalyn-studio/issues/261 Signed-off-by: Stéphane Bégaudeau --- frontend/svalyn-studio-app/src/app/App.tsx | 2 - .../src/projects/ProjectProvider.tsx | 87 ++++++++++++++++ .../src/projects/ProjectProvider.types.ts | 49 ++++++++++ .../src/projects/ProjectRouter.tsx | 31 ++++-- .../src/projects/ProjectShell.tsx | 98 +++---------------- .../src/projects/ProjectShell.types.ts | 27 ----- .../workspace/WorkspaceView.tsx | 10 +- .../workspace/WorkspaceView.types.ts | 0 .../workspace/data/ChangeData.ts | 4 +- .../{ => projects}/workspace/data/FileData.ts | 4 +- .../workspace/data/FolderData.ts | 4 +- .../workspace/data/ResourceData.ts | 2 +- .../data/SingleChangeEditingContextData.ts | 6 +- .../src/viewers/ViewerCard.tsx | 2 +- 14 files changed, 189 insertions(+), 137 deletions(-) create mode 100644 frontend/svalyn-studio-app/src/projects/ProjectProvider.tsx create mode 100644 frontend/svalyn-studio-app/src/projects/ProjectProvider.types.ts rename frontend/svalyn-studio-app/src/{ => projects}/workspace/WorkspaceView.tsx (90%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/WorkspaceView.types.ts (100%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/data/ChangeData.ts (94%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/data/FileData.ts (96%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/data/FolderData.ts (94%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/data/ResourceData.ts (93%) rename frontend/svalyn-studio-app/src/{ => projects}/workspace/data/SingleChangeEditingContextData.ts (91%) diff --git a/frontend/svalyn-studio-app/src/app/App.tsx b/frontend/svalyn-studio-app/src/app/App.tsx index 535bcee1..1f13bef3 100644 --- a/frontend/svalyn-studio-app/src/app/App.tsx +++ b/frontend/svalyn-studio-app/src/app/App.tsx @@ -35,7 +35,6 @@ import { ProfilesRouter } from '../profiles/ProfilesRouter'; import { ProjectRouter } from '../projects/ProjectRouter'; import { SearchRouter } from '../search/SearchRouter'; import { SettingsRouter } from '../settings/SettingsRouter'; -import { WorkspaceView } from '../workspace/WorkspaceView'; export const App = () => { return ( @@ -45,7 +44,6 @@ export const App = () => { } /> } /> } /> - } /> } /> } /> } /> diff --git a/frontend/svalyn-studio-app/src/projects/ProjectProvider.tsx b/frontend/svalyn-studio-app/src/projects/ProjectProvider.tsx new file mode 100644 index 00000000..20e692e3 --- /dev/null +++ b/frontend/svalyn-studio-app/src/projects/ProjectProvider.tsx @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 Stéphane Bégaudeau. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { gql, useQuery } from '@apollo/client'; +import CorporateFareIcon from '@mui/icons-material/CorporateFare'; +import { useSnackbar } from 'notistack'; +import { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { NotFoundView } from '../notfound/NotFoundView'; +import { goToDomains, goToHelp, goToHome, goToNotifications, goToSettings } from '../palette/DefaultPaletteActions'; +import { PaletteNavigationAction } from '../palette/Palette.types'; +import { usePalette } from '../palette/usePalette'; +import { GetProjectData, GetProjectVariables, ProjectProviderProps } from './ProjectProvider.types'; +import { ProjectContext } from './useProject'; + +const getProjectQuery = gql` + query getProject($identifier: ID!) { + viewer { + project(identifier: $identifier) { + identifier + name + description + organization { + identifier + name + role + } + } + } + } +`; + +export const ProjectProvider = ({ children }: ProjectProviderProps) => { + const { projectIdentifier } = useParams(); + + const { enqueueSnackbar } = useSnackbar(); + const { setActions } = usePalette(); + + const variables: GetProjectVariables = { identifier: projectIdentifier ?? '' }; + const { data, error } = useQuery(getProjectQuery, { variables }); + + useEffect(() => { + if (data) { + const { + viewer: { project }, + } = data; + if (project) { + const backToOrganization: PaletteNavigationAction = { + type: 'navigation-action', + id: 'go-to-organization', + icon: , + label: project.organization.name, + to: `/orgs/${project.organization.identifier}`, + }; + setActions([goToHome, backToOrganization, goToDomains, goToNotifications, goToSettings, goToHelp]); + } + } + if (error) { + enqueueSnackbar(error.message, { variant: 'error' }); + } + }, [data, error]); + + if (!data) { + return null; + } + if (!data.viewer.project) { + return ; + } + + return {children}; +}; diff --git a/frontend/svalyn-studio-app/src/projects/ProjectProvider.types.ts b/frontend/svalyn-studio-app/src/projects/ProjectProvider.types.ts new file mode 100644 index 00000000..7d55fe18 --- /dev/null +++ b/frontend/svalyn-studio-app/src/projects/ProjectProvider.types.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Stéphane Bégaudeau. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export interface ProjectProviderProps { + children?: React.ReactNode; +} + +export interface GetProjectData { + viewer: Viewer; +} + +export interface Viewer { + project: Project | null; +} + +export interface Project { + identifier: string; + name: string; + description: string; + organization: Organization; +} + +export interface Organization { + identifier: string; + name: string; + role: MembershipRole; +} + +export type MembershipRole = 'ADMIN' | 'MEMBER' | 'NONE'; + +export interface GetProjectVariables { + identifier: string; +} diff --git a/frontend/svalyn-studio-app/src/projects/ProjectRouter.tsx b/frontend/svalyn-studio-app/src/projects/ProjectRouter.tsx index 86796f15..7fb7b8e6 100644 --- a/frontend/svalyn-studio-app/src/projects/ProjectRouter.tsx +++ b/frontend/svalyn-studio-app/src/projects/ProjectRouter.tsx @@ -18,6 +18,7 @@ */ import { Route, Routes } from 'react-router-dom'; +import { ProjectProvider } from './ProjectProvider'; import { ProjectShell } from './ProjectShell'; import { ProjectActivityView } from './activity/ProjectActivityView'; import { ChangeProposalRouter } from './changeproposal/ChangeProposalRouter'; @@ -27,21 +28,31 @@ import { NewChangeProposalView } from './new-changeproposal/NewChangeProposalVie import { ResourceView } from './resource/ResourceView'; import { ProjectSettingsView } from './settings/ProjectSettingsView'; import { ProjectTagsView } from './tags/ProjectTagsView'; +import { WorkspaceView } from './workspace/WorkspaceView'; export const ProjectRouter = () => { return ( - + - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + + + + + + + + + } /> + + + ); +}; + +const withProjectShell = (Component: () => JSX.Element | null) => { + return ( + + ); }; diff --git a/frontend/svalyn-studio-app/src/projects/ProjectShell.tsx b/frontend/svalyn-studio-app/src/projects/ProjectShell.tsx index 0b3ee3cb..6747aa67 100644 --- a/frontend/svalyn-studio-app/src/projects/ProjectShell.tsx +++ b/frontend/svalyn-studio-app/src/projects/ProjectShell.tsx @@ -17,95 +17,29 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { gql, useQuery } from '@apollo/client'; -import CorporateFareIcon from '@mui/icons-material/CorporateFare'; import Box from '@mui/material/Box'; -import { useSnackbar } from 'notistack'; -import { useEffect } from 'react'; -import { useParams } from 'react-router-dom'; import { Navbar } from '../navbars/Navbar'; -import { NotFoundView } from '../notfound/NotFoundView'; -import { goToDomains, goToHelp, goToHome, goToNotifications, goToSettings } from '../palette/DefaultPaletteActions'; -import { PaletteNavigationAction } from '../palette/Palette.types'; -import { usePalette } from '../palette/usePalette'; import { ProjectBreadcrumbs } from './ProjectBreadcrumbs'; import { ProjectDrawer } from './ProjectDrawer'; -import { GetProjectData, GetProjectVariables, ProjectShellProps } from './ProjectShell.types'; -import { ProjectContext } from './useProject'; - -const getProjectQuery = gql` - query getProject($identifier: ID!) { - viewer { - project(identifier: $identifier) { - identifier - name - description - organization { - identifier - name - role - } - } - } - } -`; +import { ProjectShellProps } from './ProjectShell.types'; export const ProjectShell = ({ children }: ProjectShellProps) => { - const { enqueueSnackbar } = useSnackbar(); - - const { projectIdentifier } = useParams(); - const variables: GetProjectVariables = { identifier: projectIdentifier ?? '' }; - const { data, error } = useQuery(getProjectQuery, { variables }); - - const { setActions } = usePalette(); - - useEffect(() => { - if (data) { - const { - viewer: { project }, - } = data; - if (project) { - const backToOrganization: PaletteNavigationAction = { - type: 'navigation-action', - id: 'go-to-organization', - icon: , - label: project.organization.name, - to: `/orgs/${project.organization.identifier}`, - }; - setActions([goToHome, backToOrganization, goToDomains, goToNotifications, goToSettings, goToHelp]); - } - } - if (error) { - enqueueSnackbar(error.message, { variant: 'error' }); - } - }, [data, error]); - - if (!data) { - return null; - } - - if (!data.viewer.project) { - return ; - } - return ( - - - - - - - - {children} - + + + + + + + {children} - + ); }; diff --git a/frontend/svalyn-studio-app/src/projects/ProjectShell.types.ts b/frontend/svalyn-studio-app/src/projects/ProjectShell.types.ts index 7a41984f..d8b18838 100644 --- a/frontend/svalyn-studio-app/src/projects/ProjectShell.types.ts +++ b/frontend/svalyn-studio-app/src/projects/ProjectShell.types.ts @@ -20,30 +20,3 @@ export interface ProjectShellProps { children?: React.ReactNode; } - -export interface GetProjectData { - viewer: Viewer; -} - -export interface Viewer { - project: Project | null; -} - -export interface Project { - identifier: string; - name: string; - description: string; - organization: Organization; -} - -export interface Organization { - identifier: string; - name: string; - role: MembershipRole; -} - -export type MembershipRole = 'ADMIN' | 'MEMBER' | 'NONE'; - -export interface GetProjectVariables { - identifier: string; -} diff --git a/frontend/svalyn-studio-app/src/workspace/WorkspaceView.tsx b/frontend/svalyn-studio-app/src/projects/workspace/WorkspaceView.tsx similarity index 90% rename from frontend/svalyn-studio-app/src/workspace/WorkspaceView.tsx rename to frontend/svalyn-studio-app/src/projects/workspace/WorkspaceView.tsx index a6cc7c20..0418c5d0 100644 --- a/frontend/svalyn-studio-app/src/workspace/WorkspaceView.tsx +++ b/frontend/svalyn-studio-app/src/projects/workspace/WorkspaceView.tsx @@ -22,11 +22,11 @@ import Box from '@mui/material/Box'; import { useSnackbar } from 'notistack'; import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import { Workbench } from '../workbench/Workbench'; -import { IEditingContext } from '../workbench/api/editingcontext/EditingContext.types'; -import { EditingContextProvider } from '../workbench/api/editingcontext/EditingContextProvider'; -import { IAdaptable, IAdapterFactory } from '../workbench/api/providers/AdapterFactory.types'; -import { AdapterFactoryProvider } from '../workbench/api/providers/AdapterFactoryProvider'; +import { Workbench } from '../../workbench/Workbench'; +import { IEditingContext } from '../../workbench/api/editingcontext/EditingContext.types'; +import { EditingContextProvider } from '../../workbench/api/editingcontext/EditingContextProvider'; +import { IAdaptable, IAdapterFactory } from '../../workbench/api/providers/AdapterFactory.types'; +import { AdapterFactoryProvider } from '../../workbench/api/providers/AdapterFactoryProvider'; import { Change, GetChangeResourcesData, GetChangeResourcesVariables } from './WorkspaceView.types'; import { ChangeData, ChangeDataItemProvider } from './data/ChangeData'; import { FileData, FileDataItemProvider } from './data/FileData'; diff --git a/frontend/svalyn-studio-app/src/workspace/WorkspaceView.types.ts b/frontend/svalyn-studio-app/src/projects/workspace/WorkspaceView.types.ts similarity index 100% rename from frontend/svalyn-studio-app/src/workspace/WorkspaceView.types.ts rename to frontend/svalyn-studio-app/src/projects/workspace/WorkspaceView.types.ts diff --git a/frontend/svalyn-studio-app/src/workspace/data/ChangeData.ts b/frontend/svalyn-studio-app/src/projects/workspace/data/ChangeData.ts similarity index 94% rename from frontend/svalyn-studio-app/src/workspace/data/ChangeData.ts rename to frontend/svalyn-studio-app/src/projects/workspace/data/ChangeData.ts index 14de857f..92f0a3fc 100644 --- a/frontend/svalyn-studio-app/src/workspace/data/ChangeData.ts +++ b/frontend/svalyn-studio-app/src/projects/workspace/data/ChangeData.ts @@ -18,14 +18,14 @@ */ import { ReactNode } from 'react'; -import { IAdaptable } from '../../workbench/api/providers/AdapterFactory.types'; +import { IAdaptable } from '../../../workbench/api/providers/AdapterFactory.types'; import { IItemIdentityProvider, IItemLabelProvider, IItemViewProvider, ITreeItemContentProvider, ViewContent, -} from '../../workbench/api/providers/ItemProviders.types'; +} from '../../../workbench/api/providers/ItemProviders.types'; import { IContainerData, IResourceData } from './ResourceData'; export class ChangeData implements IContainerData { diff --git a/frontend/svalyn-studio-app/src/workspace/data/FileData.ts b/frontend/svalyn-studio-app/src/projects/workspace/data/FileData.ts similarity index 96% rename from frontend/svalyn-studio-app/src/workspace/data/FileData.ts rename to frontend/svalyn-studio-app/src/projects/workspace/data/FileData.ts index 15bba33a..9ce86117 100644 --- a/frontend/svalyn-studio-app/src/workspace/data/FileData.ts +++ b/frontend/svalyn-studio-app/src/projects/workspace/data/FileData.ts @@ -19,14 +19,14 @@ import { ApolloClient, gql } from '@apollo/client'; import { ReactNode } from 'react'; -import { IAdaptable } from '../../workbench/api/providers/AdapterFactory.types'; +import { IAdaptable } from '../../../workbench/api/providers/AdapterFactory.types'; import { IItemIdentityProvider, IItemLabelProvider, IItemViewProvider, ITreeItemContentProvider, ViewContent, -} from '../../workbench/api/providers/ItemProviders.types'; +} from '../../../workbench/api/providers/ItemProviders.types'; import { IContainerData, IResourceData } from './ResourceData'; export class FileData implements IResourceData { diff --git a/frontend/svalyn-studio-app/src/workspace/data/FolderData.ts b/frontend/svalyn-studio-app/src/projects/workspace/data/FolderData.ts similarity index 94% rename from frontend/svalyn-studio-app/src/workspace/data/FolderData.ts rename to frontend/svalyn-studio-app/src/projects/workspace/data/FolderData.ts index e9ac7c67..969655cf 100644 --- a/frontend/svalyn-studio-app/src/workspace/data/FolderData.ts +++ b/frontend/svalyn-studio-app/src/projects/workspace/data/FolderData.ts @@ -18,14 +18,14 @@ */ import { ReactNode } from 'react'; -import { IAdaptable } from '../../workbench/api/providers/AdapterFactory.types'; +import { IAdaptable } from '../../../workbench/api/providers/AdapterFactory.types'; import { IItemIdentityProvider, IItemLabelProvider, IItemViewProvider, ITreeItemContentProvider, ViewContent, -} from '../../workbench/api/providers/ItemProviders.types'; +} from '../../../workbench/api/providers/ItemProviders.types'; import { IContainerData, IResourceData } from './ResourceData'; export class FolderData implements IContainerData { diff --git a/frontend/svalyn-studio-app/src/workspace/data/ResourceData.ts b/frontend/svalyn-studio-app/src/projects/workspace/data/ResourceData.ts similarity index 93% rename from frontend/svalyn-studio-app/src/workspace/data/ResourceData.ts rename to frontend/svalyn-studio-app/src/projects/workspace/data/ResourceData.ts index 868a7d45..5eb8984d 100644 --- a/frontend/svalyn-studio-app/src/workspace/data/ResourceData.ts +++ b/frontend/svalyn-studio-app/src/projects/workspace/data/ResourceData.ts @@ -17,7 +17,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { IAdaptable } from '../../workbench/api/providers/AdapterFactory.types'; +import { IAdaptable } from '../../../workbench/api/providers/AdapterFactory.types'; export interface IResourceData extends IAdaptable { container: IContainerData | null; diff --git a/frontend/svalyn-studio-app/src/workspace/data/SingleChangeEditingContextData.ts b/frontend/svalyn-studio-app/src/projects/workspace/data/SingleChangeEditingContextData.ts similarity index 91% rename from frontend/svalyn-studio-app/src/workspace/data/SingleChangeEditingContextData.ts rename to frontend/svalyn-studio-app/src/projects/workspace/data/SingleChangeEditingContextData.ts index fbc06c75..c31935f4 100644 --- a/frontend/svalyn-studio-app/src/workspace/data/SingleChangeEditingContextData.ts +++ b/frontend/svalyn-studio-app/src/projects/workspace/data/SingleChangeEditingContextData.ts @@ -18,15 +18,15 @@ */ import { ReactNode } from 'react'; -import { IEditingContext } from '../../workbench/api/editingcontext/EditingContext.types'; -import { IAdaptable } from '../../workbench/api/providers/AdapterFactory.types'; +import { IEditingContext } from '../../../workbench/api/editingcontext/EditingContext.types'; +import { IAdaptable } from '../../../workbench/api/providers/AdapterFactory.types'; import { IItemIdentityProvider, IItemLabelProvider, IItemViewProvider, ITreeItemContentProvider, ViewContent, -} from '../../workbench/api/providers/ItemProviders.types'; +} from '../../../workbench/api/providers/ItemProviders.types'; import { ChangeData } from './ChangeData'; export class SingleChangeEditingContextData implements IEditingContext { diff --git a/frontend/svalyn-studio-app/src/viewers/ViewerCard.tsx b/frontend/svalyn-studio-app/src/viewers/ViewerCard.tsx index d66bfe33..72a4b8c2 100644 --- a/frontend/svalyn-studio-app/src/viewers/ViewerCard.tsx +++ b/frontend/svalyn-studio-app/src/viewers/ViewerCard.tsx @@ -24,9 +24,9 @@ import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; +import { FileData, FileDataItemProvider } from '../projects/workspace/data/FileData'; import { IAdaptable, IAdapterFactory } from '../workbench/api/providers/AdapterFactory.types'; import { AdapterFactoryProvider } from '../workbench/api/providers/AdapterFactoryProvider'; -import { FileData, FileDataItemProvider } from '../workspace/data/FileData'; import { Viewer } from './Viewer'; import { ViewerCardProps } from './ViewerCard.types';