messy react file

9. kesäkuuta 2022 | 29 näyttökertaa

import {
  Button,
  Center,
  createStyles,
  Group,
  ScrollArea,
  Table,
  Text,
  TextInput,
  UnstyledButton,
} from '@mantine/core';
import { useModals } from '@mantine/modals';
import { showNotification } from '@mantine/notifications';
import React, { useState } from 'react';
import { ChevronDown, ChevronUp, Search, Selector, Trash } from 'tabler-icons-react';
import { create_license, delete_license } from '../../data/licensingAPI';
import { IClient, ILicense, IProject } from '../../types';
import CreateNewLicense from '../CreateNewLicense/CreateNewLicense';

const useStyles = createStyles((theme) => ({
  th: {
    padding: '0 !important',
  },

  control: {
    width: '100%',
    padding: `${theme.spacing.xs}px ${theme.spacing.md}px`,

    '&:hover': {
      backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
    },
  },

  icon: {
    width: 21,
    height: 21,
    borderRadius: 21,
  },
}));

interface TableSortProps {
  data: ILicense[];
  clients: IClient[];
  projects: IProject[];
}

interface ThProps {
  children: React.ReactNode;
  reversed: boolean;
  sorted: boolean;
  onSort(): void;
}

function Th({ children, reversed, sorted, onSort }: ThProps) {
  const { classes } = useStyles();
  const Icon = sorted ? (reversed ? ChevronUp : ChevronDown) : Selector;
  return (
    <th className={classes.th}>
      <UnstyledButton onClick={onSort} className={classes.control}>
        <Group position="apart">
          <Text weight={500} size="sm">
            {children}
          </Text>
          <Center className={classes.icon}>
            <Icon size={14} />
          </Center>
        </Group>
      </UnstyledButton>
    </th>
  );
}

function filterData(data: ILicense[], search: string) {
  const keys = Object.keys(data[0]);
  const query = search.toLowerCase().trim();
  return data.filter((item: any) =>
    // eslint-disable-next-line array-callback-return, consistent-return
    keys.some((key) => {
      if (key === 'client') {
        return item.client.name.toLowerCase().includes(query);
      }

      if (key === 'project') {
        return item.project.name.toLowerCase().includes(query);
      }

      if (typeof item[key] === 'string') {
        return item[key].toLowerCase().includes(query);
      }
      return false;
    })
  );
}

function sortData(
  data: ILicense[],
  payload: { sortBy: keyof ILicense | null; reversed: boolean; search: string }
) {
  if (!payload.sortBy || payload.sortBy == null) {
    return filterData(data, payload.search);
  }

  return filterData(
    [...data].sort((a: any, b: any) => {
      if ((payload.sortBy as any) === 'client.name') {
        if (payload.reversed) {
          return b.client.name.localeCompare(a.client.name);
        }

        return a.client.name.localeCompare(b.client.name);
      }
      if ((payload.sortBy as any) === 'project.name') {
        if (payload.reversed) {
          return b.project.name.localeCompare(a.project.name);
        }

        return a.project.name.localeCompare(b.project.name);
      }

      if (payload.reversed) {
        return b[payload.sortBy!].localeCompare(a[payload.sortBy!]);
      }

      return a[payload.sortBy!].localeCompare(b[payload.sortBy!]);
    }),
    payload.search
  );
}

