Filter deployments with status options (#15)
This commit is contained in:
parent
cfb299c79e
commit
318ebdfd26
@ -1,16 +1,10 @@
|
||||
import React, { ChangeEventHandler, forwardRef } from 'react';
|
||||
import React, { forwardRef } from 'react';
|
||||
|
||||
import { Input } from '@material-tailwind/react';
|
||||
|
||||
interface SearchBarProps {
|
||||
onChange?: ChangeEventHandler<HTMLInputElement>;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
import { Input, InputProps } from '@material-tailwind/react';
|
||||
|
||||
const SearchBar: React.ForwardRefRenderFunction<
|
||||
HTMLInputElement,
|
||||
SearchBarProps
|
||||
InputProps
|
||||
> = ({ value, onChange, placeholder = 'Search', ...props }, ref) => {
|
||||
return (
|
||||
<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
|
||||
crossOrigin={undefined}
|
||||
ref={ref}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
<div className="!absolute left-3 top-[13px]">
|
||||
<i>^</i>
|
||||
|
@ -1,60 +1,47 @@
|
||||
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 DeployDetailsCard from './DeploymentDetailsCard';
|
||||
import Dropdown from '../../Dropdown';
|
||||
import SearchBar from '../../SearchBar';
|
||||
import DeployDetailsCard from './deployments/DeploymentDetailsCard';
|
||||
import FilterForm, { StatusOptions } from './deployments/FilterForm';
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ value: 'building', label: 'Building' },
|
||||
{ value: 'ready', label: 'Ready' },
|
||||
{ value: 'error', label: 'Error' },
|
||||
];
|
||||
const DEFAULT_FILTER_VALUE = {
|
||||
searchedBranch: '',
|
||||
status: 'All status',
|
||||
};
|
||||
|
||||
const DeploymentsTabPanel = () => {
|
||||
const [searchedBranch, setSearchedBranch] = useState('');
|
||||
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
|
||||
|
||||
const filteredDeployments = useMemo(() => {
|
||||
if (searchedBranch) {
|
||||
return deploymentData.filter((deployment) =>
|
||||
deployment.branch.toLowerCase().includes(searchedBranch.toLowerCase()),
|
||||
);
|
||||
}
|
||||
return deploymentData.filter((deployment) => {
|
||||
// Searched branch filter
|
||||
const branchMatch =
|
||||
!filterValue.searchedBranch ||
|
||||
deployment.branch
|
||||
.toLowerCase()
|
||||
.includes(filterValue.searchedBranch.toLowerCase());
|
||||
|
||||
return deploymentData;
|
||||
}, [searchedBranch]);
|
||||
// Status filter
|
||||
const statusMatch =
|
||||
filterValue.status === StatusOptions.ALL_STATUS ||
|
||||
deployment.status === filterValue.status;
|
||||
|
||||
return branchMatch && statusMatch;
|
||||
});
|
||||
}, [filterValue]);
|
||||
|
||||
const handleResetFilters = useCallback(() => {
|
||||
setSearchedBranch('');
|
||||
setFilterValue(DEFAULT_FILTER_VALUE);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<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">
|
||||
<Dropdown
|
||||
placeholder="All status"
|
||||
options={STATUS_OPTIONS}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<FilterForm
|
||||
value={filterValue}
|
||||
onChange={(value) => setFilterValue(value)}
|
||||
/>
|
||||
<div className="mt-2">
|
||||
{Boolean(filteredDeployments.length) ? (
|
||||
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="text-center">
|
||||
<h5 className="text-lg font-bold">No deployments found</h5>
|
||||
<p>Please change your search query or filters</p>
|
||||
<Typography variant="h5">No deployments found</Typography>
|
||||
<Typography>
|
||||
Please change your search query or filters
|
||||
</Typography>
|
||||
<Button
|
||||
className="rounded-full mt-5"
|
||||
color="white"
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
MenuItem,
|
||||
} from '@material-tailwind/react';
|
||||
|
||||
import { relativeTime } from '../../../utils/time';
|
||||
import { relativeTime } from '../../../../utils/time';
|
||||
|
||||
interface DeploymentDetails {
|
||||
title: string;
|
@ -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;
|
Loading…
Reference in New Issue
Block a user