From 012b2e71f8c0c9aac69bb228a7d23b504feedb47 Mon Sep 17 00:00:00 2001 From: Degreat Date: Thu, 18 Jan 2024 08:11:14 +0000 Subject: [PATCH] feat: snack to show feedback messages (#49) --- admin/src/app.tsx | 8 +++- admin/src/components/collection-form.tsx | 16 ++++++++ admin/src/components/snacks.tsx | 51 ++++++++++++++++++++++++ admin/src/lib/snacks.ts | 29 ++++++++++++++ admin/src/pages/login.tsx | 8 +++- 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 admin/src/components/snacks.tsx create mode 100644 admin/src/lib/snacks.ts diff --git a/admin/src/app.tsx b/admin/src/app.tsx index 7402a74..5508838 100644 --- a/admin/src/app.tsx +++ b/admin/src/app.tsx @@ -1,6 +1,12 @@ import { RouterProvider } from 'react-router-dom' +import { Snacks } from './components/snacks' import routes from './routes' export function App() { - return + return ( + <> + + + + ) } diff --git a/admin/src/components/collection-form.tsx b/admin/src/components/collection-form.tsx index ffed89b..7d684bd 100644 --- a/admin/src/components/collection-form.tsx +++ b/admin/src/components/collection-form.tsx @@ -15,6 +15,7 @@ import Input from './input' import { Link } from 'react-router-dom' import React from 'preact/compat' import Select from './select' +import { addSnack } from '@/lib/snacks' import app from '../mangobase-app' import { appendIndexFields } from '@/lib/append-index-fields' import appendSchemaFields from '@/lib/append-schema-fields' @@ -197,8 +198,23 @@ function CollectionForm({ collection, onHide }: Props) { try { setSubmitting(true) await save(form, indexes) + + const action = collection ? 'updated' : 'created' + addSnack({ + content: `Collection (${form.name}) ${action} successfully`, + duration: 2500, + type: 'success', + }) } catch (err) { + console.error(err) setSubmitting(false) + + const action = collection ? 'update' : 'create' + addSnack({ + content: `Failed to ${action} collection. Check logs`, + duration: 2500, + type: 'error', + }) } } diff --git a/admin/src/components/snacks.tsx b/admin/src/components/snacks.tsx new file mode 100644 index 0000000..fa8ad08 --- /dev/null +++ b/admin/src/components/snacks.tsx @@ -0,0 +1,51 @@ +import { removeSnack, snacks } from '@/lib/snacks' +import clsx from 'clsx' + +const ICONS = { + error: 'emergency_home', + neutral: 'stat_2', + success: 'verified', +} + +function Snacks() { + return ( +
+
    + {snacks.value.map((snack) => { + if (snack.type === 'custom') return snack.content + + return ( +
  • +
    +
    + + {ICONS[snack.type]} + +
    {snack.content}
    +
    +
    + +
    +
    +
  • + ) + })} +
+
+ ) +} + +export { Snacks } diff --git a/admin/src/lib/snacks.ts b/admin/src/lib/snacks.ts new file mode 100644 index 0000000..275e35a --- /dev/null +++ b/admin/src/lib/snacks.ts @@ -0,0 +1,29 @@ +import React from 'preact/compat' +import randomStr from './random-str' +import { signal } from '@preact/signals' + +interface Snack { + id: string + type: 'success' | 'error' | 'neutral' | 'custom' + content: string | React.ReactNode + duration: number +} + +const snacks = signal([]) + +function addSnack(snack: Omit) { + const id = randomStr(6) + snacks.value = [...snacks.value, { ...snack, id }] + + setTimeout(() => { + removeSnack(id) + }, snack.duration) + + return id +} + +function removeSnack(id: string) { + snacks.value = snacks.value.filter((snack) => snack.id !== id) +} + +export { addSnack, removeSnack, snacks } diff --git a/admin/src/pages/login.tsx b/admin/src/pages/login.tsx index 062c8ff..82b47c1 100644 --- a/admin/src/pages/login.tsx +++ b/admin/src/pages/login.tsx @@ -5,6 +5,7 @@ import Button from '@/components/button' import Input from '@/components/input' import React from 'preact/compat' import RequestStatus from '@/lib/request-status' +import { addSnack } from '@/lib/snacks' import app from '../mangobase-app' import { useNavigate } from 'react-router-dom' @@ -33,8 +34,13 @@ function Login() { app.set('auth', data) navigate('/collections', { replace: true }) - } catch (err) { + } catch (err: any) { setStatus('failed') + const message = err?.data + ? err.data.error + : err.message || 'failed to login. check logs or try again!' + + addSnack({ content: message, duration: 2500, type: 'error' }) } }