Skip to content

Commit

Permalink
Merge pull request #63 from marmelab/feat/edit-task
Browse files Browse the repository at this point in the history
Feat(task): Add task edit support
  • Loading branch information
slax57 authored Aug 13, 2024
2 parents 43145ba + d0f61fd commit b4025d6
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 166 deletions.
25 changes: 12 additions & 13 deletions src/contacts/ContactListContent.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
/* eslint-disable import/no-anonymous-default-export */
import * as React from 'react';
import {
RecordContextProvider,
ReferenceField,
SimpleListLoading,
TextField,
useListContext,
} from 'react-admin';
import type { Theme } from '@mui/material';
import {
Checkbox,
List,
ListItem,
ListItemAvatar,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
Checkbox,
Typography,
useMediaQuery,
} from '@mui/material';
import type { Theme } from '@mui/material';
import { Link } from 'react-router-dom';
import { formatRelative } from 'date-fns';
import {
RecordContextProvider,
ReferenceField,
SimpleListLoading,
TextField,
useListContext,
} from 'react-admin';
import { Link } from 'react-router-dom';

import { Avatar } from './Avatar';
import { Status } from '../misc/Status';
import { TagsList } from './TagsList';
import { Contact } from '../types';
import { Avatar } from './Avatar';
import { TagsList } from './TagsList';

