Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/frontend/src/hooks/rules.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ export const useUpdateRuleset = () => {
{
onSuccess: () => {
queryClient.invalidateQueries(['rulesets']);
queryClient.invalidateQueries(['rules', 'rulesetTypes']);
}
}
);
Expand All @@ -454,6 +455,7 @@ export const useDeleteRuleset = () => {
{
onSuccess: () => {
queryClient.invalidateQueries(['rulesets']);
queryClient.invalidateQueries(['rules', 'rulesetTypes']);
}
}
);
Expand All @@ -467,7 +469,7 @@ export const useDeleteRulesetType = () => {
},
{
onSuccess: () => {
queryClient.invalidateQueries(['rulesetTypes']);
queryClient.invalidateQueries(['rules', 'rulesetTypes']);
}
}
);
Expand All @@ -490,6 +492,7 @@ export const useCreateRuleset = () => {
{
onSuccess: () => {
queryClient.invalidateQueries(['rulesets']);
queryClient.invalidateQueries(['rules', 'rulesetTypes']);
}
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ConfluenceLink from './ProjectsConfig/ConfluenceLink';
import PartReviewSampleImage from './ProjectsConfig/PartReviewSampleImage';
import CommonMistakesTable from './ProjectsConfig/CommonMistakesTable';
import PartTagsTable from './ProjectsConfig/PartTagsTable';
import RulesetTypesTable from './ProjectsConfig/RulesetTypesTable';

const AdminToolsProjectsConfig: React.FC = () => {
return (
Expand All @@ -35,6 +36,10 @@ const AdminToolsProjectsConfig: React.FC = () => {
Cars Config
</Typography>
<CarsTable />
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor={'white'}>
Rules Config
</Typography>
<RulesetTypesTable />
<Typography variant="h5" gutterBottom borderBottom={1} color="#ef4345" borderColor={'white'}>
Links Config
</Typography>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FormControl, FormHelperText, FormLabel, TextField } from '@mui/material';
import { Box } from '@mui/system';
import { FormControl, FormHelperText, FormLabel } from '@mui/material';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { Controller, useForm } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import NERFormModal from '../../../components/NERFormModal';
import ReactHookTextField from '../../../components/ReactHookTextField';
import { useToast } from '../../../hooks/toasts.hooks';

interface RulesetTypeFormData {
Expand All @@ -16,15 +16,6 @@ interface AddRulesetTypeModalProps {
onFormSubmit: (data: RulesetTypeFormData) => Promise<void>;
}

const sectionHeaderStyle = {
fontWeight: 'bold',
color: '#ef4345',
textDecoration: 'underline',
fontSize: '1rem',
textUnderlineOffset: '5px',
marginBottom: '10px'
};

const schema = yup.object({
name: yup.string().required('Name is required')
});
Expand Down Expand Up @@ -77,26 +68,11 @@ const AddRulesetTypeModal: React.FC<AddRulesetTypeModalProps> = ({ open, onHide,
formId={'add-ruleset-type-form'}
showCloseButton
>
<Box>
<FormControl fullWidth error={!!errors.name}>
<FormLabel sx={sectionHeaderStyle}>Name Ruleset:</FormLabel>
<Controller
name="name"
control={control}
render={({ field }) => (
<TextField
{...field}
autoComplete="off"
placeholder="Name Ruleset"
error={!!errors.name}
fullWidth
sx={{ minWidth: '400px' }}
/>
)}
/>
<FormHelperText error>{errors.name?.message}</FormHelperText>
</FormControl>
</Box>
<FormControl fullWidth>
<FormLabel>Ruleset Name</FormLabel>
<ReactHookTextField name="name" control={control} sx={{ width: 1 }} />
<FormHelperText error>{errors.name?.message}</FormHelperText>
</FormControl>
</NERFormModal>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { TableRow, TableCell, Box, IconButton } from '@mui/material';
import LoadingIndicator from '../../../components/LoadingIndicator';
import { datePipe } from '../../../utils/pipes';
import ErrorPage from '../../ErrorPage';
import { NERButton } from '../../../components/NERButton';
import NERTable from '../../../components/NERTable';
import { useState } from 'react';
import { useToast } from '../../../hooks/toasts.hooks';
import { useAllRulesetTypes, useCreateRulesetType, useDeleteRulesetType } from '../../../hooks/rules.hooks';
import { Delete } from '@mui/icons-material';
import RulesetTypeDeleteModal from './RulesetTypeDeleteModal';
import AddRulesetTypeModal from './AddRulesetTypeModal';

const handleDeleteRulesetType = async (
rulesetTypeId: string,
name: string,
hasRevisionFiles: boolean,
deleteRulesetTypeMutation: (rulesetTypeId: string) => Promise<void>,
toast: ReturnType<typeof useToast>,
onSuccess: () => void
) => {
if (hasRevisionFiles) {
toast.error('Cannot delete ruleset type with existing revisions');
return;
}

try {
await deleteRulesetTypeMutation(rulesetTypeId);
toast.success(`Ruleset Type: ${name} deleted successfully!`);
onSuccess();
} catch (error: unknown) {
if (error instanceof Error) {
toast.error(error.message);
}
}
};

interface RulesetTypeDeleteButtonProps {
rulesetTypeId: string;
name: string;
hasRevisionFiles: boolean;
deleteRulesetTypeMutation: (rulesetTypeId: string) => Promise<void>;
toast: ReturnType<typeof useToast>;
}

const RulesetTypeDeleteButton: React.FC<RulesetTypeDeleteButtonProps> = ({
rulesetTypeId,
name,
hasRevisionFiles,
deleteRulesetTypeMutation,
toast
}) => {
const [showDeleteModal, setShowDeleteModal] = useState(false);

const handleDeleteSubmit = () =>
handleDeleteRulesetType(rulesetTypeId, name, hasRevisionFiles, deleteRulesetTypeMutation, toast, () =>
setShowDeleteModal(false)
);

return (
<>
<IconButton type="button" sx={{ mx: 1 }} onClick={() => setShowDeleteModal(true)}>
<Delete />
</IconButton>
{showDeleteModal && (
<RulesetTypeDeleteModal
rulesetTypeName={name}
onDelete={handleDeleteSubmit}
onHide={() => setShowDeleteModal(false)}
/>
)}
</>
);
};

const RulesetTypesTable: React.FC = () => {
const toast = useToast();
const { data: rulesetTypes, isLoading, isError, error } = useAllRulesetTypes();
const { mutateAsync: createRulesetType } = useCreateRulesetType();
const { mutateAsync: deleteRulesetType } = useDeleteRulesetType();
const [addRulesetTypeModalShow, setAddRulesetTypeModalShow] = useState(false);

if (isError) return <ErrorPage message={error?.message} />;
if (!rulesetTypes || isLoading) return <LoadingIndicator />;

const handleAddRulesetTypeConfirm = async (data: { name: string }) => {
await createRulesetType({ name: data.name });
};

const handleAddRulesetTypeCancel = () => {
setAddRulesetTypeModalShow(false);
};

const rulesetTypeTableRows = rulesetTypes.map((rulesetType, index) => (
<TableRow key={rulesetType.rulesetTypeId}>
<TableCell sx={{ borderBottom: index === rulesetTypes.length - 1 ? 'none' : 'default' }}>{rulesetType.name}</TableCell>
<TableCell sx={{ borderBottom: index === rulesetTypes.length - 1 ? 'none' : 'default' }}>
{datePipe(rulesetType.lastUpdated)}
</TableCell>
<TableCell sx={{ borderBottom: index === rulesetTypes.length - 1 ? 'none' : 'default' }}>
{rulesetType.revisionFiles.length}
</TableCell>
<TableCell sx={{ borderBottom: index === rulesetTypes.length - 1 ? 'none' : 'default' }}>
<RulesetTypeDeleteButton
rulesetTypeId={rulesetType.rulesetTypeId}
name={rulesetType.name}
hasRevisionFiles={rulesetType.revisionFiles.length > 0}
deleteRulesetTypeMutation={deleteRulesetType}
toast={toast}
/>
</TableCell>
</TableRow>
));

return (
<Box>
<AddRulesetTypeModal
open={addRulesetTypeModalShow}
onHide={handleAddRulesetTypeCancel}
onFormSubmit={handleAddRulesetTypeConfirm}
/>
<NERTable
columns={[
{ name: 'Ruleset Type', width: '35%' },
{ name: 'Last Updated', width: '30%' },
{ name: 'Revisions', width: '25%' },
{ name: ' ', width: '10%' }
]}
rows={rulesetTypeTableRows}
/>
<Box sx={{ display: 'flex', justifyContent: 'right', marginTop: '10px' }}>
<NERButton variant="contained" onClick={() => setAddRulesetTypeModalShow(!addRulesetTypeModalShow)}>
Add Ruleset Type
</NERButton>
</Box>
</Box>
);
};

export default RulesetTypesTable;
37 changes: 0 additions & 37 deletions src/frontend/src/pages/RulesPage/RulesetTypePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,8 @@ import React from 'react';
import PageLayout from '../../components/PageLayout';
import { Box } from '@mui/material';
import RulesetTypeTable from './components/RulesetTypeTable';
import { NERButton } from '../../components/NERButton';
import AddRulesetTypeModal from './components/AddRulesetTypeModal';
import { useState } from 'react';
import { useCreateRulesetType } from '../../hooks/rules.hooks';

const RulesetTypePage: React.FC = () => {
const [addRulesetTypeModalShow, setAddRulesetTypeModalShow] = useState(false);

const { mutateAsync: createRulesetType } = useCreateRulesetType();

const handleAddRulesetTypeConfirm = async (data: { name: string }) => {
await createRulesetType({ name: data.name });
};

const handleAddRulesetTypeCancel = () => {
setAddRulesetTypeModalShow(false);
};

return (
<>
<PageLayout title="Ruleset Types">
Expand All @@ -46,27 +30,6 @@ const RulesetTypePage: React.FC = () => {
ml: '20px'
})}
/>
<Box
sx={{
display: 'flex',
justifyContent: { xs: 'center', md: 'flex-end' },
pr: '30px',
pb: 2
}}
>
<NERButton
variant="contained"
sx={{ color: '#ededed' }}
onClick={() => setAddRulesetTypeModalShow(!addRulesetTypeModalShow)}
>
Add Ruleset Type
</NERButton>
<AddRulesetTypeModal
open={addRulesetTypeModalShow}
onHide={handleAddRulesetTypeCancel}
onFormSubmit={handleAddRulesetTypeConfirm}
/>
</Box>
</Box>
</Box>
</PageLayout>
Expand Down
Loading
Loading