Reconmap/web-client

View on GitHub
src/components/projects/List.jsx

Summary

Maintainability
B
4 hrs
Test Coverage
import { Flex, HStack, Select } from '@chakra-ui/react';
import PaginationV2 from 'components/layout/PaginationV2';
import PageTitle from 'components/logic/PageTitle';
import RestrictedComponent from 'components/logic/RestrictedComponent';
import useQuery from 'hooks/useQuery';
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import secureApiFetch from 'services/api';
import useDelete from '../../hooks/useDelete';
import Breadcrumb from '../ui/Breadcrumb';
import CreateButton from '../ui/buttons/Create';
import { IconFolder } from '../ui/Icons';
import Title from '../ui/Title';
import ProjectsTable from './Table';

const ProjectsList = () => {
    const navigate = useNavigate();
    const query = useQuery();
    let pageNumber = query.get('page');
    pageNumber = pageNumber !== null ? parseInt(pageNumber) : 1;
    const apiPageNumber = pageNumber - 1;

    const [numberPages, setNumberPages] = useState(1);
    const [totalCount, setTotalCount] = useState('?');

    const [projects, setProjects] = useState([]);
    const [statusFilter, setStatusFilter] = useState('active');

    const handleCreateProject = () => {
        navigate('/projects/create')
    }

    const onStatusFilterChange = ev => {
        setStatusFilter(ev.target.value);
    }

    const onPageChange = pageNumber => {
        const queryParams = new URLSearchParams();
        queryParams.set('page', pageNumber + 1);
        const url = `/projects?${queryParams.toString()}`;
        navigate(url);
    }

    const reloadProjects = useCallback(() => {
        setProjects(null);

        const queryParams = new URLSearchParams();
        queryParams.set('page', apiPageNumber);
        if (statusFilter.length) {
            queryParams.set('status', statusFilter);
        }
        const url = `/projects?${queryParams.toString()}`;

        secureApiFetch(url)
            .then(resp => {
                if (resp.headers.has('X-Page-Count')) {
                    setNumberPages(resp.headers.get('X-Page-Count'))
                }
                if (resp.headers.has('X-Total-Count')) {
                    setTotalCount(resp.headers.get('X-Total-Count'))
                }
                return resp.json()
            })
            .then(projects => {
                setProjects(projects);
            });
    }, [apiPageNumber, statusFilter])

    const destroy = useDelete('/projects/', reloadProjects);

    useEffect(() => {
        reloadProjects();
    }, [statusFilter, reloadProjects]);

    return <div>
        <PageTitle value="Projects" />
        <div className='heading'>
            <Breadcrumb />
            <PaginationV2 page={apiPageNumber} total={numberPages} onPageChange={onPageChange} />

            <HStack>
                <RestrictedComponent roles={['administrator', 'superuser', 'user']}>
                    <CreateButton onClick={handleCreateProject}>Create project</CreateButton>
                </RestrictedComponent>
            </HStack>
        </div>
        <Title title={`Projects (${totalCount})`} icon={<IconFolder />} />

        <Flex>
            <details>
                <summary>Filters</summary>
                <div className='space-x-2 mx-auto flex items-center'>
                    <div>
                        <label>Status
                            <Select onChange={onStatusFilterChange} defaultValue="active">
                                <option value="">(any)</option>
                                <option value="active">Active</option>
                                <option value="archived">Archived</option>
                            </Select>
                        </label>
                    </div>
                </div>
            </details>
        </Flex>
        <ProjectsTable projects={projects} destroy={destroy} />
    </div>
}

export default ProjectsList;