forked from cerc-io/snowballtools-base
Refactor to fetch organization and deployment details in the parent component (#23)
* Refactor to fetch organization details in the parent component * Fetch and use deployment details for a project * Remove deployment field from ProjectDetails type --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
62c969d1ff
commit
af021d3357
@ -4,6 +4,7 @@
|
|||||||
"title": "nextjs-boilerplate-9t44zbky4dg-bygideon-projects",
|
"title": "nextjs-boilerplate-9t44zbky4dg-bygideon-projects",
|
||||||
"status": "Building",
|
"status": "Building",
|
||||||
"isProduction": true,
|
"isProduction": true,
|
||||||
|
"isCurrent": false,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commit": {
|
"commit": {
|
||||||
"hash": "9haif19",
|
"hash": "9haif19",
|
||||||
@ -17,6 +18,7 @@
|
|||||||
"title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects",
|
"title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects",
|
||||||
"status": "Ready",
|
"status": "Ready",
|
||||||
"isProduction": false,
|
"isProduction": false,
|
||||||
|
"isCurrent": false,
|
||||||
"branch": "prod",
|
"branch": "prod",
|
||||||
"commit": {
|
"commit": {
|
||||||
"hash": "43de569",
|
"hash": "43de569",
|
||||||
@ -30,6 +32,7 @@
|
|||||||
"title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects",
|
"title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects",
|
||||||
"status": "Error",
|
"status": "Error",
|
||||||
"isProduction": false,
|
"isProduction": false,
|
||||||
|
"isCurrent": false,
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"commit": {
|
"commit": {
|
||||||
"hash": "4hdsf19",
|
"hash": "4hdsf19",
|
||||||
|
@ -11,13 +11,13 @@ import {
|
|||||||
|
|
||||||
import SearchBar from '../SearchBar';
|
import SearchBar from '../SearchBar';
|
||||||
import { ProjectDetails } from '../../types/project';
|
import { ProjectDetails } from '../../types/project';
|
||||||
import projectsData from '../../assets/projects.json';
|
|
||||||
|
|
||||||
interface ProjectsSearchProps {
|
interface ProjectsSearchProps {
|
||||||
|
projects: ProjectDetails[];
|
||||||
onChange?: (data: ProjectDetails) => void;
|
onChange?: (data: ProjectDetails) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectSearchBar = ({ onChange }: ProjectsSearchProps) => {
|
const ProjectSearchBar = ({ projects, onChange }: ProjectsSearchProps) => {
|
||||||
const [items, setItems] = useState<ProjectDetails[]>([]);
|
const [items, setItems] = useState<ProjectDetails[]>([]);
|
||||||
const [selectedItem, setSelectedItem] = useState<ProjectDetails | null>(null);
|
const [selectedItem, setSelectedItem] = useState<ProjectDetails | null>(null);
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ const ProjectSearchBar = ({ onChange }: ProjectsSearchProps) => {
|
|||||||
onInputValueChange({ inputValue }) {
|
onInputValueChange({ inputValue }) {
|
||||||
setItems(
|
setItems(
|
||||||
inputValue
|
inputValue
|
||||||
? projectsData.filter((project) =>
|
? projects.filter((project) =>
|
||||||
project.title.toLowerCase().includes(inputValue.toLowerCase()),
|
project.title.toLowerCase().includes(inputValue.toLowerCase()),
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { Typography, Button, Chip } from '@material-tailwind/react';
|
import { Typography, Button, Chip } from '@material-tailwind/react';
|
||||||
|
|
||||||
@ -11,7 +11,16 @@ interface OverviewProps {
|
|||||||
project: ProjectDetails;
|
project: ProjectDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OverviewTabPanel = ({ project }: OverviewProps) => (
|
const OverviewTabPanel = ({ project }: OverviewProps) => {
|
||||||
|
const currentDeploymentTitle = useMemo(() => {
|
||||||
|
const deployment = project.deployments.find((deployment) => {
|
||||||
|
return deployment.isCurrent === true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return deployment?.title;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="grid grid-cols-5">
|
<div className="grid grid-cols-5">
|
||||||
<div className="col-span-3 p-2">
|
<div className="col-span-3 p-2">
|
||||||
<div className="flex items-center gap-2 p-2 ">
|
<div className="flex items-center gap-2 p-2 ">
|
||||||
@ -49,12 +58,14 @@ const OverviewTabPanel = ({ project }: OverviewProps) => (
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between p-2 text-sm">
|
<div className="flex justify-between p-2 text-sm">
|
||||||
<p>^ Deployment</p>
|
<p>^ Deployment</p>
|
||||||
<p className="text-blue-600">{project.deployment}</p>
|
<p className="text-blue-600">{currentDeploymentTitle}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between p-2 text-sm">
|
<div className="flex justify-between p-2 text-sm">
|
||||||
<p>^ Created</p>
|
<p>^ Created</p>
|
||||||
<p>
|
<p>
|
||||||
{relativeTime(project.createdAt)} by ^ {project.createdBy}
|
{/* TODO: Use following time conversion wherever required */}
|
||||||
|
{relativeTime(new Date(Number(project.createdAt)).toISOString())} by
|
||||||
|
^ {project.createdBy}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,6 +83,7 @@ const OverviewTabPanel = ({ project }: OverviewProps) => (
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default OverviewTabPanel;
|
export default OverviewTabPanel;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useParams, Link } from 'react-router-dom';
|
import { useParams, Link, useOutletContext } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
@ -7,17 +7,21 @@ import DomainCard from './DomainCard';
|
|||||||
import { DomainDetails } from '../../../../types/project';
|
import { DomainDetails } from '../../../../types/project';
|
||||||
import domainsData from '../../../../assets/domains.json';
|
import domainsData from '../../../../assets/domains.json';
|
||||||
import repositories from '../../../../assets/repositories.json';
|
import repositories from '../../../../assets/repositories.json';
|
||||||
import projectData from '../../../../assets/projects.json';
|
|
||||||
|
|
||||||
const Domains = () => {
|
const Domains = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
|
// @ts-expect-error create context type for projects
|
||||||
|
const { projects } = useOutletContext();
|
||||||
|
|
||||||
const currProject = useMemo(() => {
|
const currProject = useMemo(() => {
|
||||||
return projectData.find((data) => data.id === Number(id));
|
return projects.find((data: any) => Number(data.id) === Number(id));
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const linkedRepo = useMemo(() => {
|
const linkedRepo = useMemo(() => {
|
||||||
return repositories.find((repo) => repo.id === currProject?.repositoryId);
|
return repositories.find(
|
||||||
|
(repo) => repo.id === Number(currProject?.repositoryId),
|
||||||
|
);
|
||||||
}, [currProject]);
|
}, [currProject]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,17 +1,61 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
import HorizontalLine from '../components/HorizontalLine';
|
import HorizontalLine from '../components/HorizontalLine';
|
||||||
import { IconButton, Typography } from '@material-tailwind/react';
|
import { IconButton, Typography } from '@material-tailwind/react';
|
||||||
import ProjectSearchBar from '../components/projects/ProjectSearchBar';
|
import ProjectSearchBar from '../components/projects/ProjectSearchBar';
|
||||||
|
import { useGQLClient } from '../context/GQLClientContext';
|
||||||
|
import { ProjectDetails } from '../types/project';
|
||||||
|
|
||||||
const ProjectSearch = () => {
|
const ProjectSearch = () => {
|
||||||
|
const client = useGQLClient();
|
||||||
|
const [projects, setProjects] = useState<ProjectDetails[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetch = async () => {
|
||||||
|
const res = await client.getOrganizations();
|
||||||
|
|
||||||
|
// Note: select first organization as organization switching not yet implemented
|
||||||
|
const projects = res.organizations[0].projects;
|
||||||
|
const orgName = res.organizations[0].name;
|
||||||
|
|
||||||
|
const updatedProjectsPromises = projects.map(async (project: any) => {
|
||||||
|
const { deployments } = await client.getDeployments(String(project.id));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...project,
|
||||||
|
// TODO: populate empty fields
|
||||||
|
icon: '',
|
||||||
|
title: project.name,
|
||||||
|
organization: orgName,
|
||||||
|
deployments,
|
||||||
|
url: '',
|
||||||
|
domain: null,
|
||||||
|
createdBy: project.owner.name,
|
||||||
|
source: '',
|
||||||
|
repositoryId: project.repository,
|
||||||
|
// TODO: populate from github API
|
||||||
|
latestCommit: {
|
||||||
|
message: '',
|
||||||
|
createdAt: '',
|
||||||
|
branch: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedProjects = await Promise.all(updatedProjectsPromises);
|
||||||
|
setProjects(updatedProjects);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch();
|
||||||
|
}, [client]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="sticky top-0 bg-white z-30">
|
<div className="sticky top-0 bg-white z-30">
|
||||||
<div className="flex p-5">
|
<div className="flex p-5">
|
||||||
<div className="grow mr-2">
|
<div className="grow mr-2">
|
||||||
<ProjectSearchBar onChange={() => {}} />
|
<ProjectSearchBar onChange={() => {}} projects={projects} />
|
||||||
</div>
|
</div>
|
||||||
<IconButton color="blue" className="rounded-full mr-2">
|
<IconButton color="blue" className="rounded-full mr-2">
|
||||||
<Typography variant="h5">+</Typography>
|
<Typography variant="h5">+</Typography>
|
||||||
@ -26,7 +70,7 @@ const ProjectSearch = () => {
|
|||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
</div>
|
</div>
|
||||||
<div className="z-0">
|
<div className="z-0">
|
||||||
<Outlet />
|
<Outlet context={{ projects }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,48 +1,14 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
|
import { useOutletContext } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button, Typography, Chip } from '@material-tailwind/react';
|
import { Button, Typography, Chip } from '@material-tailwind/react';
|
||||||
|
|
||||||
import ProjectCard from '../components/projects/ProjectCard';
|
import ProjectCard from '../components/projects/ProjectCard';
|
||||||
import projectsDetail from '../assets/projects.json';
|
|
||||||
import { useGQLClient } from '../context/GQLClientContext';
|
|
||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
const client = useGQLClient();
|
// @ts-expect-error create context type for projects
|
||||||
const [projects, setProjects] = useState([]);
|
const { projects } = useOutletContext();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchOrganization = async () => {
|
|
||||||
const res = await client.getOrganizations();
|
|
||||||
|
|
||||||
// Note: select first organization as organization switching not yet implemented
|
|
||||||
const projects = res.organizations[0].projects;
|
|
||||||
|
|
||||||
const updatedProjects = projects.map((project: any) => {
|
|
||||||
return {
|
|
||||||
...project,
|
|
||||||
// TODO: populate empty fields
|
|
||||||
icon: '',
|
|
||||||
title: '',
|
|
||||||
organization: '',
|
|
||||||
url: '',
|
|
||||||
domain: null,
|
|
||||||
createdBy: '',
|
|
||||||
source: '',
|
|
||||||
// TODO: populate from github API
|
|
||||||
latestCommit: {
|
|
||||||
message: '',
|
|
||||||
createdAt: '',
|
|
||||||
branch: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
setProjects(updatedProjects);
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchOrganization();
|
|
||||||
}, [client]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -52,7 +18,7 @@ const Projects = () => {
|
|||||||
<Typography variant="h4">Projects</Typography>
|
<Typography variant="h4">Projects</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
className="bg-gray-300 rounded-full static"
|
className="bg-gray-300 rounded-full static"
|
||||||
value={projectsDetail.length}
|
value={projects.length}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -67,7 +33,7 @@ const Projects = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-5 p-5">
|
<div className="grid grid-cols-3 gap-5 p-5">
|
||||||
{projects.length !== 0 &&
|
{projects.length !== 0 &&
|
||||||
projects.map((project, key) => {
|
projects.map((project: any, key: number) => {
|
||||||
return <ProjectCard project={project} key={key} />;
|
return <ProjectCard project={project} key={key} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,28 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useOutletContext, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
import HorizontalLine from '../../components/HorizontalLine';
|
import HorizontalLine from '../../components/HorizontalLine';
|
||||||
import projects from '../../assets/projects.json';
|
|
||||||
import ProjectTabs from '../../components/projects/project/ProjectTabs';
|
import ProjectTabs from '../../components/projects/project/ProjectTabs';
|
||||||
|
|
||||||
const getProject = (id: number) => {
|
const getProject = (projects: any, id: number) => {
|
||||||
return projects.find((project) => {
|
return projects.find((project: any) => {
|
||||||
return project.id === id;
|
return Number(project.id) === id;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const Project = () => {
|
const Project = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const project = useMemo(() => getProject(Number(id)), [id]);
|
|
||||||
|
// @ts-expect-error create context type for projects
|
||||||
|
const { projects } = useOutletContext();
|
||||||
|
|
||||||
|
const project = useMemo(
|
||||||
|
() => getProject(projects, Number(id)),
|
||||||
|
[id, projects],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useOutletContext } from 'react-router-dom';
|
||||||
|
|
||||||
import { Button, Typography, Chip } from '@material-tailwind/react';
|
import { Button, Typography, Chip } from '@material-tailwind/react';
|
||||||
|
|
||||||
import ProjectCard from '../../components/projects/ProjectCard';
|
import ProjectCard from '../../components/projects/ProjectCard';
|
||||||
import projectsDetail from '../../assets/projects.json';
|
|
||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
|
// @ts-expect-error create context type for projects
|
||||||
|
const { projects } = useOutletContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex p-5">
|
<div className="flex p-5">
|
||||||
@ -15,7 +17,7 @@ const Projects = () => {
|
|||||||
<Typography variant="h4">Projects</Typography>
|
<Typography variant="h4">Projects</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
className="bg-gray-300 rounded-full static"
|
className="bg-gray-300 rounded-full static"
|
||||||
value={projectsDetail.length}
|
value={projects.length}
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -29,7 +31,8 @@ const Projects = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 gap-5 p-5">
|
<div className="grid grid-cols-3 gap-5 p-5">
|
||||||
{projectsDetail.map((project, key) => {
|
{projects.length !== 0 &&
|
||||||
|
projects.map((project: any, key: number) => {
|
||||||
return <ProjectCard project={project} key={key} />;
|
return <ProjectCard project={project} key={key} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@ export interface ProjectDetails {
|
|||||||
id: number;
|
id: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
deployment: string;
|
deployments: DeploymentDetails[];
|
||||||
source: string;
|
source: string;
|
||||||
latestCommit: {
|
latestCommit: {
|
||||||
message: string;
|
message: string;
|
||||||
@ -30,6 +30,7 @@ export interface DeploymentDetails {
|
|||||||
isProduction: boolean;
|
isProduction: boolean;
|
||||||
status: Status;
|
status: Status;
|
||||||
branch: string;
|
branch: string;
|
||||||
|
isCurrent: boolean;
|
||||||
commit: {
|
commit: {
|
||||||
hash: string;
|
hash: string;
|
||||||
message: string;
|
message: string;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||||
|
|
||||||
import { getUser, getOrganizations } from './gql-queries';
|
import { getUser, getOrganizations, getDeployments } from './gql-queries';
|
||||||
|
|
||||||
export interface GraphQLConfig {
|
export interface GraphQLConfig {
|
||||||
gqlEndpoint: string;
|
gqlEndpoint: string;
|
||||||
@ -31,4 +31,15 @@ export class GQLClient {
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDeployments (projectId: string) : Promise<any> {
|
||||||
|
const { data } = await this.client.query({
|
||||||
|
query: getDeployments,
|
||||||
|
variables: {
|
||||||
|
projectId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,13 @@ query {
|
|||||||
export const getOrganizations = gql`
|
export const getOrganizations = gql`
|
||||||
query {
|
query {
|
||||||
organizations {
|
organizations {
|
||||||
|
id
|
||||||
|
name
|
||||||
projects {
|
projects {
|
||||||
id
|
id
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
}
|
}
|
||||||
deployments {
|
deployments {
|
||||||
id
|
id
|
||||||
@ -42,3 +45,28 @@ query {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const getDeployments = gql`
|
||||||
|
query ($projectId: String!) {
|
||||||
|
deployments(projectId: $projectId) {
|
||||||
|
id
|
||||||
|
domain{
|
||||||
|
branch
|
||||||
|
createdAt
|
||||||
|
isRedirected
|
||||||
|
id
|
||||||
|
name
|
||||||
|
status
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
branch
|
||||||
|
commitHash
|
||||||
|
title
|
||||||
|
environment
|
||||||
|
isCurrent
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
Loading…
Reference in New Issue
Block a user