Filter deployments with status options (#15)

This commit is contained in:
Nabarun Gogoi 2023-12-20 16:43:27 +05:30 committed by GitHub
parent cfb299c79e
commit 318ebdfd26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 54 deletions

View File

@ -1,16 +1,10 @@
import React, { ChangeEventHandler, forwardRef } from 'react'; import React, { forwardRef } from 'react';
import { Input } from '@material-tailwind/react'; import { Input, InputProps } from '@material-tailwind/react';
interface SearchBarProps {
onChange?: ChangeEventHandler<HTMLInputElement>;
value?: string;
placeholder?: string;
}
const SearchBar: React.ForwardRefRenderFunction< const SearchBar: React.ForwardRefRenderFunction<
HTMLInputElement, HTMLInputElement,
SearchBarProps InputProps
> = ({ value, onChange, placeholder = 'Search', ...props }, ref) => { > = ({ value, onChange, placeholder = 'Search', ...props }, ref) => {
return ( return (
<div className="relative flex w-full gap-2"> <div className="relative flex w-full gap-2">
@ -28,8 +22,8 @@ const SearchBar: React.ForwardRefRenderFunction<
}} }}
// TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427 // TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427
crossOrigin={undefined} crossOrigin={undefined}
ref={ref}
{...props} {...props}
ref={ref}
/> />
<div className="!absolute left-3 top-[13px]"> <div className="!absolute left-3 top-[13px]">
<i>^</i> <i>^</i>

View File

@ -1,60 +1,47 @@
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { Button } 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 './DeploymentDetailsCard'; import DeployDetailsCard from './deployments/DeploymentDetailsCard';
import Dropdown from '../../Dropdown'; import FilterForm, { StatusOptions } from './deployments/FilterForm';
import SearchBar from '../../SearchBar';
const STATUS_OPTIONS = [ const DEFAULT_FILTER_VALUE = {
{ value: 'building', label: 'Building' }, searchedBranch: '',
{ value: 'ready', label: 'Ready' }, status: 'All status',
{ value: 'error', label: 'Error' }, };
];
const DeploymentsTabPanel = () => { const DeploymentsTabPanel = () => {
const [searchedBranch, setSearchedBranch] = useState(''); const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
const filteredDeployments = useMemo(() => { const filteredDeployments = useMemo(() => {
if (searchedBranch) { return deploymentData.filter((deployment) => {
return deploymentData.filter((deployment) => // Searched branch filter
deployment.branch.toLowerCase().includes(searchedBranch.toLowerCase()), const branchMatch =
); !filterValue.searchedBranch ||
} deployment.branch
.toLowerCase()
.includes(filterValue.searchedBranch.toLowerCase());
return deploymentData; // Status filter
}, [searchedBranch]); const statusMatch =
filterValue.status === StatusOptions.ALL_STATUS ||
deployment.status === filterValue.status;
return branchMatch && statusMatch;
});
}, [filterValue]);
const handleResetFilters = useCallback(() => { const handleResetFilters = useCallback(() => {
setSearchedBranch(''); setFilterValue(DEFAULT_FILTER_VALUE);
}, []); }, []);
return ( return (
<div className="p-4"> <div className="p-4">
<div className="grid grid-cols-4 gap-2 text-sm text-gray-600"> <FilterForm
<div className="col-span-2"> value={filterValue}
<SearchBar onChange={(value) => setFilterValue(value)}
placeholder="Search branches"
value={searchedBranch}
onChange={(event) => setSearchedBranch(event.target.value)}
/> />
</div>
<div className="col-span-1">
<input
type="text"
className="border border-gray-300 rounded p-2 w-full focus:border-blue-300 focus:outline-none focus:shadow-outline-blue"
placeholder="All time"
/>
</div>
<div className="col-span-1">
<Dropdown
placeholder="All status"
options={STATUS_OPTIONS}
onChange={() => {}}
/>
</div>
</div>
<div className="mt-2"> <div className="mt-2">
{Boolean(filteredDeployments.length) ? ( {Boolean(filteredDeployments.length) ? (
filteredDeployments.map((deployment, key) => { filteredDeployments.map((deployment, key) => {
@ -63,8 +50,10 @@ const DeploymentsTabPanel = () => {
) : ( ) : (
<div className="h-[50vh] bg-gray-100 flex rounded items-center justify-center"> <div className="h-[50vh] bg-gray-100 flex rounded items-center justify-center">
<div className="text-center"> <div className="text-center">
<h5 className="text-lg font-bold">No deployments found</h5> <Typography variant="h5">No deployments found</Typography>
<p>Please change your search query or filters</p> <Typography>
Please change your search query or filters
</Typography>
<Button <Button
className="rounded-full mt-5" className="rounded-full mt-5"
color="white" color="white"

View File

@ -7,7 +7,7 @@ import {
MenuItem, MenuItem,
} from '@material-tailwind/react'; } from '@material-tailwind/react';
import { relativeTime } from '../../../utils/time'; import { relativeTime } from '../../../../utils/time';
interface DeploymentDetails { interface DeploymentDetails {
title: string; title: string;

View File

@ -0,0 +1,77 @@
import React, { useEffect, useState } from 'react';
import { Option, Select } from '@material-tailwind/react';
import SearchBar from '../../../SearchBar';
export enum StatusOptions {
ALL_STATUS = 'All status',
BUILDING = 'Building',
READY = 'Ready',
ERROR = 'Error',
}
interface FilterValue {
searchedBranch: string;
status: string;
}
interface FilterFormProps {
value: FilterValue;
onChange: (value: FilterValue) => void;
}
const FilterForm = ({ value, onChange }: FilterFormProps) => {
const [searchedBranch, setSearchedBranch] = useState(value.searchedBranch);
const [selectedStatus, setSelectedStatus] = useState(value.status);
useEffect(() => {
onChange({
searchedBranch,
status: selectedStatus,
});
}, [searchedBranch, selectedStatus]);
useEffect(() => {
setSearchedBranch(value.searchedBranch);
setSelectedStatus(value.status);
}, [value]);
return (
<div className="grid grid-cols-4 gap-2 text-sm text-gray-600">
<div className="col-span-2">
<SearchBar
placeholder="Search branches"
value={searchedBranch}
onChange={(event) => setSearchedBranch(event.target.value)}
/>
</div>
<div className="col-span-1">
<input
type="text"
className="border border-gray-300 rounded p-2 w-full focus:border-blue-300 focus:outline-none focus:shadow-outline-blue"
placeholder="All time"
/>
</div>
<div className="col-span-1">
<Select
value={selectedStatus}
onChange={(value) => setSelectedStatus(value!)}
label="Select Version"
>
{Object.values(StatusOptions).map((status) => (
<Option
className={status === selectedStatus ? 'hidden' : ''}
key={status}
value={status}
>
^ {status}
</Option>
))}
</Select>
</div>
</div>
);
};
export default FilterForm;