Add chips to display different kinds of information (#20)

* Add chip for showing deployment status

* Add chip to display projects count

* Display label if project search is not matching

* Add chip to display domain is not connected

* Fix text size in chips for deployment domain

* Add chip to display if repository is private

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-12-21 16:42:06 +05:30 committed by GitHub
parent 3133fb989f
commit 0a1a53e0bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 78 deletions

View File

@ -1,6 +1,6 @@
[ [
{ {
"title": "nextjs-bolerplate-9t44zbky4dg-bygideon-projects", "title": "nextjs-boilerplate-9t44zbky4dg-bygideon-projects",
"status": "Building", "status": "Building",
"environment": "Production", "environment": "Production",
"branch": "prod", "branch": "prod",
@ -12,7 +12,7 @@
"updatedAt": "2023-12-11T04:20:00" "updatedAt": "2023-12-11T04:20:00"
}, },
{ {
"title": "nextjs-bolerplate-9232dwky4dg-bygideon-projects", "title": "nextjs-boilerplate-9232dwky4dg-bygideon-projects",
"status": "Ready", "status": "Ready",
"environment": "Preview", "environment": "Preview",
"branch": "prod", "branch": "prod",
@ -24,7 +24,7 @@
"updatedAt": "2023-12-11T04:20:00" "updatedAt": "2023-12-11T04:20:00"
}, },
{ {
"title": "nextjs-bolerplate-9saa22y4dg-bygideon-projects", "title": "nextjs-boilerplate-9saa22y4dg-bygideon-projects",
"status": "Error", "status": "Error",
"environment": "Production", "environment": "Production",
"branch": "main", "branch": "main",

View File

@ -4,8 +4,9 @@
"icon": "^", "icon": "^",
"name": "iglotools", "name": "iglotools",
"title": "Iglotools", "title": "Iglotools",
"domain": "",
"organization": "Airfoil", "organization": "Airfoil",
"domain": "iglotools.co", "url": "iglotools.co",
"createdAt": "2023-12-07T04:20:00", "createdAt": "2023-12-07T04:20:00",
"createdBy": "Alice", "createdBy": "Alice",
"deployment": "iglotools.snowballtools.co", "deployment": "iglotools.snowballtools.co",
@ -21,8 +22,9 @@
"icon": "^", "icon": "^",
"name": "snowball-starter-kit", "name": "snowball-starter-kit",
"title": "Snowball Starter Kit", "title": "Snowball Starter Kit",
"domain": "",
"organization": "Snowball", "organization": "Snowball",
"domain": "starterkit.snowballtools.com", "url": "starterkit.snowballtools.com",
"createdAt": "2023-12-04T04:20:00", "createdAt": "2023-12-04T04:20:00",
"createdBy": "Bob", "createdBy": "Bob",
"deployment": "deploy.snowballtools.com", "deployment": "deploy.snowballtools.com",
@ -38,8 +40,9 @@
"icon": "^", "icon": "^",
"name": "web3-android", "name": "web3-android",
"title": "Web3 Android", "title": "Web3 Android",
"domain": "",
"organization": "Personal", "organization": "Personal",
"domain": "web3fordroids.com", "url": "web3fordroids.com",
"createdAt": "2023-12-01T04:20:00", "createdAt": "2023-12-01T04:20:00",
"createdBy": "Charlie", "createdBy": "Charlie",
"deployment": "deploy.web3fordroids.com", "deployment": "deploy.web3fordroids.com",
@ -55,8 +58,9 @@
"icon": "^", "icon": "^",
"name": "passkeys-demo", "name": "passkeys-demo",
"title": "Passkeys Demo", "title": "Passkeys Demo",
"domain": "",
"organization": "Airfoil", "organization": "Airfoil",
"domain": "passkeys.iglootools.xyz", "url": "passkeys.iglootools.xyz",
"createdAt": "2023-12-01T04:20:00", "createdAt": "2023-12-01T04:20:00",
"createdBy": "David", "createdBy": "David",
"deployment": "demo.passkeys.xyz", "deployment": "demo.passkeys.xyz",
@ -72,8 +76,9 @@
"icon": "^", "icon": "^",
"name": "iglootools", "name": "iglootools",
"title": "Iglootools", "title": "Iglootools",
"domain": "",
"organization": "Airfoil", "organization": "Airfoil",
"domain": "iglotools.xyz", "url": "iglotools.xyz",
"createdAt": "2023-12-11T04:20:00", "createdAt": "2023-12-11T04:20:00",
"createdBy": "Erin", "createdBy": "Erin",
"deployment": "staging.snowballtools.com", "deployment": "staging.snowballtools.com",
@ -89,8 +94,9 @@
"icon": "^", "icon": "^",
"name": "iglootools", "name": "iglootools",
"title": "Iglootools", "title": "Iglootools",
"domain": "",
"organization": "Airfoil", "organization": "Airfoil",
"domain": "iglotools.xyz", "url": "iglotools.xyz",
"createdAt": "2023-12-11T04:20:00", "createdAt": "2023-12-11T04:20:00",
"createdBy": "Frank", "createdBy": "Frank",
"deployment": "iglotools.snowballtools.com", "deployment": "iglotools.snowballtools.com",

View File

@ -2,31 +2,31 @@
{ {
"title": "project-101", "title": "project-101",
"updatedAt": "2023-12-21T08:30:00", "updatedAt": "2023-12-21T08:30:00",
"user": "bob" "user": "bob",
"private": false
}, },
{ {
"title": "project-102", "title": "project-102",
"updatedAt": "2023-12-21T08:30:00", "updatedAt": "2023-12-21T08:30:00",
"user": "alice" "user": "alice",
"private": true
}, },
{ {
"title": "project-103", "title": "project-103",
"updatedAt": "2023-12-21T04:20:00", "updatedAt": "2023-12-21T04:20:00",
"user": "charlie" "user": "charlie",
"private": false
}, },
{ {
"title": "project-104", "title": "project-104",
"updatedAt": "2023-12-21T04:27:00", "updatedAt": "2023-12-21T04:27:00",
"user": "alice" "user": "alice",
"private": false
}, },
{ {
"title": "project-105", "title": "project-105",
"updatedAt": "2023-12-21T04:41:00", "updatedAt": "2023-12-21T04:41:00",
"user": "ivan" "user": "ivan",
}, "private": false
{
"title": "project-106",
"updatedAt": "2023-12-21T04:32:00",
"user": "david"
} }
] ]

View File

@ -25,7 +25,7 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project }) => {
<Link to={`projects/${project.id}`}> <Link to={`projects/${project.id}`}>
<Typography>{project.name}</Typography> <Typography>{project.name}</Typography>
<Typography color="gray" variant="small"> <Typography color="gray" variant="small">
{project.domain} {project.url}
</Typography> </Typography>
</Link> </Link>
</div> </div>

View File

@ -27,6 +27,7 @@ const ProjectSearch = ({ onChange }: ProjectsSearchProps) => {
getInputProps, getInputProps,
getItemProps, getItemProps,
highlightedIndex, highlightedIndex,
inputValue,
} = useCombobox({ } = useCombobox({
onInputValueChange({ inputValue }) { onInputValueChange({ inputValue }) {
setItems( setItems(
@ -57,39 +58,47 @@ const ProjectSearch = ({ onChange }: ProjectsSearchProps) => {
<div className="relative"> <div className="relative">
<SearchBar {...getInputProps()} /> <SearchBar {...getInputProps()} />
<Card <Card
className={`absolute w-1/2 max-h-100 -mt-1 overflow-y-scroll ${ className={`absolute w-1/2 max-h-52 -mt-1 overflow-y-auto ${
!(isOpen && items.length) && 'hidden' (!inputValue || !isOpen) && 'hidden'
}`} }`}
> >
<List {...getMenuProps()}> <List {...getMenuProps()}>
<div className="p-3"> {items.length ? (
<Typography variant="small" color="gray"> <>
Suggestions <div className="p-3">
</Typography> <Typography variant="small" color="gray">
</div> Suggestions
{items.map((item, index) => (
<ListItem
selected={highlightedIndex === index || selectedItem === item}
key={item.id}
{...getItemProps({ item, index })}
>
<ListItemPrefix>
<i>^</i>
</ListItemPrefix>
<div>
<Typography variant="h6" color="blue-gray">
{item.title}
</Typography>
<Typography
variant="small"
color="gray"
className="font-normal"
>
{item.organization}
</Typography> </Typography>
</div> </div>
</ListItem> {items.map((item, index) => (
))} <ListItem
selected={highlightedIndex === index || selectedItem === item}
key={item.id}
{...getItemProps({ item, index })}
>
<ListItemPrefix>
<i>^</i>
</ListItemPrefix>
<div>
<Typography variant="h6" color="blue-gray">
{item.title}
</Typography>
<Typography
variant="small"
color="gray"
className="font-normal"
>
{item.organization}
</Typography>
</div>
</ListItem>
))}
</>
) : (
<div className="p-3">
<Typography>^ No projects matching this name</Typography>
</div>
)}
</List> </List>
</Card> </Card>
</div> </div>

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { IconButton } from '@material-tailwind/react'; import { Chip, IconButton } from '@material-tailwind/react';
import { relativeTime } from '../../../utils/time'; import { relativeTime } from '../../../utils/time';
@ -8,6 +8,7 @@ interface RepositoryDetails {
title: string; title: string;
updatedAt: string; updatedAt: string;
user: string; user: string;
private: boolean;
} }
interface ProjectRepoCardProps { interface ProjectRepoCardProps {
@ -19,9 +20,21 @@ const ProjectRepoCard: React.FC<ProjectRepoCardProps> = ({ repository }) => {
<div className="group flex items-center gap-4 text-gray-500 text-xs hover:bg-gray-100 p-2 cursor-pointer"> <div className="group flex items-center gap-4 text-gray-500 text-xs hover:bg-gray-100 p-2 cursor-pointer">
<div>^</div> <div>^</div>
<div className="grow"> <div className="grow">
<p className="text-black"> <div>
{repository.user}/{repository.title} <span className="text-black">
</p> {repository.user}/{repository.title}
</span>
{repository.private ? (
<Chip
className="normal-case inline ml-6 bg-[#FED7AA] text-[#EA580C] font-normal"
size="sm"
value="Private"
icon={'^'}
/>
) : (
''
)}
</div>
<p>{relativeTime(repository.updatedAt)}</p> <p>{relativeTime(repository.updatedAt)}</p>
</div> </div>
<div className="hidden group-hover:block"> <div className="hidden group-hover:block">

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Typography } from '@material-tailwind/react'; import { Typography, IconButton } from '@material-tailwind/react';
import { relativeTime } from '../../../utils/time'; import { relativeTime } from '../../../utils/time';
@ -18,7 +18,7 @@ interface ActivityCardProps {
const ActivityCard = ({ activity }: ActivityCardProps) => { const ActivityCard = ({ activity }: ActivityCardProps) => {
return ( return (
<div className="flex hover:bg-gray-200 rounded mt-1"> <div className="group flex hover:bg-gray-200 rounded mt-1">
<div className="w-4">{activity.authorAvatar}</div> <div className="w-4">{activity.authorAvatar}</div>
<div className="grow"> <div className="grow">
@ -30,6 +30,11 @@ const ActivityCard = ({ activity }: ActivityCardProps) => {
{activity.message} {activity.message}
</Typography> </Typography>
</div> </div>
<div className="mr-2 self-center hidden group-hover:block">
<IconButton size="sm" className="rounded-full bg-gray-600">
{'>'}
</IconButton>
</div>
</div> </div>
); );
}; };

View File

@ -3,7 +3,9 @@ import React, { useCallback, useMemo, useState } from 'react';
import { Button, Typography } from '@material-tailwind/react'; import { Button, Typography } from '@material-tailwind/react';
import deploymentData from '../../../assets/deployments.json'; import deploymentData from '../../../assets/deployments.json';
import DeployDetailsCard from './deployments/DeploymentDetailsCard'; import DeployDetailsCard, {
DeploymentDetails,
} from './deployments/DeploymentDetailsCard';
import FilterForm, { import FilterForm, {
FilterValue, FilterValue,
StatusOptions, StatusOptions,
@ -17,7 +19,7 @@ const DEFAULT_FILTER_VALUE: FilterValue = {
const DeploymentsTabPanel = () => { const DeploymentsTabPanel = () => {
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE); const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
const filteredDeployments = useMemo(() => { const filteredDeployments = useMemo<DeploymentDetails[]>(() => {
return deploymentData.filter((deployment) => { return deploymentData.filter((deployment) => {
// Searched branch filter // Searched branch filter
const branchMatch = const branchMatch =
@ -37,7 +39,7 @@ const DeploymentsTabPanel = () => {
new Date(deployment.updatedAt) <= filterValue.updateAtRange!.to!); new Date(deployment.updatedAt) <= filterValue.updateAtRange!.to!);
return branchMatch && statusMatch && dateMatch; return branchMatch && statusMatch && dateMatch;
}); }) as DeploymentDetails[];
}, [filterValue]); }, [filterValue]);
const handleResetFilters = useCallback(() => { const handleResetFilters = useCallback(() => {

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Typography } from '@material-tailwind/react'; import { Typography, Button, Chip } from '@material-tailwind/react';
import ActivityCard from './ActivityCard'; import ActivityCard from './ActivityCard';
import activityDetails from '../../../assets/activities.json'; import activityDetails from '../../../assets/activities.json';
@ -19,24 +19,40 @@ const OverviewTabPanel = ({ project }: OverviewProps) => (
<div className="grow"> <div className="grow">
<Typography>{project.name}</Typography> <Typography>{project.name}</Typography>
<Typography variant="small" color="gray"> <Typography variant="small" color="gray">
{project.domain} {project.url}
</Typography> </Typography>
</div> </div>
</div> </div>
<div className="flex justify-between p-2 text-sm"> <div className="flex justify-between p-2 text-sm items-center">
<p>Domain</p> <div>
{project.domain ? <p>{project.domain}</p> : <button>Set up</button>} ^ Domain
{!project.domain && (
<Chip
className="normal-case ml-6 bg-[#FED7AA] text-[#EA580C] inline font-normal"
size="sm"
value="Not connected"
icon="^"
/>
)}
</div>
{project.domain ? (
<p>{project.domain}</p>
) : (
<Button className="normal-case rounded-full" color="blue" size="sm">
Setup
</Button>
)}
</div> </div>
<div className="flex justify-between p-2 text-sm"> <div className="flex justify-between p-2 text-sm">
<p>Source</p> <p>^ Source</p>
<p>^ {project.source}</p> <p>{project.source}</p>
</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>{project.deployment} ^</p> <p className="text-blue-600">{project.deployment}</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} {relativeTime(project.createdAt)} by ^ {project.createdBy}
</p> </p>

View File

@ -6,13 +6,21 @@ import {
MenuList, MenuList,
MenuItem, MenuItem,
Typography, Typography,
Chip,
ChipProps,
} from '@material-tailwind/react'; } from '@material-tailwind/react';
import { relativeTime } from '../../../../utils/time'; import { relativeTime } from '../../../../utils/time';
interface DeploymentDetails { export enum Status {
BUILDING = 'Building',
READY = 'Ready',
ERROR = 'Error',
}
export interface DeploymentDetails {
title: string; title: string;
status: string; status: Status;
environment: string; environment: string;
branch: string; branch: string;
commit: { commit: {
@ -27,15 +35,24 @@ interface DeployDetailsCardProps {
deployment: DeploymentDetails; deployment: DeploymentDetails;
} }
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
[Status.BUILDING]: 'blue',
[Status.READY]: 'green',
[Status.ERROR]: 'red',
};
const DeployDetailsCard = ({ deployment }: DeployDetailsCardProps) => { const DeployDetailsCard = ({ deployment }: DeployDetailsCardProps) => {
return ( return (
<div className="grid grid-cols-4 gap-2 border-b border-gray-300"> <div className="grid grid-cols-4 gap-2 border-b border-gray-300 p-3 my-2">
<div className="col-span-2"> <div className="col-span-2">
<div className="flex"> <div className="flex">
<Typography className=" basis-2/3">{deployment.title}</Typography> <Typography className=" basis-3/4">{deployment.title}</Typography>
<Typography color="gray" className="basis-1/3"> <Chip
{deployment.status} value={deployment.status}
</Typography> color={STATUS_COLORS[deployment.status] ?? 'gray'}
variant="ghost"
icon={<i>^</i>}
/>
</div> </div>
<Typography color="gray">{deployment.environment}</Typography> <Typography color="gray">{deployment.environment}</Typography>
</div> </div>

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Button, IconButton, Typography } from '@material-tailwind/react'; import { Button, IconButton, Typography, Chip } from '@material-tailwind/react';
import ProjectCard from '../components/projects/ProjectCard'; import ProjectCard from '../components/projects/ProjectCard';
import HorizontalLine from '../components/HorizontalLine'; import HorizontalLine from '../components/HorizontalLine';
@ -26,12 +26,18 @@ const Projects = () => {
</div> </div>
</div> </div>
<HorizontalLine /> <HorizontalLine />
<div className="flex p-4"> <div className="flex p-5">
<div className="grow"> <div className="grow">
<Typography variant="h4">Projects</Typography> <div className="flex gap-2 items-center">
<Typography variant="h4">Projects</Typography>
<Chip
className="bg-gray-300 rounded-full static"
value={projectsDetail.length}
size="sm"
/>
</div>
</div> </div>
<div> <div>
{/* TODO: Create button component */}
<Link to="/projects/create"> <Link to="/projects/create">
<Button className="rounded-full" color="blue"> <Button className="rounded-full" color="blue">
Create project Create project

View File

@ -3,6 +3,7 @@ export interface ProjectDetails {
name: string; name: string;
title: string; title: string;
organization: string; organization: string;
url: string;
domain: string; domain: string;
id: number; id: number;
createdAt: string; createdAt: string;