如何在React组件中触发任何action时刷新所有API?

问题描述 投票:0回答:1
import AppTextInput from '@/components/form-fields/AppTextInput';
import Iconify from '@/components/iconify/Iconify';
import AppButton from '@/components/templates/AppButton';
import AppDataGrid from '@/components/templates/AppDataGrid';
import AppLink from '@/components/templates/AppLink';
import AppPersona from '@/components/templates/AppPersona';
import TableSkeleton from '@/components/templates/AppTable/TableSkeleton';
import AppTag from '@/components/templates/AppTag';
import AppTitle from '@/components/templates/AppTitle';
import {
  CACHE_KEY_AVAILABLE_PAYROLL,
  CACHE_KEY_GENERATED_PAYROLL,
  CACHE_KEY_PAYROLL_CYCLE_PAYRUN,
  CACHE_KEY_POSTED_PAYROLL,
  CACHE_KEY_PROCESSED_PAYROLL,
  PAYROLL_CYCLE_LOCK_ENDPOINT,
  PAYROLL_CYCLE_PAYRUN_ENDPOINT,
  PAYROLL_CYCLE_SIF_ENDPOINT,
  PAYROLL_EMP_BY_STATUS_ENDPOINT,
} from '@/constants/payroll';
import useGetAll from '@/hooks/useGetAll';
import {
  Box,
  Button,
  Divider,
  Grid,
  IconButton,
  Menu,
  MenuItem,
  Paper,
  Stack,
  Step,
  StepButton,
  Stepper,
  Typography,
} from '@mui/material';
import {
  GridColDef,
  GridRenderCellParams,
  GridRowSelectionModel,
} from '@mui/x-data-grid';
import * as React from 'react';
import { useLocation, useParams } from 'react-router-dom';
import StatusCardList from './status-card-list';
import ProcessPopup, { PAYROLL_UPDATE_PAYLOAD } from './process-popup';
import usePut from '@/hooks/usePut';
import AppDrawer from '@/components/templates/AppDrawer';
import PayView from './payview';
import { useState } from 'react';
import nav from '@/AppLayout/nav';
import APIClient from '@/services/api-client';
import { SearchOutlined } from '@mui/icons-material';
import { useQueryClient } from '@tanstack/react-query';
import { PAYROLL } from '../payroll-list';
import { toast } from 'react-toastify';
import useUserStore from '@/store/user.store';

export type PAYROLL_PROCESS =
  | 'AVAILABLE'
  | 'GENERATED'
  | 'PROCESSED'
  | 'POSTED';

interface STEP {
  id: PAYROLL_PROCESS;
  label: string;
}

const steps: STEP[] = [
  // { id: 'AVAILABLE', label: 'Available Payroll' }, //included this line by Priti
  { id: 'AVAILABLE', label: 'Available Employee' },
  { id: 'GENERATED', label: 'Generated Payroll' },
  { id: 'POSTED', label: 'Posted Payroll' },
  { id: 'PROCESSED', label: 'Processed Payroll' },
];

