157 lines
4.4 KiB
JavaScript
157 lines
4.4 KiB
JavaScript
import { Box, Button, FormControl, Input, Typography } from '@mui/joy'
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import { useResponsiveModal } from '../../../hooks/useResponsiveModal.js'
|
|
import { useNotification } from '../../../service/NotificationProvider.jsx'
|
|
import LABEL_COLORS from '../../../utils/Colors.jsx'
|
|
import { CreateLabel, UpdateLabel } from '../../../utils/Fetcher'
|
|
import { useLabels } from '../../Labels/LabelQueries'
|
|
|
|
function LabelModal({ isOpen, onClose, label }) {
|
|
const { ResponsiveModal } = useResponsiveModal()
|
|
|
|
const [labelName, setLabelName] = useState('')
|
|
const [color, setColor] = useState('')
|
|
const [error, setError] = useState('')
|
|
const { data: userLabels = [] } = useLabels()
|
|
const queryClient = useQueryClient()
|
|
const { showError } = useNotification()
|
|
|
|
// Populate the form fields when editing
|
|
useEffect(() => {
|
|
if (label) {
|
|
setLabelName(label.name)
|
|
setColor(label.color)
|
|
} else {
|
|
setLabelName('')
|
|
setColor('')
|
|
}
|
|
setError('')
|
|
}, [label])
|
|
|
|
// Validation logic
|
|
const validateLabel = () => {
|
|
if (!labelName.trim()) {
|
|
setError('Name cannot be empty')
|
|
return false
|
|
}
|
|
if (
|
|
userLabels.some(
|
|
userLabel => userLabel.name === labelName && userLabel.id !== label?.id,
|
|
)
|
|
) {
|
|
setError('Label with this name already exists')
|
|
return false
|
|
}
|
|
if (!color) {
|
|
setError('Please select a color')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
const handleSave = () => {
|
|
if (!validateLabel()) return
|
|
const saveLabel = label?.id && label.id !== -1 ? UpdateLabel : CreateLabel
|
|
saveLabel({
|
|
id: label?.id,
|
|
name: labelName,
|
|
color,
|
|
})
|
|
.then(res => {
|
|
if (res?.error) {
|
|
setError(res.error)
|
|
} else {
|
|
queryClient.invalidateQueries('labels')
|
|
onClose()
|
|
}
|
|
})
|
|
.catch(err => {
|
|
if (err.queued) {
|
|
showError({
|
|
title: 'Failed to save label',
|
|
message: 'Unable to save label. Please try again.',
|
|
})
|
|
} else {
|
|
showError({
|
|
title: 'Failed to save label',
|
|
message: 'Unable to save label. Please try again.',
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
return (
|
|
<ResponsiveModal
|
|
open={isOpen}
|
|
onClose={onClose}
|
|
size='lg'
|
|
fullWidth={true}
|
|
title={label ? 'Edit Label' : 'Add Label'}
|
|
footer={
|
|
<Box display='flex' justifyContent='space-around' mt={1}>
|
|
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
|
|
{label ? 'Save Changes' : 'Add Label'}
|
|
</Button>
|
|
<Button size='lg' onClick={onClose} variant='outlined'>
|
|
Cancel
|
|
</Button>
|
|
</Box>
|
|
}
|
|
>
|
|
<Box>
|
|
<FormControl>
|
|
<Typography gutterBottom level='body-sm' alignSelf='start'>
|
|
Name
|
|
</Typography>
|
|
<Input
|
|
fullWidth
|
|
id='labelName'
|
|
value={labelName}
|
|
onChange={e => setLabelName(e.target.value)}
|
|
/>
|
|
</FormControl>
|
|
|
|
<FormControl>
|
|
<Typography gutterBottom level='body-sm' alignSelf='start'>
|
|
Color
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
|
|
{LABEL_COLORS.map(colorOption => (
|
|
<Box
|
|
key={colorOption.value}
|
|
title={colorOption.name}
|
|
onClick={() => setColor(colorOption.value)}
|
|
sx={{
|
|
width: 26,
|
|
height: 26,
|
|
borderRadius: '50%',
|
|
background: colorOption.value,
|
|
cursor: 'pointer',
|
|
outline:
|
|
color === colorOption.value
|
|
? '3px solid var(--joy-palette-primary-500)'
|
|
: '2px solid transparent',
|
|
outlineOffset: '2px',
|
|
transition: 'all 0.15s ease',
|
|
flexShrink: 0,
|
|
'&:hover': { transform: 'scale(1.2)' },
|
|
}}
|
|
/>
|
|
))}
|
|
</Box>
|
|
</FormControl>
|
|
|
|
{error && (
|
|
<Typography color='warning' level='body-sm'>
|
|
{error}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</ResponsiveModal>
|
|
)
|
|
}
|
|
|
|
export default LabelModal
|