diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/App.scss | 7 | ||||
-rw-r--r-- | src/_colors.scss | 25 | ||||
-rw-r--r-- | src/_styles.scss | 40 | ||||
-rw-r--r-- | src/classes/brand.js | 8 | ||||
-rw-r--r-- | src/classes/item.js | 6 | ||||
-rw-r--r-- | src/components/editors/brand-editor.js | 19 | ||||
-rw-r--r-- | src/components/editors/item-editor.js | 90 | ||||
-rw-r--r-- | src/components/editors/scss/brand-editor.scss | 2 | ||||
-rw-r--r-- | src/components/tables/brand-table.js | 6 | ||||
-rw-r--r-- | src/components/tables/client-table.js | 2 | ||||
-rw-r--r-- | src/components/tables/item-table.js | 2 | ||||
-rw-r--r-- | src/views/manage/brands.js | 25 | ||||
-rw-r--r-- | src/views/manage/items.js | 25 | ||||
-rw-r--r-- | src/views/manage/scss/management-page.scss | 4 |
14 files changed, 196 insertions, 65 deletions
diff --git a/src/App.scss b/src/App.scss index 489d354..b4fc72f 100644 --- a/src/App.scss +++ b/src/App.scss @@ -17,15 +17,12 @@ @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500&display=swap'); +@import "colors"; + * { font-family: 'Open Sans', sans-serif; } -$primaryAccentColor: #bd93f9; -$backgroundColor: #282a36; -$linkColor: $primaryAccentColor; -$fgColor: #f8f8f2; - body { margin: 0; padding: 0; diff --git a/src/_colors.scss b/src/_colors.scss new file mode 100644 index 0000000..12e4b92 --- /dev/null +++ b/src/_colors.scss @@ -0,0 +1,25 @@ +/* OpenBills-web - Web based libre billing software + * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz> + + * 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 <https://www.gnu.org/licenses/>. + */ + +$primaryAccentColor: #bd93f9; +$backgroundColor: #282a36; +$linkColor: $primaryAccentColor; + +$white: #f8f8f2; +$black: black; + +$fgColor: $white; diff --git a/src/_styles.scss b/src/_styles.scss new file mode 100644 index 0000000..3faed12 --- /dev/null +++ b/src/_styles.scss @@ -0,0 +1,40 @@ +/* OpenBills-web - Web based libre billing software + * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz> + + * 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 <https://www.gnu.org/licenses/>. + */ + +@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: 1000px; + z-index: 6; + } + } +} diff --git a/src/classes/brand.js b/src/classes/brand.js index 967f9a1..f396c6f 100644 --- a/src/classes/brand.js +++ b/src/classes/brand.js @@ -43,8 +43,14 @@ export const getAllBrands = (ok, fail) => { .catch(err => fail()) } +export const editBrand = (brand, ok, fail) => { + axios.put(`/brand/${brand.id}`, brand) + .then(res => ok()) + .catch(err => fail()) +} + export const saveBrand = (brand, ok, fail) => { axios.post("/brand/new", brand) .then(res => ok()) - .catch((err) => fail()) + .catch(err => fail()) } diff --git a/src/classes/item.js b/src/classes/item.js index 1c59992..bfd0b7b 100644 --- a/src/classes/item.js +++ b/src/classes/item.js @@ -65,3 +65,9 @@ export const getAllItems = (ok, fail) => { .then(res => ok(res.data)) .catch(err => fail()) } + +export const editItem = (item, ok, fail) => { + axios.put(`/item/${item.id}`, item) + .then(res => ok()) + .catch(err => fail()) +} diff --git a/src/components/editors/brand-editor.js b/src/components/editors/brand-editor.js index 801a790..26f4cca 100644 --- a/src/components/editors/brand-editor.js +++ b/src/components/editors/brand-editor.js @@ -15,13 +15,13 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Brand, saveBrand } from './../../classes/brand' +import { Brand, saveBrand, editBrand } from './../../classes/brand' import './scss/brand-editor.scss' import { useState, useEffect } from 'react'; const BrandEditor = (props) => { - const [name, setName] = useState(""); + const [name, setName] = useState(props.brand.Name); const handleSubmit = (e) => { e.preventDefault(); @@ -29,13 +29,15 @@ const BrandEditor = (props) => { const brand = new Brand(); brand.Name = name; - // TODO: Save is for new brands. implement modification too - saveBrand(brand, handleSuccess, handleFail); + props.editing + ? editBrand(brand, handleSuccess, handleFail) + : saveBrand(brand, handleSuccess, handleFail); } const handleSuccess = () => { clearAll(); props.callback(); + props.editing && props.hide(); } const handleFail = () => { @@ -47,8 +49,8 @@ const BrandEditor = (props) => { } const handleCancel = () => { - // TODO: hide this component or something clearAll(); + props.editing && props.hide(); } const validateFloatInput = (e, callback) => { @@ -57,7 +59,7 @@ const BrandEditor = (props) => { } return ( - <div className={"editor-wrapper"}> + <div className={`editor-wrapper ${props.className ? props.className : ''}`}> <p>{props.heading}</p> <form onSubmit={handleSubmit} className={"editor brand-editor"}> <label> @@ -67,8 +69,11 @@ const BrandEditor = (props) => { value={name} onChange={(e) => setName(e.target.value)} /> </label> - <span className={"buttons"}> + <span className={`buttons ${props.editing ? '' : 'narrow'}`}> <input type="button" value="Clear" onClick={clearAll}/> + {props.editing && + <input type="button" value="Cancel" onClick={handleCancel}/> + } <input type="submit" value="Save" /> </span> </form> diff --git a/src/components/editors/item-editor.js b/src/components/editors/item-editor.js index 6c5e976..e2d9d94 100644 --- a/src/components/editors/item-editor.js +++ b/src/components/editors/item-editor.js @@ -15,22 +15,22 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Item, saveItem } from './../../classes/item' -import { Brand, getAllBrands } from './../../classes/brand' -import './scss/item-editor.scss' +import { Item, saveItem, editItem } from './../../classes/item'; +import { Brand, getAllBrands } from './../../classes/brand'; +import './scss/item-editor.scss'; import { useState, useEffect } from 'react'; const ItemEditor = (props) => { - const [name, setName] = useState(""); - const [desc, setDesc] = useState(""); - const [HSN, setHSN] = useState(""); - const [unit, setUnit] = useState(""); - const [unitPrice, setUnitPrice] = useState(0.00); - const [gstP, setGSTP] = useState(0.00); - const [minQty, setMinQty] = useState(0.00); - const [maxQty, setMaxQty] = useState(0.00); - const [brand, setBrand] = useState(new Brand()); + const [name, setName] = useState(props.item.Name); + const [desc, setDesc] = useState(props.item.Description); + const [HSN, setHSN] = useState(props.item.HSN); + const [unit, setUnit] = useState(props.item.UnitOfMeasure); + const [unitPrice, setUnitPrice] = useState(props.item.UnitPrice); + const [gstP, setGSTP] = useState(props.item.GSTPercentage); + const [minQty, setMinQty] = useState(props.item.MinQuantity); + const [maxQty, setMaxQty] = useState(props.item.MaxQuantity); + const [brand, setBrand] = useState(props.item.Brand); const [savedBrands, setSavedBrands] = useState([]); // get saved brands from API @@ -48,19 +48,22 @@ const ItemEditor = (props) => { item.Description = desc; item.HSN = HSN; item.UnitOfMeasure = unit; - item.UnitPrice = unitPrice; - item.GSTPercentage = gstP; - item.MinQuantity = minQty; - item.MaxQuantity = maxQty; + item.UnitPrice = parseFloat(unitPrice); + item.GSTPercentage = parseFloat(gstP); + item.MinQuantity = parseFloat(minQty); + item.MaxQuantity = parseFloat(maxQty); item.Brand = brand; // TODO: Save is for new items. implement modification too - saveItem(item, handleSuccess, handleFail); + props.editing + ? editItem(item, handleSuccess, handleFail) + : saveItem(item, handleSuccess, handleFail); } const handleSuccess = () => { clearAll(); props.callback(); + props.editing && props.hide(); } const handleFail = () => { @@ -85,73 +88,80 @@ const ItemEditor = (props) => { } const handleCancel = () => { - // TODO: hide this component or something clearAll(); - } - - const validateFloatInput = (e, callback) => { - const f = parseFloat(e.target.value); - f && callback(f) + props.editing && props.hide(); } return ( - <div className={"editor-wrapper"}> + <div className={`editor-wrapper ${props.className ? props.className : ''}`}> <p>{props.heading}</p> <form onSubmit={handleSubmit} className={"editor"}> <label> Product/Service: <input - type="text" name="name" - value={name} onChange={(e) => setName(e.target.value)} /> + type="text" + value={name} + onChange={(e) => setName(e.target.value)} /> </label> <label> Description: <input - type="text" name="desc" - value={desc} onChange={(e) => setDesc(e.target.value)} /> + type="text" + value={desc} + onChange={(e) => setDesc(e.target.value)} /> </label> <label> HSN: <input - type="text" name="hsn" - value={HSN} onChange={(e) => setHSN(e.target.value)} /> + type="text" + value={HSN} + onChange={(e) => setHSN(e.target.value)} /> </label> <label> Unit of Measurement: <input - type="text" name="unit" - value={unit} onChange={(e) => setUnit(e.target.value)} /> + type="text" + value={unit} + onChange={(e) => setUnit(e.target.value)} /> </label> <label> Unit Price: <input - type="number" name="unitprice" - value={unitPrice} onChange={(e) => validateFloatInput(e, setUnitPrice)} /> + type="number" + value={unitPrice} + min="0" + onChange={(e) => setUnitPrice(e.target.value)} /> </label> <label> GST %: <input - type="number" name="gstp" - value={gstP} onChange={(e) => validateFloatInput(e, setGSTP)} /> + type="number" + value={gstP} + min="0" + onChange={(e) => setGSTP(e.target.value)} /> </label> <label> Minimum Quantity: <input - type="number" name="minqty" - value={minQty} onChange={(e) => validateFloatInput(e, setMinQty)} /> + type="number" + value={minQty} + min="0" + onChange={(e) => setMinQty(e.target.value)} /> </label> <label> Maximum Quantity: <input - type="number" name="maxqty" - value={maxQty} onChange={(e) => validateFloatInput(e, setMaxQty)} /> + type="number" + value={maxQty} + min="0" + onChange={(e) => setMaxQty(e.target.value)} /> </label> {savedBrands && savedBrands.length > 0 && diff --git a/src/components/editors/scss/brand-editor.scss b/src/components/editors/scss/brand-editor.scss index 05b05a7..b9230a4 100644 --- a/src/components/editors/scss/brand-editor.scss +++ b/src/components/editors/scss/brand-editor.scss @@ -17,6 +17,6 @@ @import "editor"; -.brand-editor .buttons { +.brand-editor .buttons.narrow { width: 9.5rem; } diff --git a/src/components/tables/brand-table.js b/src/components/tables/brand-table.js index 4b361a1..db8272c 100644 --- a/src/components/tables/brand-table.js +++ b/src/components/tables/brand-table.js @@ -19,8 +19,8 @@ import './scss/brand-table.scss'; import { deleteBrand } from './../../classes/brand'; const BrandTable = (props) => { - const handleEdit = (i) => { - alert("editing coming soon") + const handleEdit = (b) => { + props.setBrandToEdit(b) } const handleDelete = (b) => { @@ -39,7 +39,7 @@ const BrandTable = (props) => { return ( <div className={"brand-table"}> {props.brands && props.brands.map(i => - <div className={"brand"}> + <div key={i.Id} className={"brand"}> <p className={"brand-name"}>{i.Name}</p> <div className={"buttons"}> <input type="button" value="Edit" onClick={() => handleEdit(i)}/> diff --git a/src/components/tables/client-table.js b/src/components/tables/client-table.js index 466316a..ffbf5e8 100644 --- a/src/components/tables/client-table.js +++ b/src/components/tables/client-table.js @@ -20,7 +20,7 @@ import { deleteClient } from './../../classes/client'; const ClientTable = (props) => { const handleEdit = (i) => { - alert("editing coming soon") + props.setItemToEdit(i) } const handleDelete = (c) => { diff --git a/src/components/tables/item-table.js b/src/components/tables/item-table.js index e42b272..208fd9c 100644 --- a/src/components/tables/item-table.js +++ b/src/components/tables/item-table.js @@ -22,7 +22,7 @@ import { faPencil, faTrashCan } from '@fortawesome/free-solid-svg-icons' const ItemTable = (props) => { const handleEdit = (i) => { - alert("editing coming soon") + props.setItemToEdit(i) } const handleDelete = (i) => { diff --git a/src/views/manage/brands.js b/src/views/manage/brands.js index 61afe16..dad21e2 100644 --- a/src/views/manage/brands.js +++ b/src/views/manage/brands.js @@ -18,11 +18,12 @@ import { useState, useEffect } from 'react'; import './scss/management-page.scss' -import { getAllBrands } from '../../classes/brand'; +import { Brand, getAllBrands } from '../../classes/brand'; import BrandEditor from './../../components/editors/brand-editor'; import BrandTable from './../../components/tables/brand-table'; const ManageBrandsPage = () => { + const [brandToEdit, setBrandToEdit] = useState(new Brand()); const [allBrands, setAllBrands] = useState([]); // TODO: handle error const updateList = () => @@ -34,9 +35,27 @@ const ManageBrandsPage = () => { return ( <> - <BrandEditor heading={"Add New Brand"} callback={updateList}/> + <BrandEditor + heading={"Add New Brand"} + brand={new Brand()} + callback={updateList}/> <hr/> - <BrandTable refresh={updateList} brands={allBrands}/> + <BrandTable + refresh={updateList} + brands={allBrands} + setBrandToEdit={setBrandToEdit}/> + + {JSON.stringify(brandToEdit) !== JSON.stringify(new Brand()) && + <div className={"floating-wrapper"}> + <BrandEditor + className={"floating-window"} + heading={`Edit ${brandToEdit.Name ? brandToEdit.Name : 'Brand'}:`} + brand={brandToEdit} + hide={() => setBrandToEdit(new Brand())} + editing={true} + callback={updateList}/> + </div> + } </> ); } diff --git a/src/views/manage/items.js b/src/views/manage/items.js index b9bf339..567be7c 100644 --- a/src/views/manage/items.js +++ b/src/views/manage/items.js @@ -22,11 +22,12 @@ import { useState, useEffect } from 'react'; import './scss/management-page.scss' -import { getAllItems } from '../../classes/item'; +import { Item, getAllItems } from '../../classes/item'; import ItemEditor from './../../components/editors/item-editor'; import ItemTable from './../../components/tables/item-table'; const ManageItemsPage = () => { + const [itemToEdit, setItemToEdit] = useState(new Item()); const [allItems, setAllItems] = useState([]); // TODO: handle error const updateList = () => @@ -38,9 +39,27 @@ const ManageItemsPage = () => { return ( <> - <ItemEditor heading={"Add New Item"} callback={updateList}/> + <ItemEditor + heading={"Add New Item"} + item={new Item()} + callback={updateList}/> <hr/> - <ItemTable refresh={updateList} items={allItems}/> + <ItemTable + refresh={updateList} + items={allItems} + setItemToEdit={setItemToEdit}/> + + {JSON.stringify(itemToEdit) !== JSON.stringify(new Item()) && + <div className={"floating-wrapper"}> + <ItemEditor + className={"floating-window"} + heading={`Edit ${itemToEdit.Name ? itemToEdit.Name : 'Item'}:`} + item={itemToEdit} + hide={() => setItemToEdit(new Item())} + editing={true} + callback={updateList}/> + </div> + } </> ); } diff --git a/src/views/manage/scss/management-page.scss b/src/views/manage/scss/management-page.scss index df26819..656d224 100644 --- a/src/views/manage/scss/management-page.scss +++ b/src/views/manage/scss/management-page.scss @@ -15,6 +15,10 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +@import "../../../styles"; + +@include floating-window; + hr { margin: 0.8rem auto 1rem auto; } |