function Payrun() {
  const params = useParams();

  const [activeStep, setActiveStep] = React.useState<number>(0);
  const [rowSelectionModel, setRowSelectionModel] =
    React.useState<GridRowSelectionModel>([]);
  const [selectedRows, setSelectedRows] =
    React.useState<PAYROLL_UPDATE_PAYLOAD>();
  const [open, setOpen] = React.useState(false);
  const [deleteOpen, setDeleteOpen] = React.useState(false);

  const { data, refetch, error, isLoading, isFetching } =
    useGetAll<EMP_PAYROLL_STATUS>(
      PAYROLL_EMP_BY_STATUS_ENDPOINT +
        '?' +
        'payrollCycleId=' +
        params.id +
        '&status=' +
        steps[activeStep].id,
      activeStep === 0
        ? CACHE_KEY_AVAILABLE_PAYROLL
        : activeStep === 1
        ? CACHE_KEY_GENERATED_PAYROLL
        : activeStep === 2
        ? CACHE_KEY_POSTED_PAYROLL
        : CACHE_KEY_PROCESSED_PAYROLL
    );
  React.useEffect(() => {
    if (isFetching) {
      console.log('Refetching data...');
    }
  }, [isFetching]);
  const handleRowSelection = (rowIds: GridRowSelectionModel) => {
    setRowSelectionModel(rowIds);

    const rows = data && data.filter((d) => rowIds.includes(d.id));

    if (activeStep === 0)
      setSelectedRows({
        payrollCycleId: params.id!,
        employees: rows?.map((r) => r.id),
      });
    else
      setSelectedRows({
        payrollCycleId: params.id!,
        payrollId: rows?.map((r) => r.payrollId),
      });
  };

  const [menuAnchor, setMenuAnchor] = React.useState<HTMLElement | null>(null);

  const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {
    setMenuAnchor(event.currentTarget);
  };

  const handleMenuClose = () => {
    setMenuAnchor(null);
  };
  const getActionLabel = () => {
    switch (steps[activeStep].id) {
      case 'AVAILABLE':
        return ['Generate Payroll'];
      case 'GENERATED':
        return ['Post Payroll', 'Delete'];
      case 'POSTED':
        return ['Process Payroll', 'Delete'];
      case 'PROCESSED':
        return ['No Actions needed'];
      default:
        return ['Action'];
    }
  };
  const refetchTableData = () => {
    const queryClient = useQueryClient(); // Get the query client instance
    queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
    console.log('Table data refetched');
    refetch();
  };
  const user = useUserStore((state) => state.user);
  const columns: GridColDef[] = [
    {
      field: 'firstName',
      minWidth: 200,
      flex: 1,
      headerName: 'Employee Name',
      renderCell: (params: GridRenderCellParams) => {
        const { value, row }: { value?: string; row: any } = params;
        return (
          <AppPersona
            profile={{
              maintext: value + ' ' + row.lastName,
              subtext: '#' + row.empCode,
            }}
          />
        );
      },
    },
    {
      field: 'departmentId',
      minWidth: 200,
      flex: 1,
      headerName: 'Department & Division',
      renderCell: (params: GridRenderCellParams) => {
        const { value, row }: { value?: string; row: any } = params;
        return (
          <Box>
            <Typography
              variant='body2'
              fontWeight={'fontWeightMedium'}
              sx={{ color: 'text.primary' }}
            >
              {value!}
            </Typography>
            <Typography
              variant='caption'
              sx={{ color: 'text.secondary' }}
              fontWeight='fontWeightMedium'
            >
              {row.divisionId !}
            </Typography>
          </Box>
        );
        // return <AppTitle title={value!} subtitle={row.division!} />;
      },
    },
    // {
    //   field: 'salary',
    //   minWidth: 100,
    //   width: 150,
    //   headerName: 'Salary',
    //   renderCell: (params: GridRenderCellParams) => {
    //     return (
    //       <Typography variant='body2' fontWeight='fontWeightMedium'>
    //         INR. {params.value}
    //       </Typography>
    //     );
    //   },
    // },
    {
      field: 'status',
      minWidth: 80,
      width: 150,
      headerName: 'Status',
      renderCell: (params: GridRenderCellParams) => {
        const val = params?.value?.toUpperCase() || 'NA'!;
        return (
          <AppTag
            variant={
              val === 'GENERATED'
                ? 'ACTIVE'
                : val === 'POSTED'
                ? 'INACTIVE'
                : ''
            }
            text={val}
          />
        );
      },
    },
    {
      field: 'action',
      flex: 1,
      headerAlign: 'center',
      align: 'center',
      headerName: 'Action',
      renderCell: (params: GridRenderCellParams) => {
        const { row }: { row: any } = params;
        return (
          <Stack
            sx={{ width: '70px' }}
            direction='row'
            alignItems='center'
            justifyContent='space-between'
          >
            <AppButton
              sx={{
                color: 'primary.main',
                fontWeight: 600,
                textDecoration: 'none',
                padding: '4px 10px',
                display: 'inline-flex',
                alignItems: 'center',
              }}
              variant='text'
              onClick={() => handleOpenDrawer(row?.payrollId)}
            >
              View
            </AppButton>
            {/* <Iconify icon='ic:sharp-more-vert' width={22} /> */}
            {/* <IconButton onClick={handleMenuClick}>
              <Iconify icon='ic:sharp-more-vert' width={18} />
            </IconButton>
            <Menu
              anchorEl={menuAnchor}
              open={Boolean(menuAnchor)}
              onClose={handleMenuClose}
            >
              {getActionLabel().map((label, index) => (
                <MenuItem
                  key={index}
                  onClick={() => handleMenuItemClick(label)}
                >
                  {label}
                </MenuItem>
              ))}
            </Menu> */}
          </Stack>
        );
      },
    },
  ];
  const [viewTitle, setViewTitle] = useState('');
  const [isDrawerOpen, setIsDrawerOpen] = useState<string>(''); // New state to track drawer open/close

  const { id } = useParams();
  // Function to open the drawer
  const handleOpenDrawer = (id: string) => {
    setIsDrawerOpen(id);
  };

  // Function to close the drawer
  const handleCloseDrawer = () => {
    setIsDrawerOpen('');
  };
  React.useEffect(() => {
    if (id) {
      setIsDrawerOpen('');
    }
  }, [id]);

  const downloadSif = () => {
    const apiClient = new APIClient(PAYROLL_CYCLE_SIF_ENDPOINT(params.id!));
    apiClient.get(true).then((data) => {
      console.log(data);
      const fileURL = URL.createObjectURL(data);
      var link = document.createElement('a');
      link.setAttribute('href', fileURL);
      const dt = new Date();
      const yr = dt.getFullYear().toString().slice(-2);
      const mn = (dt.getMonth() + 1).toString().padStart(2, '0');
      const dd = dt.getDate().toString().padStart(2, '0');

      const hr = dt.getHours().toString().padStart(2, '0');
      const min = dt.getMinutes().toString().padStart(2, '0');
      const sec = dt.getSeconds().toString().padStart(2, '0');
      link.setAttribute(
        'download',
        `${'0000001145097'}${yr + mn + dd + hr + min + sec}.txt`
      );
      document.body.appendChild(link); // Required for FF

      link.click();
      link.remove();
    });
  };

  const lockPayroll = (payrollCycleId: string) => {
    const service = new APIClient(PAYROLL_CYCLE_LOCK_ENDPOINT + params.id);
    service
      .put({ id: payrollCycleId })
      .then((data) => {
        // Invalidate relevant queries
        queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
        toast.success('Payroll locked !');
        refetch();
      })
      .catch((error) => {
        // Handle error
        console.error('Error making employee inactive:', error);
        toast.error('Failed to lock payroll');
      });
  };
  const queryClient = useQueryClient();
  // const onEdit = (editData: PAYROLL) => {
  //   queryClient.setQueryData<PAYROLL>(CACHE_KEY_PAYROLL_CYCLE_PAYRUN, editData);
  // };
  const location = useLocation();

  const { state } = location;
  console.log(state, 'state');
  return (
    <Paper
      sx={{
        border: '1px solid',
        borderColor: 'background.neutral',
        backgroundColor: 'background.paper',
        borderRadius: 2.5,
        m: 4,
        overflow: 'auto',
      }}
    >
      <Grid container direction='row' sx={{ height: '100%' }}>
        <Grid item xs={12}>
          <Stack sx={{ height: '100%' }} divider={<Divider />}>
            <Stack
              sx={{ px: 2.5, pt: 2.5, pb: 4 }}
              direction='row'
              justifyContent='space-between'
            >
              <AppTitle
                title={'Payroll Cycle Details  '}
                subtitle={`${state?.dateFrom} - ${state?.dateTo}`}
              />

              <Button
                variant='outlined'
                onClick={() => {
                  if (params.id) {
                    lockPayroll(params.id);
                  } else {
                    console.error('PayrollCycleId is undefined');
                    toast.error(
                      'Failed to lock payroll: PayrollCycleId is undefined'
                    );
                  }
                }}
              >
                <Iconify icon='ic:round-lock' width={18} />
                &nbsp;&nbsp;Lock Payroll
              </Button>
            </Stack>
            <Box>
              <StatusCardList />
              <Stepper sx={{ pt: 2, px: 1 }} nonLinear activeStep={activeStep}>
                {steps.map((step, index) => (
                  <Step key={step.label}>
                    <StepButton onClick={() => setActiveStep(index)}>
                      {step.label}
                    </StepButton>
                  </Step>
                ))}
              </Stepper>
              <Box sx={{ mt: 5 }}>
                <Stack
                  px={2}
                  direction='row'
                  justifyContent='space-between'
                  alignItems='center'
                >
                  <AppTitle title={'List of Payrolls'} />
                  <Stack direction='row' spacing={2} sx={{ width: '50%' }}>
                    <AppTextInput
                      startIcon={<SearchOutlined />}
                      placeholder='Search by Request Id, Emp no'
                    />
                    <IconButton onClick={() => {}}>
                      <Iconify icon='mdi:filter-outline' width={18} />
                    </IconButton>
                    {activeStep !== 3 && (
                      <AppButton
                        onClick={() => {
                          setOpen(true); // Open the dialog or popup for the action
                          // Assuming the action is completed successfully and you want to update the table
                          // Call refetch to fetch the latest data from the API
                          refetch();
                          console.log('Refetching data...');
                        }}
                        variant='contained'
                        disabled={!rowSelectionModel.length}
                        disableRipple
                      >
                        {activeStep === 0
                          ? 'Generate '
                          : activeStep === 1
                          ? 'Post '
                          : 'Process '}
                        Payroll
                      </AppButton>
                    )}
                    {activeStep === 3 && (
                      <Button
                        variant='text'
                        onClick={downloadSif}
                        startIcon={
                          <Iconify
                            icon='ic:baseline-cloud-download'
                            width={16}
                          />
                        }
                      >
                        SIF Download
                      </Button>
                    )}

                    {activeStep === 1 && (
                      <Button
                        variant='text'
                        disabled={!rowSelectionModel.length}
                        startIcon={<Iconify icon='ic:delete' width={16} />}
                        onClick={() => {
                          setDeleteOpen(true);
                        }}
                      >
                        Delete
                      </Button>
                    )}
                  </Stack>
                </Stack>
                <Box sx={{ mt: 2 }}>
                  {data && !isLoading ? (
                    <AppDataGrid
                      columns={columns}
                      rows={data}
                      rowHeight={75}
                      getRowId={(row: EMP_PAYROLL_STATUS) => row.id}
                      checkboxSelection={activeStep !== 3}
                      onRowSelectionModelChange={(
                        newRowSelectionModel: GridRowSelectionModel
                      ) => {
                        handleRowSelection(newRowSelectionModel);
                      }}
                      rowSelectionModel={rowSelectionModel}
                    />
                  ) : (
                    <TableSkeleton columns={columns} />
                  )}
                </Box>
              </Box>
            </Box>
          </Stack>
        </Grid>
      </Grid>
      {selectedRows && (
        <>
          <ProcessPopup
            data={selectedRows}
            process={steps[activeStep].id}
            open={open}
            setOpen={setOpen}
            refetchTableData={refetchTableData}
          />
          {activeStep == 1 && (
            <ProcessPopup
              data={selectedRows}
              process={'DELETE'}
              open={deleteOpen}
              setOpen={setDeleteOpen}
              refetchTableData={refetchTableData}
            />
          )}
        </>
      )}

      <AppDrawer
        header={viewTitle || 'View Leave'}
        open={!!isDrawerOpen} // Use the state to control drawer open/close
        setOpen={handleCloseDrawer} // Pass the close function to the AppDrawer
      >
        {isDrawerOpen && <PayView setTitle={setViewTitle} id={isDrawerOpen!} />}
      </AppDrawer>
    </Paper>
  );
}

