From 96a01c6f60b98093ac36aeed6e00b466ee7494fe Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Sep 2021 12:57:36 +0200 Subject: [PATCH] Edit and delete snippet functionality --- client/src/App.tsx | 2 +- .../components/Snippets/SnippetDetails.tsx | 84 +++++++++++++++++++ .../src/components/Snippets/SnippetForm.tsx | 42 ++++++++-- client/src/containers/Editor.tsx | 32 +++++-- client/src/store/SnippetsContext.tsx | 37 ++++++++ client/src/typescript/interfaces/Context.ts | 2 + 6 files changed, 185 insertions(+), 14 deletions(-) create mode 100644 client/src/components/Snippets/SnippetDetails.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index c579af9..73dd8e2 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -12,7 +12,7 @@ export const App = () => { - + diff --git a/client/src/components/Snippets/SnippetDetails.tsx b/client/src/components/Snippets/SnippetDetails.tsx new file mode 100644 index 0000000..5f91f40 --- /dev/null +++ b/client/src/components/Snippets/SnippetDetails.tsx @@ -0,0 +1,84 @@ +import { useContext } from 'react'; +import { Link } from 'react-router-dom'; +import { SnippetsContext } from '../../store'; +import { Snippet } from '../../typescript/interfaces'; +import { dateParser } from '../../utils'; +import { Button, Card } from '../UI'; + +interface Props { + snippet: Snippet; +} + +export const SnippetDetails = (props: Props): JSX.Element => { + const { title, language, createdAt, updatedAt, description, code, id } = + props.snippet; + + const { deleteSnippet } = useContext(SnippetsContext); + + const creationDate = dateParser(createdAt); + const updateDate = dateParser(updatedAt); + + const copyHandler = () => { + navigator.clipboard.writeText(code); + }; + + return ( + +

{description}

+ + {/* LANGUAGE */} +
+ Language + {language} +
+ + {/* CREATED AT */} +
+ Created + {creationDate.relative} +
+ + {/* UPDATED AT */} +
+ Last updated + {updateDate.relative} +
+ +
+ + {/* ACTIONS */} +
+ +
+ +
+ + {/* COPY */} +
+
+
+ ); +}; diff --git a/client/src/components/Snippets/SnippetForm.tsx b/client/src/components/Snippets/SnippetForm.tsx index 9724fde..4fb830f 100644 --- a/client/src/components/Snippets/SnippetForm.tsx +++ b/client/src/components/Snippets/SnippetForm.tsx @@ -1,10 +1,23 @@ -import { ChangeEvent, FormEvent, Fragment, useState, useContext } from 'react'; +import { + ChangeEvent, + FormEvent, + Fragment, + useState, + useContext, + useEffect +} from 'react'; import { SnippetsContext } from '../../store'; import { NewSnippet } from '../../typescript/interfaces'; import { Button, Card } from '../UI'; -export const SnippetForm = (): JSX.Element => { - const { createSnippet } = useContext(SnippetsContext); +interface Props { + inEdit?: boolean; +} + +export const SnippetForm = (props: Props): JSX.Element => { + const { inEdit = false } = props; + const { createSnippet, currentSnippet, updateSnippet } = + useContext(SnippetsContext); const [formData, setFormData] = useState({ title: '', @@ -14,6 +27,14 @@ export const SnippetForm = (): JSX.Element => { docs: '' }); + useEffect(() => { + if (inEdit) { + if (currentSnippet) { + setFormData({ ...currentSnippet }); + } + } + }, [currentSnippet]); + const inputHandler = ( e: ChangeEvent ) => { @@ -25,7 +46,14 @@ export const SnippetForm = (): JSX.Element => { const formHandler = (e: FormEvent) => { e.preventDefault(); - createSnippet(formData); + + if (inEdit) { + if (currentSnippet) { + updateSnippet(formData, currentSnippet.id); + } + } else { + createSnippet(formData); + } }; return ( @@ -119,7 +147,11 @@ export const SnippetForm = (): JSX.Element => { {/* SUBMIT SECTION */}
-
diff --git a/client/src/containers/Editor.tsx b/client/src/containers/Editor.tsx index 1ba8df5..0a5509f 100644 --- a/client/src/containers/Editor.tsx +++ b/client/src/containers/Editor.tsx @@ -1,25 +1,41 @@ -import { Fragment } from 'react'; -import { useLocation } from 'react-router-dom'; +import { Fragment, useEffect, useContext, useState } from 'react'; +import { useLocation, useParams } from 'react-router-dom'; import { SnippetForm } from '../components/Snippets/SnippetForm'; import { Layout, PageHeader } from '../components/UI'; +import { SnippetsContext } from '../store'; import { Snippet } from '../typescript/interfaces'; -interface Props { - snippet?: Snippet; +interface Params { + id?: string; } -export const Editor = (props: Props): JSX.Element => { - const { snippet } = props; +export const Editor = (): JSX.Element => { + const { setSnippet: setCurrentSnippet } = useContext(SnippetsContext); + const [inEdit, setInEdit] = useState(false); // Get previous location const location = useLocation<{ from: string }>(); const { from } = location.state || '/snippets'; + // Get id + const { id } = useParams(); + + // Set snippet + useEffect(() => { + setCurrentSnippet(-1); + + if (id) { + setCurrentSnippet(+id); + setInEdit(true); + } + }, []); + return ( - {snippet ? ( + {inEdit ? ( - + + ) : ( diff --git a/client/src/store/SnippetsContext.tsx b/client/src/store/SnippetsContext.tsx index a9bf989..15d44a5 100644 --- a/client/src/store/SnippetsContext.tsx +++ b/client/src/store/SnippetsContext.tsx @@ -17,6 +17,8 @@ export const SnippetsContext = createContext({ getSnippetById: (id: number) => {}, setSnippet: (id: number) => {}, createSnippet: (snippet: NewSnippet) => {}, + updateSnippet: (snippet: NewSnippet, id: number) => {}, + deleteSnippet: (id: number) => {}, countSnippets: () => {} }); @@ -69,6 +71,39 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { .catch(err => console.log(err)); }; + const updateSnippet = (snippet: NewSnippet, id: number): void => { + axios + .put>(`/api/snippets/${id}`, snippet) + .then(res => { + const oldSnippetIdx = snippets.findIndex(s => s.id === id); + setSnippets([ + ...snippets.slice(0, oldSnippetIdx), + res.data.data, + ...snippets.slice(oldSnippetIdx + 1) + ]); + setCurrentSnippet(res.data.data); + history.push(`/snippet/${res.data.data.id}`, { from: '/snippets' }); + }) + .catch(err => console.log(err)); + }; + + const deleteSnippet = (id: number): void => { + if (window.confirm('Are you sure you want to delete this snippet?')) { + axios + .delete>(`/api/snippets/${id}`) + .then(res => { + const deletedSnippetIdx = snippets.findIndex(s => s.id === id); + setSnippets([ + ...snippets.slice(0, deletedSnippetIdx), + ...snippets.slice(deletedSnippetIdx + 1) + ]); + setSnippet(-1); + history.push('/snippets'); + }) + .catch(err => console.log(err)); + } + }; + const countSnippets = (): void => { axios .get>('/api/snippets/statistics/count') @@ -84,6 +119,8 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { getSnippetById, setSnippet, createSnippet, + updateSnippet, + deleteSnippet, countSnippets }; diff --git a/client/src/typescript/interfaces/Context.ts b/client/src/typescript/interfaces/Context.ts index bd99dc1..a386924 100644 --- a/client/src/typescript/interfaces/Context.ts +++ b/client/src/typescript/interfaces/Context.ts @@ -8,5 +8,7 @@ export interface Context { getSnippetById: (id: number) => void; setSnippet: (id: number) => void; createSnippet: (snippet: NewSnippet) => void; + updateSnippet: (snippet: NewSnippet, id: number) => void; + deleteSnippet: (id: number) => void; countSnippets: () => void; }