export function DashboardTable({ data, projects, clients }: TableSortProps) {
  const [search, setSearch] = useState('');
  const [sortedData, setSortedData] = useState(data);
  const [sortBy, setSortBy] = useState<keyof ILicense | null>(null);
  const [reverseSortDirection, setReverseSortDirection] = useState(false);
  const modals = useModals();

  const setSorting = (field: keyof ILicense) => {
    const reversed = field === sortBy ? !reverseSortDirection : false;
    setReverseSortDirection(reversed);
    setSortBy(field);
    setSortedData(sortData(data, { sortBy: field, reversed, search }));
  };

  const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = event.currentTarget;

    setSearch(value);
    setSortedData(sortData(data, { sortBy, reversed: reverseSortDirection, search: value }));
  };

  const createNewLicense = async (
    values: { license?: string; client: string; project: string; max_ips?: number },
    id: any
  ) => {
    modals.closeModal(id);
    const result = await create_license(values);

    if ((result as any).code || (result as any).error) {
      showNotification({
        title: (result as any).error,
        message: (result as any).message,
        color: 'red',
        autoClose: 5000,
      });
    } else {
      showNotification({
        title: 'Successfully',
        message: 'Successfully created new license',
        color: 'green',
      });
      setSearch('');
      const clie = clients.find((clien) => clien.id === values.client)!;
      const proj = projects.find((proje) => proje.id === values.project)!;
      setSortedData([
        ...data,
        {
          ...result,
          client: clie,
          project: proj,
        },
      ]);
    }
  };

  const openCreateModal = () => {
    const id = modals.openModal({
      title: 'Create New License',
      children: (
        <CreateNewLicense
          createNewLicense={(values) => createNewLicense(values, id)}
          clients={clients}
          projects={projects}
        />
      ),
    });
  };

  const openConfirmModal = (row: ILicense) =>
    modals.openConfirmModal({
      title: 'Are you sure that you want to delete this license',
      children: (
        <Text size="sm">If you delete &apos;{row.license}&apos;, you cannot undo the action.</Text>
      ),
      labels: { confirm: 'Confirm', cancel: 'Cancel' },
      onCancel: () =>
        showNotification({
          title: 'Cancelled',
          message: `Cancelled deletetion of '${row.license}'`,
          color: 'red',
        }),
      onConfirm: async () => {
        const res = await delete_license(row.id);
        if (!res.success) {
          showNotification({
            title: 'Failed to delete license',
            message: res,
            color: 'red',
          });
          return;
        }

        setSortedData(sortedData.filter((item) => item.id !== row.id));
        showNotification({
          title: 'Deleted license',
          message: `Successfully deleted license '${row.license}'`,
          color: 'green',
          autoClose: 3600,
        });
      },
    });

  const rows = sortedData.map((row) => (
    <tr key={row.id}>
      <td>{row.license}</td>
      <td>{row.client.name}</td>
      <td>{row.project.name}</td>
      <td>{row.max_ips}</td>
      <td>
        <Button color="red" variant="outline" onClick={() => openConfirmModal(row)}>
          <Trash size={18} />
        </Button>
      </td>
    </tr>
  ));

  return (
    <ScrollArea>
      <Group>
        <TextInput
          placeholder="Search by any field"
          mb="md"
          icon={<Search size={14} />}
          value={search}
          onChange={handleSearchChange}
        />
        <Button onClick={openCreateModal} mb="md">
          Add
        </Button>
      </Group>
      <Table
        horizontalSpacing="md"
        verticalSpacing="xs"
        sx={{ tableLayout: 'fixed', width: '100%' }}
      >
        <thead>
          <tr>
            <Th
              sorted={sortBy === 'license'}
              reversed={reverseSortDirection}
              onSort={() => setSorting('license')}
            >
              License
            </Th>
            <Th
              sorted={sortBy === ('client.name' as any)}
              reversed={reverseSortDirection}
              onSort={() => setSorting('client.name' as any)}
            >
              Client
            </Th>
            <Th
              sorted={sortBy === ('project.name' as any)}
              reversed={reverseSortDirection}
              onSort={() => setSorting('project.name' as any)}
            >
              Project
            </Th>
            <Th
              sorted={sortBy === 'max_ips'}
              reversed={reverseSortDirection}
              onSort={() => setSorting('max_ips')}
            >
              Max IPs
            </Th>
            <th>
              <Text>Manage</Text>
            </th>
          </tr>
        </thead>
        <tbody>
          {rows.length > 0 ? (
            rows
          ) : (
            <tr>
              <td colSpan={4}>
                <Text weight={500} align="center">
                  Nothing found
                </Text>
              </td>
            </tr>
          )}
        </tbody>
      </Table>
    </ScrollArea>
  );
}