#11 Add create/edit/delete functionality for categories

This commit is contained in:
Christoph Lienhard 2020-12-29 18:29:50 +01:00
parent 9a1dc02a7f
commit 101817276f
Signed by: christoph.lienhard
GPG key ID: 6B98870DDC270884
5 changed files with 226 additions and 28 deletions

View file

@ -0,0 +1,68 @@
import {gql} from "@apollo/client";
import {BasicCategoryFragment, BasicCategoryResponse} from "../queries/category";
export const EDIT_CATEGORY = gql`
mutation UpdateCategory($id: ID!, $title: String, $description: String) {
updateCategory(input: {id: $id, categoryPatch: {description: $description, title: $title}}) {
category {
...BasicCategoryFragment
}
}
}
${BasicCategoryFragment}
`
export interface EditCategoryResponse {
updateCategory?: BasicCategoryResponse
}
export interface EditCategoryVariables {
id: string,
title?: string,
description?: string,
}
export const ADD_CATEGORY = gql`
mutation AddCategory($title: String!, $description: String) {
createCategory(input: {category: {title: $title, description: $description}}) {
category {
...BasicCategoryFragment
}
}
}
${BasicCategoryFragment}
`
export interface AddCategoryResponse {
createCategory?: {
category: BasicCategoryResponse
}
}
export interface AddCategoryVariables {
title: string,
description?: string,
}
export const DELETE_CATEGORY = gql`
mutation DeleteCategory($id: ID!) {
deleteCategory(input: { id: $id }) {
category {
...BasicCategoryFragment
}
}
}
${BasicCategoryFragment}
`
export interface DeleteCategoryResponse {
deleteCategory?: {
category: BasicCategoryResponse
}
}
export interface DeleteCategoryVariables {
id: string,
}

View file

@ -1,7 +1,7 @@
import {gql} from "@apollo/client";
export const BasicCategoryFragment = gql`
fragment BasicCategory on Category {
fragment BasicCategoryFragment on Category {
id
rowId
title
@ -20,7 +20,7 @@ export const GET_ALL_CATEGORIES = gql`
query AllCategories {
allCategories {
nodes {
...BasicCategory
...BasicCategoryFragment
}
}
}

View file

