From 1139da4da7f1bb0ee4a66d420e690beed36832c2 Mon Sep 17 00:00:00 2001 From: Vidhu Kant Sharma Date: Sun, 4 Dec 2022 21:37:52 +0530 Subject: added notification system --- src/App.js | 6 +- src/App.scss | 19 +++++ src/_colors.scss | 2 + src/_styles.scss | 40 --------- src/classes/item.js | 8 +- src/classes/notifications.js | 31 +++++++ src/components/editors/brand-editor.js | 15 +++- src/components/editors/client-editor.js | 24 ++++-- src/components/editors/item-editor.js | 24 +++++- src/components/editors/multi-address-editor.js | 1 - src/components/navbar/navbar.js | 2 +- src/components/navbar/navbar.scss | 3 +- src/components/tables/brand-table.js | 20 ++++- src/components/tables/client-table.js | 24 ++++-- src/components/tables/item-table.js | 20 ++++- src/notifications_styles/_containers.scss | 100 ++++++++++++++++++++++ src/notifications_styles/_types.scss | 91 ++++++++++++++++++++ src/notifications_styles/_variables.scss | 31 +++++++ src/notifications_styles/notification.scss | 110 +++++++++++++++++++++++++ src/views/homepage.js | 15 +++- src/views/login/register.js | 17 +++- src/views/manage/brands.js | 12 ++- src/views/manage/clients.js | 10 ++- src/views/manage/items.js | 12 ++- 24 files changed, 553 insertions(+), 84 deletions(-) delete mode 100644 src/_styles.scss create mode 100644 src/classes/notifications.js create mode 100644 src/notifications_styles/_containers.scss create mode 100644 src/notifications_styles/_types.scss create mode 100644 src/notifications_styles/_variables.scss create mode 100644 src/notifications_styles/notification.scss (limited to 'src') diff --git a/src/App.js b/src/App.js index 9fd5081..8dfbf81 100644 --- a/src/App.js +++ b/src/App.js @@ -16,7 +16,9 @@ */ import { BrowserRouter, Route, Routes } from "react-router-dom"; -import './App.scss'; +import { ReactNotifications } from "react-notifications-component"; +import "./notifications_styles/notification.scss" + import Navbar from './components/navbar/navbar'; import HomePage from './views/homepage'; import RegisterPage from './views/login/register'; @@ -27,11 +29,13 @@ import ManageItemsPage from './views/manage/items'; import ManageClientsPage from './views/manage/clients'; import ManageBrandsPage from './views/manage/brands'; import ManageInvoicesPage from './views/manage/invoices'; +import './App.scss'; const App = () => { return ( +
}/> diff --git a/src/App.scss b/src/App.scss index 1b37ffe..f7fdf46 100644 --- a/src/App.scss +++ b/src/App.scss @@ -86,3 +86,22 @@ $selectionColor: rgba($primaryAccentColor, 0.9) } } +.floating-wrapper { + height: 100vh; + width: 100vw; + box-sizing: border-box; + position: fixed; + top: 0; + left: 0; + background-color: rgba($backgroundColor, 0.3); + z-index: 5; + display: flex; + justify-content: center; + align-items: center; + backdrop-filter: blur(2px); + .floating-window { + width: 90%; + max-width: 1200px; + z-index: 6; + } +} diff --git a/src/_colors.scss b/src/_colors.scss index 08a700c..0b73ab9 100644 --- a/src/_colors.scss +++ b/src/_colors.scss @@ -30,6 +30,8 @@ $darkgray: #232627; $black: black; $warningColor: #ed4683; +$infoColor: #e8b454; +$successColor: #0ec685; $fgColor: $white; $darkFgColor: $black; diff --git a/src/_styles.scss b/src/_styles.scss deleted file mode 100644 index 064b9c6..0000000 --- a/src/_styles.scss +++ /dev/null @@ -1,40 +0,0 @@ -/* OpenBills-web - Web based libre billing software - * Copyright (C) 2022 Vidhu Kant Sharma - - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -@import "colors"; - -@mixin floating-window { - .floating-wrapper { - height: 100vh; - width: 100vw; - box-sizing: border-box; - position: fixed; - top: 0; - left: 0; - background-color: rgba($backgroundColor, 0.3); - z-index: 5; - display: flex; - justify-content: center; - align-items: center; - backdrop-filter: blur(2px); - .floating-window { - width: 90%; - max-width: 1200px; - z-index: 6; - } - } -} diff --git a/src/classes/item.js b/src/classes/item.js index 63f65ca..53f4bcd 100644 --- a/src/classes/item.js +++ b/src/classes/item.js @@ -54,25 +54,25 @@ export class InvoiceItem extends Item { export const saveItem = (item, ok, fail) => { axios.post("/item/new", item) .then(res => ok()) - .catch((err) => fail()) + .catch(err => fail(err)) } export const deleteItem = (id, ok, fail) => { axios.delete(`/item/${id}`) .then(res => ok()) - .catch((err) => fail()) + .catch(err => fail(err)) } export const getAllItems = (ok, fail) => { axios.get("/item/all") .then(res => ok(res.data)) - .catch(err => fail()) + .catch(err => fail(err)) } export const editItem = (item, ok, fail) => { axios.put(`/item/${item.Id}`, item) .then(res => ok()) - .catch(err => fail()); + .catch(err => fail(err)); } export const getDiscountValue = (item) => diff --git a/src/classes/notifications.js b/src/classes/notifications.js new file mode 100644 index 0000000..5ffdec8 --- /dev/null +++ b/src/classes/notifications.js @@ -0,0 +1,31 @@ +/* OpenBills-web - Web based libre billing software + * Copyright (C) 2022 Vidhu Kant Sharma + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// TODO: load defaults from config +export function notificationConfig(type, duration=3000) { + return { + type: type, + insert: "top", + container: "top-right", + animationIn: ["animate__animated", "animate__fadeIn"], + animationOut: ["animate__animated", "animate__fadeOut"], + dismiss: { + duration: duration, + onScreen: true + } + }; +} diff --git a/src/components/editors/brand-editor.js b/src/components/editors/brand-editor.js index 7796c68..2b7806e 100644 --- a/src/components/editors/brand-editor.js +++ b/src/components/editors/brand-editor.js @@ -16,8 +16,10 @@ */ import { Brand, saveBrand, editBrand } from './../../classes/brand' +import { notificationConfig } from "./../../classes/notifications"; import './scss/brand-editor.scss' +import { Store } from "react-notifications-component"; import { useState } from 'react'; const BrandEditor = (props) => { @@ -36,13 +38,22 @@ const BrandEditor = (props) => { } const handleSuccess = () => { + Store.addNotification({ + title: `Successfully ${props.editing ? "edited" : "added"} brand!`, + message: `${name} has successfully been ${props.editing ? "edited" : "saved"}.`, + ...notificationConfig("success") + }); clearAll(); props.callback(); props.editing && props.hide(); } - const handleFail = () => { - alert("fail"); + const handleFail = err => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to ${props.editing ? "edit" : "add"} brand '${name}'. ${err.message}`, + ...notificationConfig("danger") + }); } const clearAll = () => { diff --git a/src/components/editors/client-editor.js b/src/components/editors/client-editor.js index 9a752c7..96ab639 100644 --- a/src/components/editors/client-editor.js +++ b/src/components/editors/client-editor.js @@ -16,11 +16,13 @@ */ import { Client, saveClient, editClient, Contact, Address } from './../../classes/client'; +import { notificationConfig } from "./../../classes/notifications"; import MultiAddressEditor from './multi-address-editor'; import AddressEditor from './address-editor'; import ContactEditor from './contact-editor'; import './scss/client-editor.scss'; +import { Store } from "react-notifications-component"; import { useState, useEffect } from 'react'; const ClientEditor = (props) => { @@ -37,8 +39,8 @@ const ClientEditor = (props) => { // will delete existing shipping addresses if false useEffect(() => - setShippingAddresses(shipToBillingAddress ? [] : (shippingAddresses.length > 0 ? shippingAddresses : [new Address()])) - , [shipToBillingAddress, shippingAddresses]); + setShippingAddresses(i => shipToBillingAddress ? [] : (i.length > 0 ? i : [new Address()])) + , [shipToBillingAddress]); const handleSubmit = (e) => { e.preventDefault(); @@ -63,15 +65,23 @@ const ClientEditor = (props) => { } const handleSuccess = (res) => { - console.log("Successfully saved client", res) + Store.addNotification({ + title: `Successfully ${props.editing ? "edited" : "added"} client!`, + message: `${name} has successfully been ${props.editing ? "edited" : "saved"}.`, + ...notificationConfig("success") + }); clearAll(); - props.successCallback(); + props.successCallback && props.successCallback(); props.editing && props.hide(); } - const handleFail = (err) => { - alert("error while saving client. please check logs"); - console.log(err); + const handleFail = err => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to edit client ${name}. ${err.message}`, + ...notificationConfig("danger") + }); + console.log(err) } const clearAll = () => { diff --git a/src/components/editors/item-editor.js b/src/components/editors/item-editor.js index be0ac4f..c1d260d 100644 --- a/src/components/editors/item-editor.js +++ b/src/components/editors/item-editor.js @@ -17,9 +17,11 @@ import { Item, saveItem, editItem } from './../../classes/item'; import { Brand, getAllBrands } from './../../classes/brand'; +import { notificationConfig } from "./../../classes/notifications"; import './scss/item-editor.scss'; import { useState, useEffect } from 'react'; +import { Store } from "react-notifications-component"; const ItemEditor = (props) => { const [name, setName] = useState(props.item.Name); @@ -37,8 +39,13 @@ const ItemEditor = (props) => { // get saved brands from API // needed by the brands dropdown menu useEffect(() => { - // TODO: handle error - getAllBrands(setSavedBrands, () => {}); + getAllBrands(setSavedBrands, err => { + Store.addNotification({ + title: "Error while getting Brands list.", + message: err.message, + ...notificationConfig("danger") + }); + }); }, []) const handleSubmit = (e) => { @@ -76,13 +83,22 @@ const ItemEditor = (props) => { } const handleSuccess = () => { + Store.addNotification({ + title: `Successfully ${props.editing ? "edited" : "added"} item!`, + message: `${name} has successfully been ${props.editing ? "edited" : "saved"}.`, + ...notificationConfig("success") + }); clearAll(); props.callback(); props.editing && props.hide(); } - const handleFail = () => { - alert("fail"); + const handleFail = err => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to ${props.editing ? "edit" : "add"} item ${name}. ${err.message}`, + ...notificationConfig("danger") + }); } const handleBrandSelect = (e) => { diff --git a/src/components/editors/multi-address-editor.js b/src/components/editors/multi-address-editor.js index 1159fab..1ea58b7 100644 --- a/src/components/editors/multi-address-editor.js +++ b/src/components/editors/multi-address-editor.js @@ -18,7 +18,6 @@ import AddressEditor from './address-editor'; const MultiAddressEditor = ({addresses, setAddresses, setShipToBillingAddress}) => { - console.log(addresses) const handleChange = (id, data) => { const newAddresses = [...addresses]; newAddresses[id] = { diff --git a/src/components/navbar/navbar.js b/src/components/navbar/navbar.js index bf909e7..1254952 100644 --- a/src/components/navbar/navbar.js +++ b/src/components/navbar/navbar.js @@ -58,7 +58,7 @@ const Navbar = () => {
- App Logo + App Logo diff --git a/src/components/navbar/navbar.scss b/src/components/navbar/navbar.scss index 8173249..a7f560e 100644 --- a/src/components/navbar/navbar.scss +++ b/src/components/navbar/navbar.scss @@ -52,8 +52,9 @@ .logo { flex: 1; + height: 3.5rem; img { - height: 4rem; + height: 3.5rem; } } } diff --git a/src/components/tables/brand-table.js b/src/components/tables/brand-table.js index db8272c..3b38a5c 100644 --- a/src/components/tables/brand-table.js +++ b/src/components/tables/brand-table.js @@ -17,6 +17,9 @@ import './scss/brand-table.scss'; import { deleteBrand } from './../../classes/brand'; +import { notificationConfig } from "./../../classes/notifications"; + +import { Store } from "react-notifications-component"; const BrandTable = (props) => { const handleEdit = (b) => { @@ -25,15 +28,24 @@ const BrandTable = (props) => { const handleDelete = (b) => { // TODO: add confirmation prompt - deleteBrand(b.Id, handleDelSuccess, handleDelFail); + deleteBrand(b.Id, () => handleDelSuccess(b), err => handleDelFail(b, err)); } - const handleDelSuccess = () => { + const handleDelSuccess = b => { + Store.addNotification({ + title: "Successfully deleted brand!", + message: `Brand '${b.Name}' has successfully been deleted.`, + ...notificationConfig("success") + }); props.refresh(); } - const handleDelFail = () => { - alert("fail") + const handleDelFail = (b, err) => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to delete brand '${b.Name}'. ${err.message}`, + ...notificationConfig("danger") + }); } return ( diff --git a/src/components/tables/client-table.js b/src/components/tables/client-table.js index cea612d..1f51c88 100644 --- a/src/components/tables/client-table.js +++ b/src/components/tables/client-table.js @@ -17,23 +17,35 @@ import './scss/client-table.scss'; import { deleteClient } from './../../classes/client'; +import { notificationConfig } from "./../../classes/notifications"; + +import { Store } from "react-notifications-component"; const ClientTable = (props) => { - const handleEdit = (c) => { + const handleEdit = c => { props.setClientToEdit(c) } - const handleDelete = (c) => { + const handleDelete = c => { // TODO: add confirmation prompt - deleteClient(c.Id, handleDelSuccess, handleDelFail); + deleteClient(c.Id, () => handleDelSuccess(c), err => handleDelFail(c, err)); } - const handleDelSuccess = () => { + const handleDelSuccess = c => { + Store.addNotification({ + title: "Successfully deleted client!", + message: `Client '${c.Name}' has successfully been deleted.`, + ...notificationConfig("success") + }); props.refresh(); } - const handleDelFail = () => { - alert("fail") + const handleDelFail = (c, err) => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to delete client '${c.Name}'. ${err.message}`, + ...notificationConfig("danger") + }); } return ( diff --git a/src/components/tables/item-table.js b/src/components/tables/item-table.js index 2fb7210..1b47f6f 100644 --- a/src/components/tables/item-table.js +++ b/src/components/tables/item-table.js @@ -17,6 +17,9 @@ import './scss/table.scss'; import { deleteItem } from './../../classes/item'; +import { notificationConfig } from "./../../classes/notifications"; + +import { Store } from "react-notifications-component"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faPencil, faTrashCan } from '@fortawesome/free-solid-svg-icons' @@ -27,15 +30,24 @@ const ItemTable = (props) => { const handleDelete = (i) => { // TODO: add confirmation prompt - deleteItem(i.Id, handleDelSuccess, handleDelFail); + deleteItem(i.Id, () => handleDelSuccess(i), err => handleDelFail(i, err)); } - const handleDelSuccess = () => { + const handleDelSuccess = i => { + Store.addNotification({ + title: "Successfully deleted item!", + message: `Item '${i.Name}' has successfully been deleted.`, + ...notificationConfig("success") + }); props.refresh(); } - const handleDelFail = () => { - alert("fail") + const handleDelFail = (i, err) => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to delete item '${i.Name}'. ${err.message}`, + ...notificationConfig("danger") + }); } return ( diff --git a/src/notifications_styles/_containers.scss b/src/notifications_styles/_containers.scss new file mode 100644 index 0000000..b0f00a4 --- /dev/null +++ b/src/notifications_styles/_containers.scss @@ -0,0 +1,100 @@ +.rnc__notification-container--top-center, +.rnc__notification-container--top-left, +.rnc__notification-container--top-right, +.rnc__notification-container--bottom-center, +.rnc__notification-container--bottom-left, +.rnc__notification-container--bottom-right, +.rnc__notification-container--center, +.rnc__notification-container--top-full, +.rnc__notification-container--bottom-full { + min-width: 325px; + position: absolute; + pointer-events: all; +} + +.rnc__notification-container--center, +.rnc__notification-container--top-center, +.rnc__notification-container--bottom-center { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + left: calc(50% - 175px); +} + +.rnc__notification-container--center, +.rnc__notification-container--top-center, +.rnc__notification-container--bottom-center { + max-width: 350px; +} + +.rnc__notification-container--center { + top: 20px; + height: 100%; + pointer-events: none; +} + +.rnc__notification-container--top-full, +.rnc__notification-container--bottom-full { + width: 100%; + min-width: 100%; +} + +.rnc__notification-container--bottom-full { + bottom: 0; +} + +.rnc__util--flex-center { + min-width: 325px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + pointer-events: all; +} + +.rnc__notification-container--top-center { + top: 20px; +} +.rnc__notification-container--bottom-center { + bottom: 20px; +} + +.rnc__notification-container--top-left { + left: 20px; + top: 20px; +} + +.rnc__notification-container--top-right { + right: 20px; + top: 20px; +} + +.rnc__notification-container--bottom-left { + left: 20px; + bottom: 20px; +} + +.rnc__notification-container--bottom-right { + bottom: 20px; + right: 20px; +} + +.rnc__notification-container--mobile-top, +.rnc__notification-container--mobile-bottom { + pointer-events: all; + position: absolute; +} + +.rnc__notification-container--mobile-top { + right: 20px; + left: 20px; + top: 20px; +} + +.rnc__notification-container--mobile-bottom { + right: 20px; + left: 20px; + bottom: 20px; + margin-bottom: -15px; +} diff --git a/src/notifications_styles/_types.scss b/src/notifications_styles/_types.scss new file mode 100644 index 0000000..ccfbee2 --- /dev/null +++ b/src/notifications_styles/_types.scss @@ -0,0 +1,91 @@ +@import "_variables.scss"; + +.rnc__notification-item--default { + background-color: $default; + border-left: 8px solid $default_dark; + + .rnc__notification-timer { + background-color: $default_timer; + } + .rnc__notification-timer-filler { + background-color: $default_timer_filler; + } + .rnc__notification-close-mark { + background-color: $default; + } +} + +.rnc__notification-item--success { + background-color: $success; + border-left: 8px solid $success_dark; + + .rnc__notification-timer { + background-color: $success_timer; + } + .rnc__notification-timer-filler { + background-color: $success_timer_filler; + } + .rnc__notification-close-mark { + background-color: $success; + } +} + +.rnc__notification-item--danger { + background-color: $danger; + border-left: 8px solid $danger_dark; + + .rnc__notification-timer { + background-color: $danger_timer; + } + .rnc__notification-timer-filler { + background-color: $danger_timer_filler; + } + .rnc__notification-close-mark { + background-color: $danger; + } +} + +.rnc__notification-item--info { + background-color: $info; + border-left: 8px solid $info_dark; + + .rnc__notification-timer { + background-color: $info_timer; + } + .rnc__notification-timer-filler { + background-color: $info_timer_filler; + } + .rnc__notification-close-mark { + background-color: $info; + } +} + +.rnc__notification-item--warning { + background-color: $warning; + border-left: 8px solid $warning_dark; + + .rnc__notification-timer { + background-color: $warning_timer; + } + .rnc__notification-timer-filler { + background-color: $warning_timer_filler; + } + .rnc__notification-close-mark { + background-color: $warning; + } +} + +.rnc__notification-item--awesome { + background-color: $awesome; + border-left: 8px solid $awesome_dark; + + .rnc__notification-timer { + background-color: $awesome_timer; + } + .rnc__notification-timer-filler { + background-color: $awesome_timer_filler; + } + .rnc__notification-close-mark { + background-color: $awesome; + } +} \ No newline at end of file diff --git a/src/notifications_styles/_variables.scss b/src/notifications_styles/_variables.scss new file mode 100644 index 0000000..a65c811 --- /dev/null +++ b/src/notifications_styles/_variables.scss @@ -0,0 +1,31 @@ +@import "../colors"; + +$default: $altBackgroundColor !default; +$default_dark: $primaryAccentColor !default; +$default_timer: $backgroundColor !default; +$default_timer_filler: $primaryAccentColor !default; + +$success: $altBackgroundColor !default; +$success_dark: $successColor !default; +$success_timer: $backgroundColor !default; +$success_timer_filler: $successColor !default; + +$danger: $altBackgroundColor !default; +$danger_dark: $warningColor !default; +$danger_timer: $backgroundColor !default; +$danger_timer_filler: $warningColor !default; + +$warning: $altBackgroundColor !default; +$warning_dark: $infoColor !default; +$warning_timer: $backgroundColor !default; +$warning_timer_filler: $infoColor !default; + +$info: $default; +$info_dark: $default_dark; +$info_timer: $default_timer; +$info_timer_filler: $default_timer_filler; + +$awesome: $success; +$awesome_dark: $success_dark; +$awesome_timer: $success_timer; +$awesome_timer_filler: $success_timer_filler; diff --git a/src/notifications_styles/notification.scss b/src/notifications_styles/notification.scss new file mode 100644 index 0000000..33d8efb --- /dev/null +++ b/src/notifications_styles/notification.scss @@ -0,0 +1,110 @@ +@import "./_containers.scss"; +@import "./_types.scss"; + +@keyframes timer { + 0% { width: 100%; } + 100% { width: 0%; } +} + +.rnc__base { + position: fixed; + z-index: 9000; + pointer-events: none; + width: 100%; + height: 100%; +} + +.rnc__notification-item { + display: flex; + position: relative; + border-radius: 3px; + margin-bottom: 15px; + box-shadow: 1px 3px 4px rgba(0, 0, 0, 0.2); + cursor: pointer; +} + +.rnc__notification-container--top-full .rnc__notification-item, +.rnc__notification-container--bottom-full .rnc__notification-item { + margin-bottom: 0; + border-radius: 0; +} + +.rnc__notification-container--top-full .rnc__notification, +.rnc__notification-container--bottom-full .rnc__notification { + width: 100% !important; +} + +.rnc__notification-timer { + width: 100%; + height: 3px; + margin-top: 10px; + border-radius: 5px; +} +.rnc__notification-timer-filler { + height: 3px; + border-radius: 5px; +} +.rnc__notification-title { + color: #fff; + font-weight: 700; + font-size: 14px; + margin-top: 5px; + margin-bottom: 5px; +} +.rnc__notification-message { + color: #fff; + max-width: calc(100% - 15px); + font-size: 14px; + line-height: 150%; + word-wrap: break-word; + margin-bottom: 0; + margin-top: 0; +} +.rnc__notification-content { + padding: 8px 15px; + display: inline-block; + width: 100%; +} +.rnc__notification-close-mark { + width: 18px; + height: 18px; + border-radius: 50%; + display: inline-block; + position: absolute; + right: 10px; + top: 10px; + + &::after { + content: '\D7'; + position: absolute; + transform: translate(-50%, -50%); + color: #fff; + font-size: 12px; + left: 50%; + top: 50%; + } +} + +.rnc__notification-container--mobile-top .rnc__notification-item, +.rnc__notification-container--mobile-bottom .rnc__notification-item, +.rnc__notification-container--mobile-top .notification, +.rnc__notification-container--mobile-bottom .notification { + max-width: 100%; + width: 100%; +} + +.rnc__notification-container--top-right .notification, +.rnc__notification-container--bottom-right .notification { + margin-left: auto; +} + +.rnc__notification-container--top-left .notification, +.rnc__notification-container--bottom-left .notification { + margin-right: auto; +} + +.rnc__notification-container--mobile-top .notification, +.rnc__notification-container--mobile-bottom .notification { + margin-left: auto; + margin-right: auto; +} \ No newline at end of file diff --git a/src/views/homepage.js b/src/views/homepage.js index a9cdb50..92f9544 100644 --- a/src/views/homepage.js +++ b/src/views/homepage.js @@ -15,9 +15,22 @@ * along with this program. If not, see . */ -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; +import { notificationConfig } from "../classes/notifications"; +import { Store } from "react-notifications-component"; const HomePage = () => { + // this is temporary, just for testing + // TODO: find better way to do this + const navigate = useNavigate(); + if (!localStorage.getItem("accessToken")) { + Store.addNotification({ + title: "You are not logged in", + message: "You need to log in before accessing this page.", + ...notificationConfig("default") + }); + navigate("/login") + } return ( <>

Welcome to OpenBills

diff --git a/src/views/login/register.js b/src/views/login/register.js index c747f59..1b1755b 100644 --- a/src/views/login/register.js +++ b/src/views/login/register.js @@ -17,12 +17,15 @@ import './scss/login.scss'; import { User, validateEmail, validateUsername, validatePassword, saveUser } from '../../classes/user'; +import { notificationConfig } from "./../../classes/notifications"; +import { Store } from "react-notifications-component"; import { Link } from 'react-router-dom'; import { useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faEye } from '@fortawesome/free-solid-svg-icons' + const RegisterPage = () => { const [user, setUser] = useState(new User()); const [showPassword, setShowPassword] = useState(false); @@ -41,11 +44,19 @@ const RegisterPage = () => { } const handleSuccess = () => { - alert("yay") + Store.addNotification({ + title: "Created new account", + message: `Welcome to OpenBills, ${user.UserName}!`, + ...notificationConfig("default") + }); } - const handleError = () => { - alert("fail") + const handleError = err => { + Store.addNotification({ + title: "An error occoured", + message: `Failed to create new account. ${err.message}`, + ...notificationConfig("danger") + }); } return ( diff --git a/src/views/manage/brands.js b/src/views/manage/brands.js index dad21e2..4ab3924 100644 --- a/src/views/manage/brands.js +++ b/src/views/manage/brands.js @@ -16,18 +16,26 @@ */ import { useState, useEffect } from 'react'; +import { Store } from "react-notifications-component"; import './scss/management-page.scss' import { Brand, getAllBrands } from '../../classes/brand'; import BrandEditor from './../../components/editors/brand-editor'; import BrandTable from './../../components/tables/brand-table'; +import { notificationConfig } from "./../../classes/notifications"; const ManageBrandsPage = () => { const [brandToEdit, setBrandToEdit] = useState(new Brand()); const [allBrands, setAllBrands] = useState([]); - // TODO: handle error + const updateList = () => - getAllBrands(setAllBrands, () => {}); + getAllBrands(setAllBrands, err => { + Store.addNotification({ + title: "Error while getting Brands list.", + message: err.message, + ...notificationConfig("danger") + }); + }); useEffect(() => { updateList(); diff --git a/src/views/manage/clients.js b/src/views/manage/clients.js index 8445d80..89e5ade 100644 --- a/src/views/manage/clients.js +++ b/src/views/manage/clients.js @@ -20,18 +20,26 @@ */ import { useState, useEffect } from 'react'; +import { Store } from "react-notifications-component"; import './scss/management-page.scss'; import { Client, getAllClients } from '../../classes/client'; import ClientEditor from './../../components/editors/client-editor'; import ClientTable from './../../components/tables/client-table'; +import { notificationConfig } from "./../../classes/notifications"; const ManageClientsPage = () => { const [clientToEdit, setClientToEdit] = useState(new Client()); const [allClients, setAllClients] = useState([]); // TODO: handle error const updateList = () => - getAllClients(setAllClients, () => {}); + getAllClients(setAllClients, err => { + Store.addNotification({ + title: "Error while getting Clients list.", + message: err.message, + ...notificationConfig("danger") + }); + }); useEffect(() => { updateList(); diff --git a/src/views/manage/items.js b/src/views/manage/items.js index 567be7c..2a862bc 100644 --- a/src/views/manage/items.js +++ b/src/views/manage/items.js @@ -20,18 +20,26 @@ */ import { useState, useEffect } from 'react'; +import { Store } from "react-notifications-component"; import './scss/management-page.scss' import { Item, getAllItems } from '../../classes/item'; import ItemEditor from './../../components/editors/item-editor'; import ItemTable from './../../components/tables/item-table'; +import { notificationConfig } from "./../../classes/notifications"; const ManageItemsPage = () => { const [itemToEdit, setItemToEdit] = useState(new Item()); const [allItems, setAllItems] = useState([]); - // TODO: handle error + const updateList = () => - getAllItems(setAllItems, () => {}); + getAllItems(setAllItems, err => { + Store.addNotification({ + title: "Error while getting Items list.", + message: err.message, + ...notificationConfig("danger") + }); + }); useEffect(() => { updateList(); -- cgit v1.2.3