diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index e2e48c0..bcd2a00 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -83,24 +83,6 @@ export class Database { return userOrgs; } - async getProjectsByOrganizationId (organizationId: string): Promise { - const projectRepository = this.dataSource.getRepository(Project); - - const projects = await projectRepository.find({ - relations: { - organization: true, - owner: true - }, - where: { - organization: { - id: organizationId - } - } - }); - - return projects; - } - async getProjectById (projectId: string): Promise { const projectRepository = this.dataSource.getRepository(Project); diff --git a/packages/frontend/package.json b/packages/frontend/package.json index fde8579..98f6253 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -28,7 +28,6 @@ "react-oauth-popup": "^1.0.5", "react-router-dom": "^6.20.1", "react-scripts": "5.0.1", - "react-tabs": "^6.0.2", "react-timer-hook": "^3.0.7", "typescript": "^4.9.5", "usehooks-ts": "^2.10.0", diff --git a/packages/frontend/src/components/projects/project/ProjectTabs.tsx b/packages/frontend/src/components/projects/project/ProjectTabs.tsx deleted file mode 100644 index 5aead90..0000000 --- a/packages/frontend/src/components/projects/project/ProjectTabs.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; -import { Project } from 'gql-client'; - -import OverviewTabPanel from './OverviewTabPanel'; -import DeploymentsTabPanel from './DeploymentsTabPanel'; -import SettingsTabPanel from './SettingsTabPanel'; - -interface ProjectTabsProps { - project: Project; - onUpdate: () => Promise; -} - -const Database = () => ( -
- Content of database tab -

- It is a long established fact that a reader will be distracted by the - readable content of a page when looking at its layout. -

-
-); -const Integrations = () => ( -
- Content of integrations tab -

- There are many variations of passages of Lorem Ipsum available. -

-
-); - -const ProjectTabs = ({ project, onUpdate }: ProjectTabsProps) => { - return ( - - - Overview - Deployments - Database - Integrations - Settings - - - - - - - - - - - - - - - - - - ); -}; - -export default ProjectTabs; diff --git a/packages/frontend/src/pages/org-slug/projects/Id.tsx b/packages/frontend/src/pages/org-slug/projects/Id.tsx index 40586b4..ff86a03 100644 --- a/packages/frontend/src/pages/org-slug/projects/Id.tsx +++ b/packages/frontend/src/pages/org-slug/projects/Id.tsx @@ -1,17 +1,30 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { + Link, + Outlet, + useLocation, + useNavigate, + useParams, +} from 'react-router-dom'; import { Project as ProjectType } from 'gql-client'; -import { Button, Typography } from '@material-tailwind/react'; +import { + Button, + Tab, + Tabs, + TabsBody, + TabsHeader, + Typography, +} from '@material-tailwind/react'; import HorizontalLine from '../../../components/HorizontalLine'; -import ProjectTabs from '../../../components/projects/project/ProjectTabs'; import { useGQLClient } from '../../../context/GQLClientContext'; const Id = () => { const { id } = useParams(); const navigate = useNavigate(); const client = useGQLClient(); + const location = useLocation(); const [project, setProject] = useState(null); @@ -22,6 +35,15 @@ const Id = () => { } }, []); + const currentTab = useMemo(() => { + if (id) { + const [, tabPath] = location.pathname.split(id); + return tabPath; + } else { + return ''; + } + }, [location, id]); + useEffect(() => { fetchProject(id); }, [id]); @@ -54,7 +76,44 @@ const Id = () => {
- + + + + + Overview + + + + + Deployments + + + + + Database + + + + + Integrations + + + + + Settings + + + + + + +
) : ( diff --git a/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx b/packages/frontend/src/pages/org-slug/projects/id/DeploymentsTabPanel.tsx similarity index 86% rename from packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx rename to packages/frontend/src/pages/org-slug/projects/id/DeploymentsTabPanel.tsx index 9a34698..0654f0c 100644 --- a/packages/frontend/src/components/projects/project/DeploymentsTabPanel.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/DeploymentsTabPanel.tsx @@ -1,25 +1,31 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Project, Domain } from 'gql-client'; +import { Domain } from 'gql-client'; +import { useOutletContext } from 'react-router-dom'; import { Button, Typography } from '@material-tailwind/react'; -import DeploymentDetailsCard from './deployments/DeploymentDetailsCard'; +import DeploymentDetailsCard from '../../../../components/projects/project/deployments/DeploymentDetailsCard'; import FilterForm, { FilterValue, StatusOptions, -} from './deployments/FilterForm'; -import { DeploymentDetails } from '../../../types/project'; -import { useGQLClient } from '../../../context/GQLClientContext'; -import { COMMIT_DETAILS } from '../../../constants'; +} from '../../../../components/projects/project/deployments/FilterForm'; +import { + DeploymentDetails, + OutletContextType, +} from '../../../../types/project'; +import { useGQLClient } from '../../../../context/GQLClientContext'; +import { COMMIT_DETAILS } from '../../../../constants'; const DEFAULT_FILTER_VALUE: FilterValue = { searchedBranch: '', status: StatusOptions.ALL_STATUS, }; -const DeploymentsTabPanel = ({ project }: { project: Project }) => { +const DeploymentsTabPanel = () => { const client = useGQLClient(); + const { project } = useOutletContext(); + const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE); const [deployments, setDeployments] = useState([]); const [prodBranchDomains, setProdBranchDomains] = useState([]); diff --git a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx b/packages/frontend/src/pages/org-slug/projects/id/OverviewTabPanel.tsx similarity index 89% rename from packages/frontend/src/components/projects/project/OverviewTabPanel.tsx rename to packages/frontend/src/pages/org-slug/projects/id/OverviewTabPanel.tsx index a198864..453259f 100644 --- a/packages/frontend/src/components/projects/project/OverviewTabPanel.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/OverviewTabPanel.tsx @@ -1,29 +1,26 @@ import React, { useEffect, useState } from 'react'; -import { Domain, DomainStatus, Project } from 'gql-client'; +import { Domain, DomainStatus } from 'gql-client'; +import { useOutletContext } from 'react-router-dom'; import { Typography, Button, Chip } from '@material-tailwind/react'; -import ActivityCard from './ActivityCard'; -import { relativeTimeMs } from '../../../utils/time'; -import { useOctokit } from '../../../context/OctokitContext'; -import { GitCommitDetails } from '../../../types/project'; -import { useGQLClient } from '../../../context/GQLClientContext'; +import ActivityCard from '../../../../components/projects/project/ActivityCard'; +import { relativeTimeMs } from '../../../../utils/time'; +import { useOctokit } from '../../../../context/OctokitContext'; +import { GitCommitDetails, OutletContextType } from '../../../../types/project'; +import { useGQLClient } from '../../../../context/GQLClientContext'; const COMMITS_PER_PAGE = 4; -interface OverviewProps { - project: Project; -} - -// TODO: Check if any live domain is set for production branch - -const OverviewTabPanel = ({ project }: OverviewProps) => { +const OverviewTabPanel = () => { const { octokit } = useOctokit(); const [activities, setActivities] = useState([]); const [liveDomain, setLiveDomain] = useState(); const client = useGQLClient(); + const { project } = useOutletContext(); + useEffect(() => { if (!octokit) { return; diff --git a/packages/frontend/src/components/projects/project/SettingsTabPanel.tsx b/packages/frontend/src/pages/org-slug/projects/id/SettingsTabPanel.tsx similarity index 71% rename from packages/frontend/src/components/projects/project/SettingsTabPanel.tsx rename to packages/frontend/src/pages/org-slug/projects/id/SettingsTabPanel.tsx index d17f768..1648c17 100644 --- a/packages/frontend/src/components/projects/project/SettingsTabPanel.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/SettingsTabPanel.tsx @@ -1,5 +1,5 @@ import React, { createElement } from 'react'; -import { Project } from 'gql-client'; +import { useOutletContext } from 'react-router-dom'; import { Tabs, @@ -9,11 +9,12 @@ import { TabPanel, } from '@material-tailwind/react'; -import Domains from './settings/Domains'; -import GeneralTabPanel from './settings/GeneralTabPanel'; -import { EnvironmentVariablesTabPanel } from './settings/EnvironmentVariablesTabPanel'; -import GitTabPanel from './settings/GitTabPanel'; -import MembersTabPanel from './settings/MembersTabPanel'; +import Domains from '../../../../components/projects/project/settings/Domains'; +import GeneralTabPanel from '../../../../components/projects/project/settings/GeneralTabPanel'; +import { EnvironmentVariablesTabPanel } from '../../../../components/projects/project/settings/EnvironmentVariablesTabPanel'; +import GitTabPanel from '../../../../components/projects/project/settings/GitTabPanel'; +import MembersTabPanel from '../../../../components/projects/project/settings/MembersTabPanel'; +import { OutletContextType } from '../../../../types/project'; const tabsData = [ { @@ -48,13 +49,9 @@ const tabsData = [ }, ]; -const SettingsTabPanel = ({ - project, - onUpdate, -}: { - project: Project; - onUpdate: () => Promise; -}) => { +const SettingsTabPanel = () => { + const { project, onUpdate } = useOutletContext(); + return ( <> ( +
+ Content of database tab +

+ It is a long established fact that a reader will be distracted by the + readable content of a page when looking at its layout. +

+
+); + +const Integrations = () => ( +
+ Content of integrations tab +

+ There are many variations of passages of Lorem Ipsum available. +

+
+); + +export const projectTabRoutes = [ + { + index: true, + element: , + }, + { + path: 'deployments', + element: , + }, + { + path: 'database', + element: , + }, + { + path: 'integrations', + element: , + }, + { + path: 'settings', + element: , + }, +]; diff --git a/packages/frontend/src/pages/org-slug/projects/routes.tsx b/packages/frontend/src/pages/org-slug/projects/routes.tsx index 3a89f70..bd0af80 100644 --- a/packages/frontend/src/pages/org-slug/projects/routes.tsx +++ b/packages/frontend/src/pages/org-slug/projects/routes.tsx @@ -5,6 +5,7 @@ import Id from './Id'; import AddDomain from './id/domain/add'; import { createProjectRoutes } from './create/routes'; import { addDomainRoutes } from './id/domain/add/routes'; +import { projectTabRoutes } from './id/routes'; export const projectsRoutesWithoutSearch = [ { @@ -23,5 +24,6 @@ export const projectsRoutesWithSearch = [ { path: ':id', element: , + children: projectTabRoutes, }, ]; diff --git a/packages/frontend/src/types/project.ts b/packages/frontend/src/types/project.ts index e7c8c9a..3fedabe 100644 --- a/packages/frontend/src/types/project.ts +++ b/packages/frontend/src/types/project.ts @@ -60,3 +60,8 @@ export interface Commit { createdAt: string; branch: string; } + +export type OutletContextType = { + project: Project; + onUpdate: () => Promise; +}; diff --git a/yarn.lock b/yarn.lock index 6cbcf14..79c848f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6139,11 +6139,6 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clsx@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" - integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== - cmd-shim@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" @@ -13281,7 +13276,7 @@ promzard@^1.0.0: dependencies: read "^2.0.0" -prop-types@15.8.1, prop-types@^15.5.0, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -13623,14 +13618,6 @@ react-syntax-highlighter@^15.5.0: prismjs "^1.27.0" refractor "^3.6.0" -react-tabs@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-6.0.2.tgz#bc1065c3828561fee285a8fd045f22e0fcdde1eb" - integrity sha512-aQXTKolnM28k3KguGDBSAbJvcowOQr23A+CUJdzJtOSDOtTwzEaJA+1U4KwhNL9+Obe+jFS7geuvA7ICQPXOnQ== - dependencies: - clsx "^2.0.0" - prop-types "^15.5.0" - react-timer-hook@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/react-timer-hook/-/react-timer-hook-3.0.7.tgz#ac42c43d0034b873cbf97b44eb34ccb2b11fe5e0"