export default React.memo(Payrun);

interface EMP_PAYROLL_STATUS {
  id: string;
  payrollId: string;
  payrollCycleId: string;
  title: string;
  firstName: string;
  lastName: string;
  empCode: string;
  departmentId: string;
  divisionId: string;
  salary: number;
  status: PAYROLL_PROCESS;
  payMode: string;
}

这里我已经使用“queryClient”进行参考技术,但它不起作用使用 queryclient 刷新所有 api 的解决方案是什么

  const refetchTableData = () => {
    const queryClient = useQueryClient(); // Get the query client instance
    queryClient.invalidateQueries(CACHE_KEY_PAYROLL_CYCLE_PAYRUN);
    console.log('Table data refetched');
    refetch();
  };

使用 queryclient 创建了函数,但它不起作用,请帮助我使用查询客户端刷新所有 api,请更正我的代码和逻辑,提前感谢朋友们, 我的错误是什么,请也提供我在这里犯的错误。

javascript reactjs react-query
1个回答
0
投票

请问,告诉我这个const“CACHE_KEY_PAYROLL_CYCLE_PAYRUN”是什么值?如果它的值等于 {queryKey: ["something_key"]},那么就可以,否则就不好,你应该更正为 {queryKey: ["somethig_key"]} https://tanstack.com/query/v4/docs/framework/react/guides/query-invalidation

© www.soinside.com 2019 - 2024. All rights reserved.