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",
|
||||
"status": "Building",
|
||||
"isProduction": true,
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commit": {
|
||||
"hash": "9haif19",
|
||||
@ -17,6 +18,7 @@
|
||||
"title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects",
|
||||
"status": "Ready",
|
||||
"isProduction": false,
|
||||
"isCurrent": false,
|
||||
"branch": "prod",
|
||||
"commit": {
|
||||
"hash": "43de569",
|
||||
@ -30,6 +32,7 @@
|
||||
"title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects",
|
||||
"status": "Error",
|
||||
"isProduction": false,
|
||||
"isCurrent": false,
|
||||
"branch": "main",
|
||||
"commit": {
|
||||
"hash": "4hdsf19",
|
||||
|
@ -11,13 +11,13 @@ import {
|
||||
|
||||
import SearchBar from '../SearchBar';
|
||||
import { ProjectDetails } from '../../types/project';
|
||||
import projectsData from '../../assets/projects.json';
|
||||
|
||||
interface ProjectsSearchProps {
|
||||
projects: ProjectDetails[];
|
||||
onChange?: (data: ProjectDetails) => void;
|
||||
}
|
||||
|
||||
const ProjectSearchBar = ({ onChange }: ProjectsSearchProps) => {
|
||||
const ProjectSearchBar = ({ projects, onChange }: ProjectsSearchProps) => {
|
||||
const [items, setItems] = useState<ProjectDetails[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<ProjectDetails | null>(null);
|
||||
|
||||
@ -32,7 +32,7 @@ const ProjectSearchBar = ({ onChange }: ProjectsSearchProps) => {
|
||||
onInputValueChange({ inputValue }) {
|
||||
setItems(
|
||||
inputValue
|
||||
? projectsData.filter((project) =>
|
||||
? projects.filter((project) =>
|
||||
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';
|
||||
|
||||
@ -11,7 +11,16 @@ interface OverviewProps {
|
||||
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="col-span-3 p-2">
|
||||
<div className="flex items-center gap-2 p-2 ">
|
||||
@ -49,12 +58,14 @@ const OverviewTabPanel = ({ project }: OverviewProps) => (
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Deployment</p>
|
||||
<p className="text-blue-600">{project.deployment}</p>
|
||||
<p className="text-blue-600">{currentDeploymentTitle}</p>
|
||||
</div>
|
||||
<div className="flex justify-between p-2 text-sm">
|
||||
<p>^ Created</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>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,6 +83,7 @@ const OverviewTabPanel = ({ project }: OverviewProps) => (
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default OverviewTabPanel;
|
||||
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
@ -7,17 +7,21 @@ import DomainCard from './DomainCard';
|
||||
import { DomainDetails } from '../../../../types/project';
|
||||
import domainsData from '../../../../assets/domains.json';
|
||||
import repositories from '../../../../assets/repositories.json';
|
||||
import projectData from '../../../../assets/projects.json';
|
||||
|
||||
const Domains = () => {
|
||||
const { id } = useParams();
|
||||
|
||||
// @ts-expect-error create context type for projects
|
||||
const { projects } = useOutletContext();
|
||||
|
||||
const currProject = useMemo(() => {
|
||||
return projectData.find((data) => data.id === Number(id));
|
||||
return projects.find((data: any) => Number(data.id) === Number(id));
|
||||
}, [id]);
|
||||
|
||||
const linkedRepo = useMemo(() => {
|
||||
return repositories.find((repo) => repo.id === currProject?.repositoryId);
|
||||
return repositories.find(
|
||||
(repo) => repo.id === Number(currProject?.repositoryId),
|
||||
);
|
||||
}, [currProject]);
|
||||
|
||||
return (
|
||||
|
@ -1,17 +1,61 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import HorizontalLine from '../components/HorizontalLine';
|
||||
import { IconButton, Typography } from '@material-tailwind/react';
|
||||
import ProjectSearchBar from '../components/projects/ProjectSearchBar';
|
||||
import { useGQLClient } from '../context/GQLClientContext';
|
||||
import { ProjectDetails } from '../types/project';
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<div className="sticky top-0 bg-white z-30">
|
||||
<div className="flex p-5">
|
||||
<div className="grow mr-2">
|
||||
<ProjectSearchBar onChange={() => {}} />
|
||||
<ProjectSearchBar onChange={() => {}} projects={projects} />
|
||||
</div>
|
||||
<IconButton color="blue" className="rounded-full mr-2">
|
||||
<Typography variant="h5">+</Typography>
|
||||
@ -26,7 +70,7 @@ const ProjectSearch = () => {
|
||||
<HorizontalLine />
|
||||
</div>
|
||||
<div className="z-0">
|
||||
<Outlet />
|
||||
<Outlet context={{ projects }} />
|
||||
</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 { Button, Typography, Chip } from '@material-tailwind/react';
|
||||
|
||||
import ProjectCard from '../components/projects/ProjectCard';
|
||||
import projectsDetail from '../assets/projects.json';
|
||||
import { useGQLClient } from '../context/GQLClientContext';
|
||||
|
||||
const Projects = () => {
|
||||
const client = useGQLClient();
|
||||
const [projects, setProjects] = useState([]);
|
||||
|
||||
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]);
|
||||
// @ts-expect-error create context type for projects
|
||||
const { projects } = useOutletContext();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -52,7 +18,7 @@ const Projects = () => {
|
||||
<Typography variant="h4">Projects</Typography>
|
||||
<Chip
|
||||
className="bg-gray-300 rounded-full static"
|
||||
value={projectsDetail.length}
|
||||
value={projects.length}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
@ -67,7 +33,7 @@ const Projects = () => {
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-5 p-5">
|
||||
{projects.length !== 0 &&
|
||||
projects.map((project, key) => {
|
||||
projects.map((project: any, key: number) => {
|
||||
return <ProjectCard project={project} key={key} />;
|
||||
})}
|
||||
</div>
|
||||
|
@ -1,22 +1,28 @@
|
||||
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 HorizontalLine from '../../components/HorizontalLine';
|
||||
import projects from '../../assets/projects.json';
|
||||
import ProjectTabs from '../../components/projects/project/ProjectTabs';
|
||||
|
||||
const getProject = (id: number) => {
|
||||
return projects.find((project) => {
|
||||
return project.id === id;
|
||||
const getProject = (projects: any, id: number) => {
|
||||
return projects.find((project: any) => {
|
||||
return Number(project.id) === id;
|
||||
});
|
||||
};
|
||||
|
||||
const Project = () => {
|
||||
const { id } = useParams();
|
||||
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 (
|
||||
<div className="h-full">
|
||||
|
@ -1,12 +1,14 @@
|
||||
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 ProjectCard from '../../components/projects/ProjectCard';
|
||||
import projectsDetail from '../../assets/projects.json';
|
||||
|
||||
const Projects = () => {
|
||||
// @ts-expect-error create context type for projects
|
||||
const { projects } = useOutletContext();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-5">
|
||||
@ -15,7 +17,7 @@ const Projects = () => {
|
||||
<Typography variant="h4">Projects</Typography>
|
||||
<Chip
|
||||
className="bg-gray-300 rounded-full static"
|
||||
value={projectsDetail.length}
|
||||
value={projects.length}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
@ -29,7 +31,8 @@ const Projects = () => {
|
||||
</div>
|
||||
</div>
|
||||
<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} />;
|
||||
})}
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ export interface ProjectDetails {
|
||||
id: number;
|
||||
createdAt: string;
|
||||
createdBy: string;
|
||||
deployment: string;
|
||||
deployments: DeploymentDetails[];
|
||||
source: string;
|
||||
latestCommit: {
|
||||
message: string;
|
||||
@ -30,6 +30,7 @@ export interface DeploymentDetails {
|
||||
isProduction: boolean;
|
||||
status: Status;
|
||||
branch: string;
|
||||
isCurrent: boolean;
|
||||
commit: {
|
||||
hash: string;
|
||||
message: string;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||
|
||||
import { getUser, getOrganizations } from './gql-queries';
|
||||
import { getUser, getOrganizations, getDeployments } from './gql-queries';
|
||||
|
||||
export interface GraphQLConfig {
|
||||
gqlEndpoint: string;
|
||||
@ -31,4 +31,15 @@ export class GQLClient {
|
||||
|
||||
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`
|
||||
query {
|
||||
organizations {
|
||||
id
|
||||
name
|
||||
projects {
|
||||
id
|
||||
owner {
|
||||
id
|
||||
name
|
||||
}
|
||||
deployments {
|
||||
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