snowballtools-base/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx
Nabarun Gogoi 76dfd3bb76
Implement routes for project settings tab (#64)
* Add routes to settings tab panel

* Refactor code to move settings tab components to pages

* Rename registry fields in project and deployment entity

* Use kebab case for routes

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
2024-02-12 15:18:00 +05:30

176 lines
5.2 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Domain, DomainStatus } from 'gql-client';
import { useOutletContext } from 'react-router-dom';
import { Typography, Button, Chip } from '@material-tailwind/react';
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;
const OverviewTabPanel = () => {
const { octokit } = useOctokit();
const [activities, setActivities] = useState<GitCommitDetails[]>([]);
const [liveDomain, setLiveDomain] = useState<Domain>();
const client = useGQLClient();
const { project } = useOutletContext<OutletContextType>();
useEffect(() => {
if (!octokit) {
return;
}
// TODO: Save repo commits in DB and avoid using GitHub API in frontend
// TODO: Figure out fetching latest commits for all branches
const fetchRepoActivity = async () => {
const [owner, repo] = project.repository.split('/');
if (!repo) {
// Do not fetch branches if repo not available
return;
}
// Get all branches in project repo
const result = await octokit.rest.repos.listBranches({
owner,
repo,
});
// Get first 4 commits from repo branches
const commitsByBranchPromises = result.data.map(async (branch) => {
const result = await octokit.rest.repos.listCommits({
owner,
repo,
sha: branch.commit.sha,
per_page: COMMITS_PER_PAGE,
});
return result.data.map((data) => ({
...data,
branch,
}));
});
const commitsByBranch = await Promise.all(commitsByBranchPromises);
const commitsWithBranch = commitsByBranch.flat();
// Order commits by date and set latest 4 commits in activity section
const orderedCommits = commitsWithBranch
.sort(
(a, b) =>
new Date(b.commit.author!.date!).getTime() -
new Date(a.commit.author!.date!).getTime(),
)
.slice(0, COMMITS_PER_PAGE);
setActivities(orderedCommits);
};
fetchRepoActivity();
}, [octokit, project]);
useEffect(() => {
const fetchLiveProdDomain = async () => {
const { domains } = await client.getDomains(project.id, {
branch: project.prodBranch,
status: DomainStatus.Live,
});
if (domains.length === 0) {
return;
}
setLiveDomain(domains[0]);
};
fetchLiveProdDomain();
}, [project]);
return (
<div className="grid grid-cols-5">
<div className="col-span-3 p-2">
<div className="flex items-center gap-2 p-2 ">
<div>^</div>
<div className="grow">
<Typography>{project.name}</Typography>
<Typography variant="small" color="gray">
{project.subDomain}
</Typography>
</div>
</div>
<div className="flex justify-between p-2 text-sm items-center">
<div>^ Domain</div>
{liveDomain ? (
<Chip
className="normal-case ml-6 inline font-normal"
size="sm"
value="Connected"
icon="^"
color="green"
/>
) : (
<div className="flex justify-between items-center w-full m-2">
<Chip
className="normal-case inline font-normal"
size="sm"
value="Not connected"
icon="^"
color="orange"
/>
<Button
className="normal-case rounded-full"
color="blue"
size="sm"
>
Setup
</Button>
</div>
)}
</div>
{project.deployments.length !== 0 ? (
<>
<div className="flex justify-between p-2 text-sm">
<p>^ Source</p>
<p>{project.deployments[0]?.branch}</p>
</div>
<div className="flex justify-between p-2 text-sm">
<p>^ Deployment</p>
<p className="text-blue-600">{liveDomain?.name}</p>
</div>
<div className="flex justify-between p-2 text-sm">
<p>^ Created</p>
<p>
{relativeTimeMs(project.deployments[0].createdAt)} by ^{' '}
{project.deployments[0].createdBy.name}
</p>
</div>
</>
) : (
<div>No current deployment found</div>
)}
</div>
<div className="col-span-2 p-2">
<div className="flex justify-between">
<Typography variant="h6">Activity</Typography>
<button className="text-xs bg-gray-300 rounded-full p-2">
See all
</button>
</div>
<div className="p-2">
{activities.map((activity, index) => {
return <ActivityCard activity={activity} key={index} />;
})}
</div>
</div>
</div>
);
};
export default OverviewTabPanel;