| @@ -1,130 +0,0 @@ | |||
| import React, { useState } from 'react'; | |||
| import styles from './invoice.module.scss'; | |||
| import { ReactComponent as ChevronBack } from 'ionicons/dist/svg/chevron-back.svg'; | |||
| import { format } from 'date-fns'; | |||
| import { IonButton, IonDatetime, IonInput } from '@ionic/react'; | |||
| type OwnProps = { | |||
| invoice: { | |||
| id: string, | |||
| date: string, | |||
| status: string, | |||
| dateOfPayment?: string, | |||
| referenceNumber?: string, | |||
| }, | |||
| client: { | |||
| name: string, | |||
| logo: string, | |||
| project: { | |||
| name: string, | |||
| contract: { | |||
| name: string, | |||
| amount: string, | |||
| }, | |||
| } | |||
| } | |||
| } | |||
| export const InvoiceCard: React.FC<OwnProps> = (props) => { | |||
| const [showDetails, setShowDetails] = useState<boolean>(false); | |||
| const [formInputs, setFormInputs] = useState<{ | |||
| dateOfPayment: string, | |||
| referenceNumber: string, | |||
| }>({ | |||
| dateOfPayment: props.invoice.dateOfPayment ? props.invoice.dateOfPayment.toString() : '', | |||
| referenceNumber: props.invoice.referenceNumber ? props.invoice.referenceNumber : '', | |||
| }); | |||
| let statusClass = ''; | |||
| switch (props.invoice.status) { | |||
| case 'due': statusClass = styles.due; break; | |||
| case 'over due': statusClass = styles.overDue; break; | |||
| case 'paid': statusClass = styles.paid; break; | |||
| case 'cancelled': statusClass = styles.cancelled; break; | |||
| default: break; | |||
| } | |||
| return <div> | |||
| <section className={styles.card} onClick={() => setShowDetails(true)}> | |||
| <figure> | |||
| <img src={props.client.logo} alt="logo" /> | |||
| </figure> | |||
| <div className={styles.contentHolder}> | |||
| <h6> Invoice ID: {props.invoice.id} </h6> | |||
| <h2> {props.client.project.contract.amount} </h2> | |||
| <p> {props.client.project.contract.name} </p> | |||
| </div> | |||
| <div className={styles.status}> | |||
| <span className={statusClass}> {props.invoice.status} </span> | |||
| </div> | |||
| </section> | |||
| { showDetails && <div className={styles.backdrop}> | |||
| <section className={styles.cardDetails}> | |||
| <header> | |||
| <IonButton onClick={() => setShowDetails(false)} fill={'clear'} size={'small'}> <ChevronBack /> </IonButton> | |||
| <h4> Pending Invoice </h4> | |||
| </header> | |||
| <ul> | |||
| <li> | |||
| <label> Invoice ID </label> | |||
| <p> {props.invoice.id} </p> | |||
| </li> | |||
| <li> | |||
| <label> Invoice Date </label> | |||
| <p> {format(new Date(props.invoice.date), 'dd MMMM yyyy')} </p> | |||
| </li> | |||
| <li> | |||
| <label> Client </label> | |||
| <p> {props.client.name} </p> | |||
| </li> | |||
| <li> | |||
| <label> Project </label> | |||
| <p> {props.client.project.name} </p> | |||
| </li> | |||
| <li> | |||
| <label> Contract </label> | |||
| <p> {props.client.project.contract.name} </p> | |||
| </li> | |||
| <li> | |||
| <label> Invoice Amount </label> | |||
| <p> {props.client.project.contract.amount} </p> | |||
| </li> | |||
| </ul> | |||
| <section className={styles.form}> | |||
| <div className={styles.inputHolder}> | |||
| <label> Payment Date </label> | |||
| <IonDatetime value={formInputs.dateOfPayment} mode={'ios'} placeholder={'DD/MM/YYYY'} displayFormat={'DD/MM/YYYY'} | |||
| onIonChange={(e) => setFormInputs({ | |||
| dateOfPayment: e.detail.value? e.detail.value.toString() : '', | |||
| referenceNumber: formInputs.referenceNumber, | |||
| })}></IonDatetime> | |||
| </div> | |||
| <div className={styles.inputHolder}> | |||
| <label> Reference No. </label> | |||
| <IonInput placeholder={'Enter here'} value={formInputs.referenceNumber} | |||
| onIonChange={(e) => setFormInputs({ | |||
| dateOfPayment: formInputs.dateOfPayment, | |||
| referenceNumber: e.detail.value? e.detail.value.toString() : '', | |||
| })}></IonInput> | |||
| </div> | |||
| {(props.invoice.status !== 'cancelled' && props.invoice.status !== 'paid' || formInputs.dateOfPayment !== props.invoice.dateOfPayment || formInputs.dateOfPayment !== props.invoice.date) && <div className={styles.buttonsHolder}> | |||
| <IonButton fill={'outline'} className={styles.cancel} shape={'round'}> Cancel Invoice </IonButton> | |||
| <IonButton fill={'solid'} className={styles.approve + ' ' + (formInputs.dateOfPayment && formInputs.referenceNumber ? '' : styles.disabled)} shape={'round'} | |||
| onClick={() => setShowDetails(false)}> Paid </IonButton> | |||
| </div>} | |||
| </section> | |||
| </section> | |||
| </div> } | |||
| </div> | |||
| } | |||
| @@ -63,9 +63,10 @@ | |||
| height: 7px; | |||
| border-radius: 50%; | |||
| background-color: var(--grey); | |||
| margin-right: 5px; | |||
| } | |||
| &.due { | |||
| &.warning { | |||
| color: var(--warning); | |||
| &::before { | |||
| @@ -73,7 +74,7 @@ | |||
| } | |||
| } | |||
| &.overDue { | |||
| &.danger { | |||
| color: var(--red); | |||
| &::before { | |||
| @@ -81,7 +82,7 @@ | |||
| } | |||
| } | |||
| &.paid { | |||
| &.success { | |||
| color: var(--teal); | |||
| &::before { | |||
| @@ -0,0 +1,102 @@ | |||
| import React, { useState } from 'react'; | |||
| import styles from './itemCard.module.scss'; | |||
| import { ReactComponent as ChevronBack } from 'ionicons/dist/svg/chevron-back.svg'; | |||
| import { IonButton } from '@ionic/react'; | |||
| type OwnProps = { | |||
| leftImage?: string, | |||
| heading: { | |||
| name: string, | |||
| value: string | |||
| }, | |||
| value: string, | |||
| description?: string, | |||
| rightText?: { | |||
| name: string, | |||
| type: 'success' | 'danger' | 'warning' | undefined | |||
| }, | |||
| details?: Array<{ | |||
| name: string, | |||
| value: string | |||
| }>, | |||
| form?: JSX.Element | |||
| } | |||
| // Card - Image on the left, heading-value-description in the middle and status or any text on the right. eg: Pending invoice cards | |||
| export const ItemCard: React.FC<OwnProps> = (props) => { | |||
| const [showDetails, setShowDetails] = useState<boolean>(false); | |||
| let statusClass = ''; | |||
| if (props.rightText && props.rightText.type) { | |||
| switch(props.rightText.type) { | |||
| case 'success' : statusClass = styles.success; break; | |||
| case 'danger' : statusClass = styles.danger; break; | |||
| case 'warning' : statusClass = styles.warning; break; | |||
| default: statusClass = ''; | |||
| } | |||
| } | |||
| return <div> | |||
| <section className={styles.card} onClick={() => setShowDetails(true)}> | |||
| <figure> | |||
| <img src={props.leftImage} /> | |||
| </figure> | |||
| <div className={styles.contentHolder}> | |||
| <h6> { props.heading.name }: {props.heading.value} </h6> | |||
| <h2> {props.value} </h2> | |||
| <p> {props.description} </p> | |||
| </div> | |||
| { props.rightText && <div className={styles.status}> | |||
| <span | |||
| className={statusClass}> { props.rightText.name } </span> | |||
| </div> } | |||
| </section> | |||
| { showDetails && props.details && <div className={styles.backdrop} onClick={(e: any) => { | |||
| if (e.target.className.toString().includes('backdrop')) { | |||
| setShowDetails(false); | |||
| } | |||
| }}> | |||
| <section className={styles.cardDetails}> | |||
| <header> | |||
| <IonButton onClick={() => setShowDetails(false)} fill={'clear'} size={'small'}> <ChevronBack /> </IonButton> | |||
| <h4> Pending Invoice </h4> | |||
| </header> | |||
| <ul> | |||
| { props.details.map((detail, key) => { | |||
| return <li key={key}> | |||
| <label> { detail.name } </label> | |||
| <p> { detail.value } </p> | |||
| </li> | |||
| }) } | |||
| </ul> | |||
| { props.form } | |||
| </section> | |||
| </div> } | |||
| </div> | |||
| } | |||
| {/* <section className={styles.form}> | |||
| <div className={styles.inputHolder}> | |||
| <label> Payment Date </label> | |||
| <IonDatetime mode={'ios'} placeholder={'DD/MM/YYYY'} displayFormat={'DD/MM/YYYY'}></IonDatetime> | |||
| </div> | |||
| <div className={styles.inputHolder}> | |||
| <label> Reference No. </label> | |||
| <IonInput placeholder={'Enter here'}></IonInput> | |||
| </div> | |||
| <div className={styles.buttonsHolder}> | |||
| <IonButton fill={'outline'} className={styles.cancel} shape={'round'}> Cancel Invoice </IonButton> | |||
| <IonButton fill={'solid'} className={styles.approve} shape={'round'} | |||
| onClick={() => setShowDetails(false)}> Paid </IonButton> | |||
| </div> | |||
| </section> */} | |||
| @@ -1,10 +0,0 @@ | |||
| import React from 'react'; | |||
| import styles from './invoice.module.scss'; | |||
| import { format } from 'date-fns'; | |||
| export const TransactionCard: React.FC = () => { | |||
| return <div className={styles.card}> | |||
| </div> | |||
| } | |||
| @@ -2,7 +2,8 @@ import { IonContent, IonPage } from '@ionic/react'; | |||
| import { ReactComponent as CogIcon } from 'ionicons/dist/svg/cog-outline.svg'; | |||
| import { ReactComponent as LineChartIcon } from 'ionicons/dist/svg/stats-chart.svg'; | |||
| import styles from './accounts.module.scss'; | |||
| import { InvoiceCard } from '../../components/invoice-card/invoice'; | |||
| import { ItemCard } from '../../components/item-card/itemCard'; | |||
| import { format } from 'date-fns'; | |||
| const sampleInvoiceData = [{ | |||
| invoice: { | |||
| @@ -102,46 +103,39 @@ const Accounts: React.FC = () => { | |||
| </header> | |||
| {sampleInvoiceData.map((invoice, key) => { | |||
| return <InvoiceCard key={key} invoice={invoice.invoice} client={invoice.client} /> | |||
| })} | |||
| </div> | |||
| <div className={styles.dashboardContainerSegment}> | |||
| <header> | |||
| <h5> Pending Invoices </h5> | |||
| <a> See All </a> | |||
| </header> | |||
| {sampleInvoiceData.map((invoice, key) => { | |||
| return <InvoiceCard key={key} invoice={invoice.invoice} client={invoice.client} /> | |||
| })} | |||
| </div> | |||
| <div className={styles.dashboardContainerSegment}> | |||
| <header> | |||
| <h5> Pending Invoices </h5> | |||
| <a> See All </a> | |||
| </header> | |||
| {sampleInvoiceData.map((invoice, key) => { | |||
| return <InvoiceCard key={key} invoice={invoice.invoice} client={invoice.client} /> | |||
| })} | |||
| </div> | |||
| <div className={styles.dashboardContainerSegment}> | |||
| <header> | |||
| <h5> Pending Invoices </h5> | |||
| <a> See All </a> | |||
| </header> | |||
| {sampleInvoiceData.map((invoice, key) => { | |||
| return <InvoiceCard key={key} invoice={invoice.invoice} client={invoice.client} /> | |||
| return <ItemCard key={key} | |||
| leftImage={invoice.client.logo} | |||
| heading={{ name: 'Invoice ID', value: invoice.invoice.id }} | |||
| value={invoice.client.project.contract.amount} | |||
| description={invoice.client.project.contract.name} | |||
| rightText={{ | |||
| name: invoice.invoice.status, | |||
| type: invoice.invoice.status === 'due' ? 'warning' : 'danger' | |||
| }} | |||
| details={[{ | |||
| name: 'Invoice ID', | |||
| value: invoice.invoice.id, | |||
| }, { | |||
| name: 'Invoice Date', | |||
| value: format(new Date(invoice.invoice.date), 'dd MMMM yyyy') | |||
| }, { | |||
| name: 'Client', | |||
| value: invoice.client.name | |||
| }, { | |||
| name: 'Project', | |||
| value: invoice.client.project.name | |||
| }, { | |||
| name: 'Contract', | |||
| value: invoice.client.project.contract.name | |||
| }, { | |||
| name: 'Invoice Amount', | |||
| value: invoice.client.project.contract.amount | |||
| }]} | |||
| /> | |||
| })} | |||
| </div> | |||
| </div> | |||
| </IonContent> | |||
| </IonPage> | |||
| ); | |||