diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/classes/client.js | 14 | ||||
-rw-r--r-- | src/components/editors/address-editor.js | 37 | ||||
-rw-r--r-- | src/components/editors/client-editor.js | 76 | ||||
-rw-r--r-- | src/components/editors/contact-editor.js | 35 | ||||
-rw-r--r-- | src/components/editors/scss/_colors.scss (renamed from src/components/editors/scss/colors.scss) | 2 | ||||
-rw-r--r-- | src/components/editors/scss/address-editor.scss | 20 | ||||
-rw-r--r-- | src/components/editors/scss/client-editor.scss | 7 | ||||
-rw-r--r-- | src/views/manage/clients.js | 2 |
8 files changed, 156 insertions, 37 deletions
diff --git a/src/classes/client.js b/src/classes/client.js index 524d0ce..cae528e 100644 --- a/src/classes/client.js +++ b/src/classes/client.js @@ -43,24 +43,24 @@ export class Client { this.Contact = new Contact(); this.GSTIN = ""; this.BillingAddress = new Address(); - this.ShippingAddress = []; + this.ShippingAddresses = []; } } export const saveClient = (item, ok, fail) => { axios.post("/client/new", item) - .then(res => { console.log(res);ok()}) - .catch((err) => fail()) + .then(res => ok(res)) + .catch(err => fail(err)) } export const deleteClient = (id, ok, fail) => { axios.delete(`/client/${id}`) - .then(res => ok()) - .catch((err) => fail()) + .then(res => ok(res)) + .catch(err => fail(err)) } export const getAllClients = (ok, fail) => { axios.get("/client/all") - .then(res => ok(res.data)) - .catch(err => fail()) + .then(res => ok(res.Data)) + .catch(err => fail(err)) } diff --git a/src/components/editors/address-editor.js b/src/components/editors/address-editor.js index d2b015f..3675f26 100644 --- a/src/components/editors/address-editor.js +++ b/src/components/editors/address-editor.js @@ -18,18 +18,31 @@ import { Address } from './../../classes/client'; import './scss/address-editor.scss'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faXmark } from '@fortawesome/free-solid-svg-icons'; const AddressEditor = (props) => { - const [country, setCountry] = useState(""); - const [state, setState] = useState(""); - const [city, setCity] = useState(""); - const [address, setAddress] = useState(""); - const [postalCode, setPostalCode] = useState(""); + const handleInput = (field, event) => { + const a = new Address(); + const val = event.target.value; + a.Country = field === "country" ? val : props.address.Country; + a.State = field === "state" ? val : props.address.State; + a.City = field === "city" ? val : props.address.City; + a.Text = field === "address" ? val : props.address.Text; + a.PostalCode = field === "postal" ? val : props.address.PostalCode; + props.setAddress(a); + } return ( <div className={"address-editor"}> <p className={"heading"}>{props.heading}</p> + {props.isBillingAddress || // cross button + <FontAwesomeIcon + icon={faXmark} + className={"remove-button"} + onClick={props.deleteAddress}/> + } <div className={"labels-wrapper"}> <div> @@ -37,21 +50,21 @@ const AddressEditor = (props) => { Country: <input type="text" name="name" - value={country} onChange={(e) => setCountry(e.target.value)} /> + value={props.address.Country} onChange={(e) => handleInput("country", e)} /> </label> <label> State: <input type="text" name="name" - value={state} onChange={(e) => setState(e.target.value)} /> + value={props.address.State} onChange={(e) => handleInput("state", e)} /> </label> <label> City: <input type="text" name="name" - value={city} onChange={(e) => setCity(e.target.value)} /> + value={props.address.City} onChange={(e) => handleInput("city", e)} /> </label> </div> @@ -60,14 +73,14 @@ const AddressEditor = (props) => { Address: <textarea type="text" name="name" - value={address} onChange={(e) => setAddress(e.target.value)} /> + value={props.address.Text} onChange={(e) => handleInput("address", e)} /> </label> <label> Postal Code: <input type="text" name="name" - value={postalCode} onChange={(e) => setPostalCode(e.target.value)} /> + value={props.address.PostalCode} onChange={(e) => handleInput("postal", e)} /> </label> </div> </div> @@ -76,7 +89,7 @@ const AddressEditor = (props) => { <input type="checkbox" checked={props.billingAddressIsShipping} - onChange={props.callback()} /> + onChange={() => props.setShipToBillingAddress(!props.billingAddressIsShipping)} /> Shipping address same as billing address </label> } diff --git a/src/components/editors/client-editor.js b/src/components/editors/client-editor.js index eccd9e8..f4535c2 100644 --- a/src/components/editors/client-editor.js +++ b/src/components/editors/client-editor.js @@ -15,24 +15,37 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Client, saveClient } from './../../classes/client'; +import { Client, saveClient, Contact, Address } from './../../classes/client'; import AddressEditor from './address-editor'; import ContactEditor from './contact-editor'; import './scss/client-editor.scss'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; const ClientEditor = (props) => { const [name, setName] = useState(""); const [GSTIN, setGSTIN] = useState([]); + const [contact, setContact] = useState(new Contact()); + const [billingAddress, setBillingAddress] = useState(new Address()); + const [shippingAddresses, setShippingAddresses] = useState([]); const [shipToBillingAddress, setShipToBillingAddress] = useState(true); + useEffect(() => { + // will delete existing shipping addresses if false + setShippingAddresses(shipToBillingAddress ? [] : [new Address()]) + }, [shipToBillingAddress]); + const handleSubmit = (e) => { e.preventDefault(); const client = new Client(); client.Name = name; client.GSTIN = GSTIN; + client.Contact = contact; + client.BillingAddress = billingAddress; + client.ShippingAddresses = shipToBillingAddress + ? [billingAddress] + : shippingAddresses // TODO: Save is for new items. implement modification too saveClient(client, handleSuccess, handleFail); @@ -40,16 +53,21 @@ const ClientEditor = (props) => { const handleSuccess = () => { clearAll(); - props.callback(); + props.successCallback(); } - const handleFail = () => { + const handleFail = (err) => { alert("fail"); + console.log(err); } const clearAll = () => { setName(""); setGSTIN("") + setContact(new Contact()); + setBillingAddress(new Address()); + setShippingAddresses([new Address()]); + setShipToBillingAddress(true); } const handleCancel = () => { @@ -57,6 +75,27 @@ const ClientEditor = (props) => { clearAll(); } + const handleShippingAddressUpdate = (id, data) => { + setShippingAddresses([ + ...shippingAddresses.slice(0, id), + data, + ...shippingAddresses.slice(id+1) + ]); + } + + const handleShippingAddressDelete = (id) => { + // deleting the last address sets + // shipToBillingAddress to true + if (shippingAddresses.length === 1) { + setShipToBillingAddress(true); + } else { + setShippingAddresses([ + ...shippingAddresses.slice(0, id), + ...shippingAddresses.slice(id+1) + ]); + } + } + return ( <div className={"editor-wrapper"}> <p>{props.heading}</p> @@ -77,14 +116,35 @@ const ClientEditor = (props) => { </label> </div> - <ContactEditor heading={"Contact Details"}/> + <ContactEditor + heading={"Contact Details"} + contact={contact} + setContact={setContact} /> <AddressEditor heading={"Billing Address"} isBillingAddress={true} billingAddressIsShipping={shipToBillingAddress} - callback={setShipToBillingAddress} /> - - <span className={"buttons"}> + setShipToBillingAddress={setShipToBillingAddress} + address={billingAddress} + setAddress={setBillingAddress} /> + + {shippingAddresses.length > 0 && shippingAddresses.map((i, id) => + <AddressEditor + key={id} + heading={`Shipping Address ${shippingAddresses.length === 1 ? '' : id + 1}`} + address={i} + deleteAddress={() => handleShippingAddressDelete(id)} + setAddress={(data) => handleShippingAddressUpdate(id, data)} /> + )} + + <span className={`buttons ${shippingAddresses.length > 0 ? 'wide' : ''}`}> + {shippingAddresses.length > 0 && + <input + className={"wide-button"} + type="button" + value="Add Shipping Address" + onClick={()=> setShippingAddresses([...shippingAddresses, new Address()])}/> + } <input type="button" value="Clear" onClick={clearAll}/> <input type="button" value="Cancel" onClick={handleCancel}/> <input type="submit" value="Save" /> diff --git a/src/components/editors/contact-editor.js b/src/components/editors/contact-editor.js index 325f4f4..3c9a1f9 100644 --- a/src/components/editors/contact-editor.js +++ b/src/components/editors/contact-editor.js @@ -18,13 +18,22 @@ import { Contact } from './../../classes/client'; import './scss/contact-editor.scss'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; const ContactEditor = (props) => { - const [name, setName] = useState(""); - const [phones, setPhones] = useState(""); - const [emails, setEmails] = useState(""); - const [website, setWebsite] = useState(""); + const handleInput = (field, event) => { + const c = new Contact(); + const val = event.target.value; + c.Name = field === "name" ? val : props.contact.Name; + c.Website = field === "website" ? val : props.contact.Website; + c.Phones = field === "phones" + ? (val.length === 0 ? [] : val.split("\n")) + : props.contact.Phones; + c.Emails = field === "emails" + ? (val.length === 0 ? [] : val.split("\n")) + : props.contact.Emails; + props.setContact(c); + } return ( <div className={"contact-editor"}> @@ -35,28 +44,36 @@ const ContactEditor = (props) => { Contact Name: <input type="text" name="name" - value={name} onChange={(e) => setName(e.target.value)} /> + value={props.contact.Name} onChange={(e) => handleInput("name", e)} /> </label> <label> Website: <input type="text" name="name" - value={website} onChange={(e) => setWebsite(e.target.value)} /> + value={props.contact.Website} onChange={(e) => handleInput("website", e)} /> </label> <label> Phone: <textarea type="text" name="name" - value={phones} onChange={(e) => setPhones(e.target.value)} /> + value={props.contact.Phones.length > 0 + ? props.contact.Phones.forEach(i => i) + : "" + } + onChange={(e) => handleInput("phones", e)} /> </label> <label> E-mail: <textarea type="text" name="name" - value={emails} onChange={(e) => setEmails(e.target.value)} /> + value={props.contact.Emails.length > 0 + ? props.contact.Emails.forEach(i => i) + : "" + } + onChange={(e) => handleInput("emails", e)} /> </label> </div> </div> diff --git a/src/components/editors/scss/colors.scss b/src/components/editors/scss/_colors.scss index 4e5b1c2..994dcf7 100644 --- a/src/components/editors/scss/colors.scss +++ b/src/components/editors/scss/_colors.scss @@ -22,3 +22,5 @@ $fgColor: white; $fgColorAlt: black; $inputBackgroundColor: #00000000; + +$warningColor: #ed4683; diff --git a/src/components/editors/scss/address-editor.scss b/src/components/editors/scss/address-editor.scss index 49b0a0a..50db4a9 100644 --- a/src/components/editors/scss/address-editor.scss +++ b/src/components/editors/scss/address-editor.scss @@ -20,6 +20,8 @@ .address-editor { min-height: 18rem; + position: relative; + .heading { font-size: 1.1rem; color: $primaryAccentColor; @@ -27,6 +29,20 @@ align: center; } + .remove-button { + opacity: 0; + position: absolute; + font-size: 2rem; + top: 0.8rem; + right: 1rem; + color: $secondaryAccentColor; + transition: opacity 0.2s, color 0.3s; + } + + .remove-button:hover { + color: $warningColor; + } + .checkbox-label { border: none; max-width: none; @@ -63,3 +79,7 @@ } } } + +.address-editor:hover .remove-button { + opacity: 1; +} diff --git a/src/components/editors/scss/client-editor.scss b/src/components/editors/scss/client-editor.scss index 52b7189..30919fd 100644 --- a/src/components/editors/scss/client-editor.scss +++ b/src/components/editors/scss/client-editor.scss @@ -25,4 +25,11 @@ display: flex; justify-content: space-around; } + + .buttons.wide { + width: 24.5rem; + input.wide-button { + width: 11rem; + } + } } diff --git a/src/views/manage/clients.js b/src/views/manage/clients.js index 74c1a38..4493902 100644 --- a/src/views/manage/clients.js +++ b/src/views/manage/clients.js @@ -38,7 +38,7 @@ const ManageClientsPage = () => { return ( <> - <ClientEditor heading={"Add New Client"} callback={updateList}/> + <ClientEditor heading={"Add New Client"} successCallback={updateList}/> <hr/> {false && <ClientTable refresh={updateList} items={allClients}/>} </> |