Edit and delete snippet functionality

This commit is contained in:
unknown
2021-09-22 12:57:36 +02:00
parent f7614302a3
commit 96a01c6f60
6 changed files with 185 additions and 14 deletions

View File

@@ -12,7 +12,7 @@ export const App = () => {
<Route exact path='/' component={Home} />
<Route path='/snippets' component={Snippets} />
<Route path='/snippet/:id' component={Snippet} />
<Route path='/editor' component={Editor} />
<Route path='/editor/:id?' component={Editor} />
</Switch>
</SnippetsContextProvider>
</BrowserRouter>

View File

@@ -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 (
<Card title={title}>
<p>{description}</p>
{/* LANGUAGE */}
<div className={`d-flex justify-content-between`}>
<span>Language</span>
<span className='fw-bold'>{language}</span>
</div>
{/* CREATED AT */}
<div className={`d-flex justify-content-between`}>
<span>Created</span>
<span>{creationDate.relative}</span>
</div>
{/* UPDATED AT */}
<div className={`d-flex justify-content-between`}>
<span>Last updated</span>
<span>{updateDate.relative}</span>
</div>
<hr />
{/* ACTIONS */}
<div className='d-flex justify-content-between'>
<Link
to={{
pathname: `/editor/${id}`,
state: { from: window.location.pathname }
}}
>
<Button text='Edit' color='dark' small outline classes='me-3' />
</Link>
<Button
text='Pin to homescreen'
color='dark'
small
outline
handler={copyHandler}
classes='me-3'
/>
<Button
text='Delete'
color='danger'
small
outline
handler={() => deleteSnippet(id)}
/>
</div>
<hr />
{/* COPY */}
<div className='d-grid'>
<Button text='Copy code' color='dark' small handler={copyHandler} />
</div>
</Card>
);
};

View File

@@ -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<NewSnippet>({
title: '',
@@ -14,6 +27,14 @@ export const SnippetForm = (): JSX.Element => {
docs: ''
});
useEffect(() => {
if (inEdit) {
if (currentSnippet) {
setFormData({ ...currentSnippet });
}
}
}, [currentSnippet]);
const inputHandler = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
@@ -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 */}
<div className='d-grid'>
<Button text='Create snippet' color='dark' type='submit' />
<Button
text={`${inEdit ? 'Update snippet' : 'Create snippet'}`}
color='dark'
type='submit'
/>
</div>
</form>
</Card>

View File

@@ -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<Params>();
// Set snippet
useEffect(() => {
setCurrentSnippet(-1);
if (id) {
setCurrentSnippet(+id);
setInEdit(true);
}
}, []);
return (
<Layout>
{snippet ? (
{inEdit ? (
<Fragment>
<PageHeader title='edit snippet' prevDest={from} />
<PageHeader title='Edit snippet' prevDest={from} />
<SnippetForm inEdit />
</Fragment>
) : (
<Fragment>

View File

@@ -17,6 +17,8 @@ export const SnippetsContext = createContext<Context>({
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<Response<Snippet>>(`/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<Response<{}>>(`/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<Response<LanguageCount[]>>('/api/snippets/statistics/count')
@@ -84,6 +119,8 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
getSnippetById,
setSnippet,
createSnippet,
updateSnippet,
deleteSnippet,
countSnippets
};

View File

@@ -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;
}