This commit is contained in:
Majeemaj 2024-11-24 12:33:15 -05:00
parent 450f8ed4e1
commit 7d6b65e745
2 changed files with 100 additions and 119 deletions

View file

@ -2,13 +2,14 @@ import DeleteIcon from '@mui/icons-material/Delete'
import EditIcon from '@mui/icons-material/Edit'
import {
Box,
Button,
Chip,
CircularProgress,
Container,
IconButton,
Table,
Typography,
} from '@mui/joy'
import React, { useEffect, useState } from 'react'
import { useEffect, useState } from 'react'
import LabelModal from '../Modals/Inputs/LabelModal'
import { useLabels } from './LabelQueries'
@ -16,13 +17,17 @@ import { useLabels } from './LabelQueries'
import { Add } from '@mui/icons-material'
import { useQueryClient } from 'react-query'
import { DeleteLabel } from '../../utils/Fetcher'
import { getTextColorFromBackgroundColor } from '../../utils/LabelColors'
const LabelView = () => {
const { data: labels, isLabelsLoading, isError } = useLabels()
const [userLabels, setUserLabels] = useState([labels])
const [modalOpen, setModalOpen] = useState(false)
const [currentLabel, setCurrentLabel] = useState(null) // Label being edited or null for new label
const queryClient = useQueryClient()
const handleAddLabel = () => {
setCurrentLabel(null) // Adding a new label
setModalOpen(true)
@ -52,6 +57,7 @@ const LabelView = () => {
)
setUserLabels(updatedLabels)
}
useEffect(() => {
if (labels) {
setUserLabels(labels)
@ -81,56 +87,50 @@ const LabelView = () => {
return (
<Container maxWidth='md'>
<Table aria-label='Manage Labels' stickyHeader hoverRow>
<thead>
<tr>
<th style={{ textAlign: 'center' }}>Label</th>
<th style={{ textAlign: 'center' }}>Color</th>
<th style={{ textAlign: 'center' }}>Actions</th>
</tr>
</thead>
<tbody>
<Typography level='h2' sx={{ my: 2 }}>
Labels
</Typography>
<div className='flex flex-col gap-2'>
{userLabels.map(label => (
<tr key={label.id}>
<td>{label.name}</td>
<td
style={{
// center without display flex:
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
}}
<div
key={label}
className='grid w-full grid-cols-[1fr,auto,auto] rounded-lg border border-zinc-200/80 p-4 shadow-sm dark:bg-zinc-900'
>
<Box
width={20}
height={20}
borderRadius='50%'
<Chip
variant='outlined'
color='primary'
size='lg'
sx={{
backgroundColor: label.color,
}}
/>
</td>
<td
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
background: label.color,
borderColor: label.color,
color: getTextColorFromBackgroundColor(label.color),
}}
>
<IconButton onClick={() => handleEditLabel(label)}>
<EditIcon />
</IconButton>
{label.name}
</Chip>
<div className='flex gap-2'>
<Button
size='sm'
variant='soft'
color='neutral'
onClick={() => handleEditLabel(label)}
startDecorator={<EditIcon />}
>
Edit
</Button>
<IconButton
size='sm'
variant='soft'
onClick={() => handleDeleteLabel(label.id)}
color='danger'
>
<DeleteIcon />
</IconButton>
</td>
</tr>
</div>
</div>
))}
</tbody>
</Table>
</div>
{userLabels.length === 0 && (
<Typography textAlign='center' mt={2}>
@ -146,6 +146,7 @@ const LabelView = () => {
label={currentLabel}
/>
)}
<Box
sx={{
position: 'fixed',

View file

@ -1,3 +1,4 @@
import React, { useEffect, useState } from 'react'
import {
Box,
Button,
@ -9,18 +10,16 @@ import {
Select,
Typography,
} from '@mui/joy'
import React, { useEffect } from 'react'
import { useQueryClient } from 'react-query'
import { useQueryClient, useMutation } from 'react-query'
import { CreateLabel, UpdateLabel } from '../../../utils/Fetcher'
import LABEL_COLORS from '../../../utils/LabelColors'
import { useLabels } from '../../Labels/LabelQueries'
function LabelModal({ isOpen, onClose, onSave, label }) {
const [labelName, setLabelName] = React.useState('')
const [color, setColor] = React.useState('')
const [error, setError] = React.useState('')
const { data: userLabels, isLoadingLabels } = useLabels()
function LabelModal({ isOpen, onClose, label }) {
const [labelName, setLabelName] = useState('')
const [color, setColor] = useState('')
const [error, setError] = useState('')
const { data: userLabels = [] } = useLabels()
const queryClient = useQueryClient()
// Populate the form fields when editing
@ -35,48 +34,48 @@ function LabelModal({ isOpen, onClose, onSave, label }) {
setError('')
}, [label])
// Validation logic
const validateLabel = () => {
if (!labelName || labelName.trim() === '') {
if (!labelName.trim()) {
setError('Name cannot be empty')
return false
} else if (
}
if (
userLabels.some(
userLabel => userLabel.name === labelName && userLabel.id !== label.id,
userLabel => userLabel.name === labelName && userLabel.id !== label?.id,
)
) {
setError('Label with this name already exists')
return false
} else if (color === '') {
}
if (!color) {
setError('Please select a color')
return false
}
return true
}
const handleSave = () => {
if (!validateLabel()) {
return
}
const saveAction = label
? UpdateLabel({ id: label.id, name: labelName, color })
: CreateLabel({ name: labelName, color })
saveAction.then(res => {
if (res.error) {
console.log(res.error)
setError('Failed to save label. Please try again.')
return
}
res.json().then(data => {
if (data.error) {
setError('Failed to save label. Please try again.')
return
}
onSave({ id: data?.res?.id, name: labelName, color })
// Mutation for saving labels
const saveLabelMutation = useMutation(
newLabel =>
label
? UpdateLabel({ id: label.id, ...newLabel })
: CreateLabel(newLabel),
{
onSuccess: () => {
queryClient.invalidateQueries('labels')
onClose()
})
})
},
onError: () => {
setError('Failed to save label. Please try again.')
},
},
)
const handleSave = () => {
if (!validateLabel()) return
saveLabelMutation.mutate({ name: labelName, color })
}
return (
@ -85,51 +84,39 @@ function LabelModal({ isOpen, onClose, onSave, label }) {
<Typography level='title-md' mb={1}>
{label ? 'Edit Label' : 'Add Label'}
</Typography>
<FormControl>
<Typography level='body-sm' alignSelf={'start'}>
<Typography gutterBottom level='body-sm' alignSelf='start'>
Name
</Typography>
<Input
margin='normal'
required
fullWidth
name='labelName'
type='text'
id='labelName'
value={labelName}
onChange={e => setLabelName(e.target.value)}
/>
</FormControl>
{/* Color Selection */}
<FormControl>
<Typography level='body-sm' alignSelf={'start'}>
Color:
<Typography gutterBottom level='body-sm' alignSelf='start'>
Color
</Typography>
<Select
label='Color'
value={color}
onChange={(e, value) => value && setColor(value)}
renderValue={selected => (
<Typography
key={selected.value}
startDecorator={
<Box
className='h-4 w-4'
borderRadius={10}
sx={{
background: selected.value,
shadow: { xs: 1 },
}}
sx={{ background: selected.value }}
/>
}
>
{selected.label}
</Typography>
)}
onChange={(e, value) => {
value && setColor(value)
}}
>
{LABEL_COLORS.map(val => (
<Option key={val.value} value={val.value}>
@ -138,31 +125,24 @@ function LabelModal({ isOpen, onClose, onSave, label }) {
width={20}
height={20}
borderRadius={10}
sx={{
background: val.value,
}}
sx={{ background: val.value }}
/>
<Typography
sx={{
ml: 1,
color: 'text.secondary',
}}
variant='caption'
>
<Typography sx={{ ml: 1 }} variant='caption'>
{val.name}
</Typography>
</Box>
</Option>
))}
</Select>
</FormControl>
{error && (
<Typography color='warning' level='body-sm'>
{error}
</Typography>
)}
</FormControl>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Box display='flex' justifyContent='space-around' mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{label ? 'Save Changes' : 'Add Label'}
</Button>