mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 21:33:10 +01:00
Edit and delete snippet functionality
This commit is contained in:
@@ -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>
|
||||
|
||||
84
client/src/components/Snippets/SnippetDetails.tsx
Normal file
84
client/src/components/Snippets/SnippetDetails.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user