@ -1,11 +1,29 @@
import {Paper, Typography} from "@material-ui/core";
import React, {useState} from "react";
import {makeStyles} from "@material-ui/core/styles";
import {useQuery} from "@apollo/client";
import {Reference, useMutation, useQuery} from "@apollo/client";
import AddCard from "./AddCard";
import AccordionWithEdit from "./AccordionWithEdit";
import {GET_ALL_CATEGORIES, GetAllCategoriesResponse, BasicCategoryResponse} from "../backend/queries/category";
import {
BasicCategoryFragment,
BasicCategoryResponse,
GET_ALL_CATEGORIES,
GetAllCategoriesResponse
} from "../backend/queries/category";
import ChangeCategoryDialog, {ChangeCategoryDialogContent} from "./ChangeCategoryDialog";
import {useSnackbar} from "notistack";
import DeleteConfirmationDialog, {DeleteConfirmationDialogContent} from "./DeleteConfirmationDialog";
import {
ADD_CATEGORY,
AddCategoryResponse,
AddCategoryVariables,
DELETE_CATEGORY,
DeleteCategoryResponse,
DeleteCategoryVariables,
EDIT_CATEGORY,
EditCategoryResponse,
EditCategoryVariables
} from "../backend/mutations/category";
const useStyles = makeStyles((theme) => ({
root: {
@ -21,62 +39,174 @@ const emptyChangeCategoryDialog: ChangeCategoryDialogContent = {
details: "",
}
const emptyDeleteConfirmationDialogContent: DeleteConfirmationDialogContent = {
id: "",
type: "Kategorie",
title: "",
}
export default function CategoryList() {
const {data} = useQuery<GetAllCategoriesResponse, null>(GET_ALL_CATEGORIES);
const [dialogOpen, setDialogOpen] = useState(false);
const [changeDialogOpen, setChangeDialogOpen] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [dialogTitle, setDialogTitle] = useState("");
const [dialogConfirmButtonText, setDialogConfirmButtonText] = useState("");
const [dialogContent, setDialogContent] = useState(emptyChangeCategoryDialog);
const [changeDialogContent, setChangeDialogContent] = useState(emptyChangeCategoryDialog);
const [deleteDialogContent, setDeleteDialogContent] = useState(emptyDeleteConfirmationDialogContent);
const { enqueueSnackbar } = useSnackbar();
const categories = useQuery<GetAllCategoriesResponse, null>(GET_ALL_CATEGORIES).data?.allCategories.nodes;
const [editCategory, {loading: editLoading}] = useMutation<EditCategoryResponse, EditCategoryVariables>(EDIT_CATEGORY, {
onError: (e) => enqueueSnackbar(`Ein Fehler ist aufgetreten: ${e.message}`, { variant: "error"}),
onCompleted: (response) => {
if (response.updateCategory) {
enqueueSnackbar("Kategorie erfolgreich geändert.", { variant: "success"})
setChangeDialogOpen(false);
} else {
enqueueSnackbar("Ein Fehler ist aufgetreten, versuche es erneut.", { variant: "error"})
}
}
});
const [addCategory, {loading: addLoading}] = useMutation<AddCategoryResponse, AddCategoryVariables>(ADD_CATEGORY, {
onError: (e) => enqueueSnackbar(`Ein Fehler ist aufgetreten: ${e.message}`, { variant: "error"}),
onCompleted: (response) => {
if (response.createCategory) {
enqueueSnackbar("Kategorie erfolgreich hinzugefügt.", { variant: "success"})
setChangeDialogOpen(false);
} else {
enqueueSnackbar("Ein Fehler ist aufgetreten, versuche es erneut.", { variant: "error"})
}
},
update: (cache, { data }) => {
cache.modify({
fields: {
allCategories(existingCategories = { nodes: []}) {
const newCategoryRef = cache.writeFragment<BasicCategoryResponse | undefined>({
data: data?.createCategory?.category,
fragment: BasicCategoryFragment,
fragmentName: "BasicCategoryFragment",
});
return {nodes: [...existingCategories.nodes, newCategoryRef]};
}
}
});
}
});
const [deleteCategory, {loading: deleteLoading}] = useMutation<DeleteCategoryResponse, DeleteCategoryVariables>(DELETE_CATEGORY, {
onError: (e) => enqueueSnackbar(`Ein Fehler ist aufgetreten: ${e.message}`, { variant: "error"}),
onCompleted: (response) => {
if (response.deleteCategory) {
enqueueSnackbar("Kategorie erfolgreich gelöscht.", { variant: "success"})
setDeleteDialogOpen(false);
} else {
enqueueSnackbar("Ein Fehler ist aufgetreten, versuche es erneut.", { variant: "error"})
}
},
update: (cache, { data }) => {
const idToRemove = data?.deleteCategory?.category.id;
cache.modify({
fields: {
allCategories(existingCategoriesRef: { nodes: Array<Reference>} = { nodes: []}, {readField}) {
console.log("existingCategory: ", existingCategoriesRef)
return {nodes: existingCategoriesRef.nodes.filter(categoryRef => readField('id', categoryRef) !== idToRemove)};
}
}
});
}
});
const classes = useStyles();
const loading = editLoading || addLoading;
const handleAddClick = () => {
setDialogTitle("Neue Kategorie erstellen");
setDialogConfirmButtonText("Erstellen");
if (dialogContent.id !== "") {
setDialogContent(emptyChangeCategoryDialog);
if (changeDialogContent.id !== "") {
setChangeDialogContent(emptyChangeCategoryDialog);
}
setDialogOpen(true);
setChangeDialogOpen(true);
}
const handleEditButtonClick = (category: BasicCategoryResponse) => {
setDialogTitle("Kategorie bearbeiten");
setDialogConfirmButtonText("Speichern")
if (dialogContent.id !== category.id) {
setDialogContent({
if (changeDialogContent.id !== category.id) {
setChangeDialogContent({
id: category.id,
title: category.title,
details: category.description,
})
}
setDialogOpen(true);
setChangeDialogOpen(true);
};
const handleDialogContentChange = (content: ChangeCategoryDialogContent) => {
setDialogContent(content)
const handleDeleteButtonClick = (category: BasicCategoryResponse) => {
setDeleteDialogContent({
...deleteDialogContent,
id: category.id,
title: category.title,
});
setDeleteDialogOpen(true);
}
const handleDialogContentChange = (content: ChangeCategoryDialogContent) => {
setChangeDialogContent(content)
}
const handleChangeConfirmButtonClick = () => {
if (changeDialogContent.id !== "") {
editCategory({
variables: {
id: changeDialogContent.id,
title: changeDialogContent.title,
description: changeDialogContent.details,
}
})
} else {
addCategory({
variables: {
title: changeDialogContent.title,
description: changeDialogContent.details,
}
})
}
};
const handleDeleteConfirmButtonClick = () => {
deleteCategory({
variables: {
id: deleteDialogContent.id
}
})
};
return (
<Paper className={classes.root}>
<Typography component={"h2"} variant="h6" color="primary" gutterBottom>Kategorien</Typography>
{data?.allCategories.nodes.map(category => <AccordionWithEdit
{categories?.map(category => <AccordionWithEdit
key={category.id}
title={category.title}
description={category.description}
onEditButtonClick={() => handleEditButtonClick(category)}
onDeleteButtonClick={() => handleDeleteButtonClick(category)}
/>
)}
<AddCard handleClick={handleAddClick}/>
<ChangeCategoryDialog
title={dialogTitle}
confirmButtonText={dialogConfirmButtonText}
open={dialogOpen}
content={dialogContent}
open={changeDialogOpen}
content={changeDialogContent}
loading={loading}
handleContentChange={handleDialogContentChange}
handleConfirmButtonClick={() => {
console.log("go for category mutation");
setDialogOpen(false);
}}
handleClose={() => setDialogOpen(false)}/>
handleConfirmButtonClick={handleChangeConfirmButtonClick}
handleClose={() => setChangeDialogOpen(false)}
/>
<DeleteConfirmationDialog
content={deleteDialogContent}
open={deleteDialogOpen}
loading={deleteLoading}
handleConfirmButtonClick={handleDeleteConfirmButtonClick}
handleClose={() => setDeleteDialogOpen(false)}
/>
</Paper>
)
}

View file

@ -17,6 +17,7 @@ interface ChangeCategoryDialogProps {
confirmButtonText: string,
open: boolean,
content: ChangeCategoryDialogContent,
loading: boolean,
handleContentChange(content: ChangeCategoryDialogContent): void
@ -42,7 +43,7 @@ export default function ChangeCategoryDialog(props: ChangeCategoryDialogProps) {
onCancelButtonClick={props.handleClose}
onConfirmButtonClick={props.handleConfirmButtonClick}
confirmButtonText={props.confirmButtonText}
loading={false}
loading={props.loading}
/>
</Dialog>
);

View file

@ -183,19 +183,18 @@ export default function QuestionList() {
}
})
};
return (
<Paper className={classes.root}>
<Typography component={"h2"} variant="h6" color="primary" gutterBottom>Fragen</Typography>
{questions?.map(question => {
return <AccordionWithEdit
{questions?.map(question => <AccordionWithEdit
key={question.id}
title={question.title}
subTitle={question.categoryByCategoryRowId?.title}
description={question.description}
onEditButtonClick={() => handleEditButtonClick(question)}
onDeleteButtonClick={() => handleDeleteButtonClick(question)}
/>;
}
/>
)}
<AddCard handleClick={handleAddClick}/>
<ChangeQuestionDialog