export const ContactListContent = () => {
const {
Expand Down
208 changes: 55 additions & 153 deletions src/contacts/TagsListEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
import ControlPointIcon from '@mui/icons-material/ControlPoint';
import EditIcon from '@mui/icons-material/Edit';
import { Box, Chip, Menu, MenuItem } from '@mui/material';
import * as React from 'react';
import { useState, FormEvent } from 'react';
import { useState } from 'react';
import {
useGetMany,
useCreate,
useUpdate,
useGetList,
Identifier,
useGetList,
useGetMany,
useRecordContext,
Toolbar,
useUpdate,
} from 'react-admin';
import {
Chip,
Box,
Button,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TextField,
MenuItem,
Menu,
} from '@mui/material';
import ControlPointIcon from '@mui/icons-material/ControlPoint';
import EditIcon from '@mui/icons-material/Edit';

import { colors } from '../tags/colors';
import { TagChip } from '../tags/TagChip';
import { TagCreateModal } from '../tags/TagCreateModal';
import { Contact, Tag } from '../types';
import { DialogCloseButton } from '../misc/DialogCloseButton';
import ContentSave from '@mui/icons-material/Save';

export const TagsListEdit = () => {
const record = useRecordContext<Contact>();
const [open, setOpen] = useState(false);
const [newTagName, setNewTagName] = useState('');
const [newTagColor, setNewTagColor] = useState(colors[0]);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [disabled, setDisabled] = useState(false);

const { data: allTags, isPending: isPendingAllTags } = useGetList<Tag>(
'tags',
Expand All @@ -50,100 +33,86 @@ export const TagsListEdit = () => {
{ enabled: record && record.tags && record.tags.length > 0 }
);
const [update] = useUpdate<Contact>();
const [create] = useCreate<Tag>();

const unselectedTags =
allTags &&
record &&
allTags.filter(tag => !record.tags.includes(tag.id));

const handleOpen = (event: React.MouseEvent<HTMLDivElement>) => {
const handleMenuOpen = (event: React.MouseEvent<HTMLDivElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
const handleMenuClose = () => {
setAnchorEl(null);
};

const handleDeleteTag = (id: Identifier) => {
const handleTagAdd = (id: Identifier) => {
if (!record) {
throw new Error('No contact record found');
}
const tags = record.tags.filter(tagId => tagId !== id);
const tags = [...record.tags, id];
update('contacts', {
id: record.id,
data: { tags },
previousData: record,
});
setAnchorEl(null);
};

const handleAddTag = (id: Identifier) => {
const handleTagDelete = async (id: Identifier) => {
if (!record) {
throw new Error('No contact record found');
}
const tags = [...record.tags, id];
update('contacts', {
const tags = record.tags.filter(tagId => tagId !== id);
await update('contacts', {
id: record.id,
data: { tags },
previousData: record,
});
setAnchorEl(null);
};

const handleOpenCreateDialog = () => {
const openTagCreateDialog = () => {
setOpen(true);
setAnchorEl(null);
setDisabled(false);
};

const handleNewTagNameChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setNewTagName(event.target.value);
const handleTagCreateClose = () => {
setOpen(false);
};

const handleCreateTag = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!record) {
throw new Error('No contact record found');
}
setDisabled(true);
create(
'tags',
{ data: { name: newTagName, color: newTagColor } },
{
onSuccess: tag => {
update(
'contacts',
{
id: record.id,
data: { tags: [...record.tags, tag.id] },
previousData: record,
},
{
onSuccess: () => {
setNewTagName('');
setNewTagColor(colors[0]);
setOpen(false);
},
}
);
},
const handleTagCreated = React.useCallback(
async (tag: Tag) => {
if (!record) {
throw new Error('No contact record found');
}
);
};

await update(
'contacts',
{
id: record.id,
data: { tags: [...record.tags, tag.id] },
previousData: record,
},
{
onSuccess: () => {
setOpen(false);
},
}
);
},
[update, record]
);

if (isPendingRecordTags || isPendingAllTags) return null;
return (
<>
{tags?.map(tag => (
<Box mt={1} mb={1} key={tag.id}>
<Chip
size="small"
variant="outlined"
onDelete={() => handleDeleteTag(tag.id)}
label={tag.name}
style={{ backgroundColor: tag.color, border: 0 }}
<TagChip
tag={tag}
onUnlink={() => handleTagDelete(tag.id)}
key={tag.id}
/>
</Box>
))}
Expand All @@ -152,18 +121,18 @@ export const TagsListEdit = () => {
icon={<ControlPointIcon />}
size="small"
variant="outlined"
onClick={handleOpen}
onClick={handleMenuOpen}
label="Add tag"
color="primary"
/>
</Box>
<Menu
open={Boolean(anchorEl)}
onClose={handleClose}
onClose={handleMenuClose}
anchorEl={anchorEl}
>
{unselectedTags?.map(tag => (
<MenuItem key={tag.id} onClick={() => handleAddTag(tag.id)}>
<MenuItem key={tag.id} onClick={() => handleTagAdd(tag.id)}>
<Chip
size="small"
variant="outlined"
Expand All @@ -172,93 +141,26 @@ export const TagsListEdit = () => {
backgroundColor: tag.color,
border: 0,
}}
onClick={() => handleAddTag(tag.id)}
onClick={() => handleTagAdd(tag.id)}
/>
</MenuItem>
))}
<MenuItem onClick={handleOpenCreateDialog}>
<MenuItem onClick={openTagCreateDialog}>
<Chip
icon={<EditIcon />}
size="small"
variant="outlined"
onClick={handleOpenCreateDialog}
onClick={openTagCreateDialog}
color="primary"
label="Create new tag"
/>
</MenuItem>
</Menu>
<Dialog
<TagCreateModal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="form-dialog-title"
>
<form onSubmit={handleCreateTag}>
<DialogCloseButton onClose={() => setOpen(false)} />
<DialogTitle id="form-dialog-title">
Create a new tag
</DialogTitle>
<DialogContent>
<TextField
autoFocus
label="Tag name"
value={newTagName}
onChange={handleNewTagNameChange}
sx={{ mt: 1 }}
/>
<Box display="flex" flexWrap="wrap" width={230} mt={2}>
{colors.map(color => (
<RoundButton
key={color}
color={color}
selected={color === newTagColor}
handleClick={() => {
setNewTagColor(color);
}}
/>
))}
</Box>
</DialogContent>
<DialogActions
sx={{
justifyContent: 'flex-start',
p: 0,
}}
>
<Toolbar
sx={{
width: '100%',
}}
>
<Button
type="submit"
color="primary"
disabled={disabled}
variant="contained"
startIcon={<ContentSave />}
>
Save
</Button>
</Toolbar>
</DialogActions>
</form>
</Dialog>
onClose={handleTagCreateClose}
onSuccess={handleTagCreated}
/>
</>
);
};

const RoundButton = ({ color, handleClick, selected }: any) => (
<Box
component="button"
type="button"
sx={{
bgcolor: color,
width: 30,
height: 30,
borderRadius: 15,
border: selected ? '2px solid grey' : 'none',
display: 'inline-block',
margin: 1,
}}
onClick={handleClick}
/>
);
18 changes: 18 additions & 0 deletions src/tags/RoundButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Box } from '@mui/material';

export const RoundButton = ({ color, handleClick, selected }: any) => (
<Box
component="button"
type="button"
sx={{
bgcolor: color,
width: 30,
height: 30,
borderRadius: 15,
border: selected ? '2px solid grey' : 'none',
display: 'inline-block',
margin: 1,
}}
onClick={handleClick}
/>
);
Loading

0 comments on commit b4025d6

Please sign in to comment.