Merge pull request #3 from majicmaj/majicmaj-feature/refactor-labels-view-table
Feature - Refactor Labels Table to display the appearance of the label
This commit is contained in:
commit
78c3a6a186
2 changed files with 98 additions and 125 deletions
|
@ -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'
|
||||
|
||||
|
@ -17,12 +18,15 @@ import { Add } from '@mui/icons-material'
|
|||
import { useQueryClient } from 'react-query'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { DeleteLabel } from '../../utils/Fetcher'
|
||||
import { getTextColorFromBackgroundColor } from '../../utils/LabelColors'
|
||||
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||
|
||||
const LabelView = () => {
|
||||
const { data: labels, isLabelsLoading, isError } = useLabels()
|
||||
|
||||
const [userLabels, setUserLabels] = useState([labels])
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
|
||||
const [currentLabel, setCurrentLabel] = useState(null)
|
||||
const queryClient = useQueryClient()
|
||||
const [confirmationModel, setConfirmationModel] = useState({})
|
||||
|
@ -74,6 +78,7 @@ const LabelView = () => {
|
|||
)
|
||||
setUserLabels(updatedLabels)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (labels) {
|
||||
setUserLabels(labels)
|
||||
|
@ -103,62 +108,49 @@ 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>
|
||||
{userLabels.map(label => (
|
||||
<tr key={label.id}>
|
||||
<td
|
||||
onClick={() => {
|
||||
Navigate('/my/chores', { state: { label: label.id } })
|
||||
}}
|
||||
<div className='flex flex-col gap-2'>
|
||||
{userLabels.map(label => (
|
||||
<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'
|
||||
>
|
||||
<Chip
|
||||
variant='outlined'
|
||||
color='primary'
|
||||
size='lg'
|
||||
sx={{
|
||||
background: label.color,
|
||||
borderColor: label.color,
|
||||
color: getTextColorFromBackgroundColor(label.color),
|
||||
}}
|
||||
>
|
||||
{label.name}
|
||||
</Chip>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
size='sm'
|
||||
variant='soft'
|
||||
color='neutral'
|
||||
onClick={() => handleEditLabel(label)}
|
||||
startDecorator={<EditIcon />}
|
||||
|
||||
>
|
||||
{label.name}
|
||||
</td>
|
||||
<td
|
||||
style={{
|
||||
// center without display flex:
|
||||
textAlign: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
Edit
|
||||
</Button>
|
||||
<IconButton
|
||||
size='sm'
|
||||
variant='soft'
|
||||
onClick={() => handleDeleteLabel(label.id)}
|
||||
color='danger'
|
||||
>
|
||||
<Box
|
||||
width={20}
|
||||
height={20}
|
||||
borderRadius='50%'
|
||||
sx={{
|
||||
backgroundColor: label.color,
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={() => handleEditLabel(label)}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={() => handleDeleteClicked(label.id)}
|
||||
color='danger'
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
{userLabels.length === 0 && (
|
||||
<Typography textAlign='center' mt={2}>
|
||||
|
@ -174,6 +166,7 @@ const LabelView = () => {
|
|||
label={currentLabel}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
|
|
|
@ -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>
|
||||
{error && (
|
||||
<Typography color='warning' level='body-sm'>
|
||||
{error}
|
||||
</Typography>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
|
||||
{error && (
|
||||
<Typography color='warning' level='body-sm'>
|
||||
{error}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box display='flex' justifyContent='space-around' mt={1}>
|
||||
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
|
||||
{label ? 'Save Changes' : 'Add Label'}
|
||||
</Button>
|
||||
|
|
Loading…
Add table
Reference in a new issue