diff --git a/client/src/components/Snippets/SnippetCard.tsx b/client/src/components/Snippets/SnippetCard.tsx index 67f9c45..3e8c873 100644 --- a/client/src/components/Snippets/SnippetCard.tsx +++ b/client/src/components/Snippets/SnippetCard.tsx @@ -12,7 +12,7 @@ interface Props { } export const SnippetCard = (props: Props): JSX.Element => { - const { title, description, language, code, id, updatedAt, isPinned } = + const { title, description, language, code, id, createdAt, isPinned } = props.snippet; const { setSnippet } = useContext(SnippetsContext); @@ -21,47 +21,47 @@ export const SnippetCard = (props: Props): JSX.Element => { }; return ( - + {/* TITLE */}
{title} {isPinned ? : ''}
- {/* UPDATE DATE */}
- {dateParser(updatedAt).relative} + {/* LANGUAGE */} +
{/* DESCRIPTION */} -

- {description ? description : 'No description'} -

+

{description ? description : 'No description'}

- {/* LANGUAGE */} - -
+
+ {/* UPDATE DATE */} +

Created {dateParser(createdAt).relative}

+
- {/* ACTIONS */} -
- -
); diff --git a/client/src/components/Snippets/SnippetGrid.tsx b/client/src/components/Snippets/SnippetGrid.tsx index 85e4fd7..e60b3c9 100644 --- a/client/src/components/Snippets/SnippetGrid.tsx +++ b/client/src/components/Snippets/SnippetGrid.tsx @@ -9,9 +9,9 @@ export const SnippetGrid = (props: Props): JSX.Element => { const { snippets } = props; return ( -
+
{snippets.map(snippet => ( -
+
))} diff --git a/client/src/components/UI/Card.tsx b/client/src/components/UI/Card.tsx index 8875e2c..18ee470 100644 --- a/client/src/components/UI/Card.tsx +++ b/client/src/components/UI/Card.tsx @@ -1,14 +1,18 @@ interface Props { title?: string; children?: JSX.Element | JSX.Element[]; + classes?: string; + bodyClasses?: string; } export const Card = (props: Props): JSX.Element => { - const { title, children } = props; + const { title, children, classes, bodyClasses } = props; + const parentClasses = `card mb-3 ${classes}`; + const childClasses = `card-body ${bodyClasses}`; return ( -
-
+
+
{title}
{children}
diff --git a/client/src/components/UI/EmptyState.tsx b/client/src/components/UI/EmptyState.tsx new file mode 100644 index 0000000..da88812 --- /dev/null +++ b/client/src/components/UI/EmptyState.tsx @@ -0,0 +1,16 @@ +import { Link } from 'react-router-dom'; + +export const EmptyState = (): JSX.Element => { + const editorLink = ( + + editor + + ); + + return ( +
+

You currently don't have any snippets

+

Go to the {editorLink} and create one

+
+ ); +}; diff --git a/client/src/components/UI/PageHeader.tsx b/client/src/components/UI/PageHeader.tsx index 7d19d32..0efc383 100644 --- a/client/src/components/UI/PageHeader.tsx +++ b/client/src/components/UI/PageHeader.tsx @@ -11,7 +11,7 @@ export const PageHeader = (props: Props): JSX.Element => { return (
-

{title}

+

{title}

{prevDest && (
{ + const { snippets, getSnippets } = useContext(SnippetsContext); + + useEffect(() => { + getSnippets(); + }, []); + + return ( + + {snippets.length === 0 ? ( + + ) : ( + + {snippets.some(s => s.isPinned) && ( + + +
+ s.isPinned)} /> +
+
+ )} + + +
+ !s.isPinned).slice(0, 6)} + /> +
+
+ )} +
+ ); +}; diff --git a/client/src/containers/Snippets.tsx b/client/src/containers/Snippets.tsx index 02b68df..329638c 100644 --- a/client/src/containers/Snippets.tsx +++ b/client/src/containers/Snippets.tsx @@ -1,7 +1,14 @@ import { useEffect, useContext, useState, Fragment } from 'react'; import { SnippetsContext } from '../store'; import { SnippetGrid } from '../components/Snippets/SnippetGrid'; -import { Badge, Button, Card, Layout, Spinner } from '../components/UI'; +import { + Badge, + Button, + Card, + EmptyState, + Layout, + Spinner +} from '../components/UI'; import { Snippet } from '../typescript/interfaces'; export const Snippets = (): JSX.Element => { @@ -33,46 +40,46 @@ export const Snippets = (): JSX.Element => { return ( -
- - - {languageCount.map((el, idx) => { - const isActiveFilter = filter === el.language; + {snippets.length === 0 ? ( + + ) : ( + +
+ + + {languageCount.map((el, idx) => { + const isActiveFilter = filter === el.language; - return ( -
filterHandler(el.language)} - > - {el.language} - {el.count} -
- ); - })} -
-
-
+
-
-
-
- {snippets.length > 0 ? ( - - ) : ( -
- +
+
- )} -
+ + )} ); }; diff --git a/client/src/store/SnippetsContext.tsx b/client/src/store/SnippetsContext.tsx index 36426f9..962c8ed 100644 --- a/client/src/store/SnippetsContext.tsx +++ b/client/src/store/SnippetsContext.tsx @@ -34,18 +34,22 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { const history = useHistory(); + const redirectOnError = () => { + history.push('/'); + }; + const getSnippets = (): void => { axios .get>('/api/snippets') .then(res => setSnippets(res.data.data)) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); }; const getSnippetById = (id: number): void => { axios .get>(`/api/snippets/${id}`) .then(res => setCurrentSnippet(res.data.data)) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); }; const setSnippet = (id: number): void => { @@ -69,9 +73,12 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { .then(res => { setSnippets([...snippets, res.data.data]); setCurrentSnippet(res.data.data); - history.push(`/snippet/${res.data.data.id}`); + history.push({ + pathname: `/snippet/${res.data.data.id}`, + state: { from: '/snippets' } + }); }) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); }; const updateSnippet = (snippet: NewSnippet, id: number): void => { @@ -90,7 +97,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { state: { from: '/snippets' } }); }) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); }; const deleteSnippet = (id: number): void => { @@ -106,7 +113,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { setSnippet(-1); history.push('/snippets'); }) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); } }; @@ -122,7 +129,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { axios .get>('/api/snippets/statistics/count') .then(res => setLanguageCount(res.data.data)) - .catch(err => console.log(err)); + .catch(err => redirectOnError()); }; const context = { diff --git a/src/db/migrations/00_initial.ts b/src/db/migrations/00_initial.ts index b4b9e2e..78ba849 100644 --- a/src/db/migrations/00_initial.ts +++ b/src/db/migrations/00_initial.ts @@ -1,53 +1,45 @@ -import { DataTypes, Model, QueryInterface } from 'sequelize'; -import { - Snippet, - SnippetCreationAttributes -} from '../../typescript/interfaces'; - +import { DataTypes, QueryInterface } from 'sequelize'; const { INTEGER, STRING, DATE, TEXT } = DataTypes; export const up = async (queryInterface: QueryInterface): Promise => { - await queryInterface.createTable>( - 'snippets', - { - id: { - type: INTEGER, - allowNull: false, - primaryKey: true, - autoIncrement: true - }, - title: { - type: STRING, - allowNull: false - }, - description: { - type: TEXT, - allowNull: true, - defaultValue: '' - }, - language: { - type: STRING, - allowNull: false - }, - code: { - type: TEXT, - allowNull: false - }, - docs: { - type: TEXT, - allowNull: true, - defaultValue: '' - }, - createdAt: { - type: DATE, - allowNull: false - }, - updatedAt: { - type: DATE, - allowNull: false - } + await queryInterface.createTable('snippets', { + id: { + type: INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + title: { + type: STRING, + allowNull: false + }, + description: { + type: TEXT, + allowNull: true, + defaultValue: '' + }, + language: { + type: STRING, + allowNull: false + }, + code: { + type: TEXT, + allowNull: false + }, + docs: { + type: TEXT, + allowNull: true, + defaultValue: '' + }, + createdAt: { + type: DATE, + allowNull: false + }, + updatedAt: { + type: DATE, + allowNull: false } - ); + }); }; export const down = async (queryInterface: QueryInterface): Promise => {