forked from cerc-io/snowballtools-base
197 lines
6.1 KiB
TypeScript
197 lines
6.1 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Domain, DomainStatus } from 'gql-client';
|
|
import { useNavigate, useOutletContext } from 'react-router-dom';
|
|
import { RequestError } from 'octokit';
|
|
|
|
import { Typography, Chip, Avatar, Tooltip } from '@material-tailwind/react';
|
|
|
|
import ActivityCard from '../../../../components/projects/project/ActivityCard';
|
|
import { relativeTimeMs } from '../../../../utils/time';
|
|
import { useOctokit } from '../../../../context/OctokitContext';
|
|
import { GitCommitWithBranch, OutletContextType } from '../../../../types';
|
|
import { useGQLClient } from '../../../../context/GQLClientContext';
|
|
import { formatAddress } from '../../../../utils/format';
|
|
import { Button } from 'components/shared/Button';
|
|
import { Heading } from 'components/shared/Heading';
|
|
|
|
const COMMITS_PER_PAGE = 4;
|
|
|
|
const OverviewTabPanel = () => {
|
|
const { octokit } = useOctokit();
|
|
const navigate = useNavigate();
|
|
const [activities, setActivities] = useState<GitCommitWithBranch[]>([]);
|
|
const [liveDomain, setLiveDomain] = useState<Domain>();
|
|
|
|
const client = useGQLClient();
|
|
|
|
const { project } = useOutletContext<OutletContextType>();
|
|
|
|
useEffect(() => {
|
|
// 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 () => {
|
|
try {
|
|
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);
|
|
} catch (err) {
|
|
if (!(err instanceof RequestError)) {
|
|
throw err;
|
|
}
|
|
|
|
// TODO: Show warning in activity section on request error
|
|
console.log(err.message);
|
|
}
|
|
};
|
|
|
|
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 gap-[72px] mt-7">
|
|
<div className="col-span-3">
|
|
<div className="flex items-center gap-2">
|
|
<Avatar
|
|
src={project.icon || '/gray.png'}
|
|
variant="rounded"
|
|
placeholder={''}
|
|
/>
|
|
<div className="grow">
|
|
<Typography placeholder={''}>{project.name}</Typography>
|
|
<Typography variant="small" color="gray" placeholder={''}>
|
|
{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 items-center">
|
|
<Chip
|
|
className="normal-case inline font-normal mx-2"
|
|
size="sm"
|
|
value="Not connected"
|
|
icon="^"
|
|
color="orange"
|
|
/>
|
|
<Button
|
|
className="normal-case rounded-full"
|
|
variant="primary"
|
|
size="sm"
|
|
onClick={() => {
|
|
navigate('settings/domains');
|
|
}}
|
|
>
|
|
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 ^{' '}
|
|
<Tooltip content={project.deployments[0].createdBy.name}>
|
|
{formatAddress(project.deployments[0].createdBy.name ?? '')}
|
|
</Tooltip>
|
|
</p>
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div>No current deployment found</div>
|
|
)}
|
|
</div>
|
|
<div className="col-span-2 mr-1">
|
|
<div className="flex items-center justify-between">
|
|
<Heading className="text-lg leading-6 font-medium">Activity</Heading>
|
|
<Button variant="tertiary" size="sm">
|
|
See all
|
|
</Button>
|
|
</div>
|
|
<div className="mt-5">
|
|
{activities.map((activity, index) => {
|
|
return (
|
|
<ActivityCard activity={activity} key={`activity-${index}`} />
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default OverviewTabPanel;
|