You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.0 KiB
318 lines
9.0 KiB
import React, { useState, useEffect } from 'react'; |
|
import { BrowserRouter as Router, Switch, Route, Link, useParams } from 'react-router-dom'; |
|
import './light.css'; |
|
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; |
|
import { isAdmin, BasicTable, requester } from './utils.js'; |
|
import { NotFound, PleaseLogin } from './Misc.js'; |
|
|
|
export function TransactionEditor(props) { |
|
const { input, setInput, error } = props; |
|
|
|
const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); |
|
const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] }); |
|
const handleChange = (e) => handleValues(e, e.currentTarget); |
|
const handleCheck = (e, v) => setInput({ ...input, [v.name]: v.checked }); |
|
|
|
const makeProps = (name) => ({ |
|
name: name, |
|
onChange: handleChange, |
|
value: input[name] || '', |
|
error: error[name], |
|
}); |
|
|
|
const accountOptions = [ |
|
{ key: '0', text: 'Cash (CAD Lock Box)', value: 'Cash' }, |
|
{ key: '1', text: 'Interac (Email) Transfer (TD)', value: 'Interac' }, |
|
{ key: '2', text: 'Square (Credit)', value: 'Square Pmt' }, |
|
{ key: '3', text: 'Dream Payments (Debit/Credit)', value: 'Dream Pmt' }, |
|
{ key: '4', text: 'Deposit to TD (Not Interac)', value: 'TD Chequing' }, |
|
{ key: '5', text: 'PayPal', value: 'Paypal' }, |
|
{ key: '6', text: 'Member Balance / Protocash', value: 'Member' }, |
|
{ key: '7', text: 'Supense (Clearing) Acct / Membership Adjustment', value: 'Clearing' }, |
|
]; |
|
|
|
const sourceOptions = [ |
|
{ key: '0', text: 'Web (Spaceport)', value: 'Web' }, |
|
{ key: '1', text: 'Database Edit', value: 'DB Edit' }, |
|
{ key: '2', text: 'System', value: 'System' }, |
|
{ key: '3', text: 'Receipt or Statement', value: 'Receipt or Stmt' }, |
|
{ key: '4', text: 'Quicken Import', value: 'Quicken Import' }, |
|
{ key: '5', text: 'PayPal IPN', value: 'Paypal IPN' }, |
|
{ key: '6', text: 'Auto', value: 'Auto' }, |
|
{ key: '7', text: 'Nexus Database Bulk', value: 'Nexus DB Bulk' }, |
|
{ key: '8', text: 'IPN Trigger', value: 'IPN Trigger' }, |
|
{ key: '9', text: 'Intranet Receipt', value: 'Intranet Receipt' }, |
|
{ key: '10', text: 'Automatic', value: 'Automatic' }, |
|
{ key: '11', text: 'Manual', value: 'Manual' }, |
|
]; |
|
|
|
const categoryOptions = [ |
|
{ key: '0', text: 'Membership Dues', value: 'Membership' }, |
|
{ key: '1', text: 'Payment On Account or Prepayment', value: 'OnAcct' }, |
|
{ key: '2', text: 'Snack / Pop / Coffee', value: 'Snacks' }, |
|
{ key: '3', text: 'Donations', value: 'Donation' }, |
|
{ key: '4', text: 'Consumables (Specify which in memo)', value: 'Consumables' }, |
|
{ key: '5', text: 'Purchases of Goods or Parts or Stock', value: 'Purchases' }, |
|
{ key: '6', text: 'Auction, Garage Sale, Nearly Free Shelf, Etc.', value: 'Garage Sale' }, |
|
{ key: '7', text: 'Reimbursement (Enter a negative value)', value: 'Reimburse' }, |
|
{ key: '8', text: 'Other (Explain in memo)', value: 'Other' }, |
|
]; |
|
|
|
return ( |
|
<div className='transaction-editor'> |
|
<Form.Group widths='equal'> |
|
<Form.Input |
|
label='Date' |
|
fluid |
|
{...makeProps('date')} |
|
/> |
|
<Form.Input |
|
label='Amount' |
|
fluid |
|
{...makeProps('amount')} |
|
/> |
|
</Form.Group> |
|
|
|
<Form.Select |
|
label='Category' |
|
fluid |
|
options={categoryOptions} |
|
{...makeProps('category')} |
|
onChange={handleValues} |
|
/> |
|
|
|
<Form.Select |
|
label='Account' |
|
fluid |
|
options={accountOptions} |
|
{...makeProps('account_type')} |
|
onChange={handleValues} |
|
/> |
|
|
|
<Form.Group widths='equal'> |
|
<Form.Input |
|
label='Payment Method' |
|
fluid |
|
{...makeProps('payment_method')} |
|
/> |
|
<Form.Select |
|
label='Info Source' |
|
fluid |
|
options={sourceOptions} |
|
{...makeProps('info_source')} |
|
onChange={handleValues} |
|
/> |
|
</Form.Group> |
|
|
|
<Form.Group widths='equal'> |
|
<Form.Input |
|
label='Reference Number' |
|
fluid |
|
{...makeProps('reference_number')} |
|
/> |
|
|
|
<Form.Input |
|
label='# Membership Months' |
|
fluid |
|
{...makeProps('number_of_membership_months')} |
|
/> |
|
</Form.Group> |
|
|
|
<Form.Input |
|
label='Memo / Notes' |
|
fluid |
|
{...makeProps('memo')} |
|
/> |
|
|
|
</div> |
|
); |
|
}; |
|
|
|
function EditTransaction(props) { |
|
const { transaction, setTransaction, token, refreshUser } = props; |
|
const [input, setInput] = useState(transaction); |
|
const [error, setError] = useState(false); |
|
const [loading, setLoading] = useState(false); |
|
const [success, setSuccess] = useState(false); |
|
const { id } = useParams(); |
|
|
|
const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); |
|
const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] }); |
|
const handleChange = (e) => handleValues(e, e.currentTarget); |
|
const handleCheck = (e, v) => setInput({ ...input, [v.name]: v.checked }); |
|
|
|
const handleSubmit = (e) => { |
|
setLoading(true); |
|
setSuccess(false); |
|
requester('/transactions/'+id+'/', 'PUT', token, input) |
|
.then(res => { |
|
setLoading(false); |
|
setSuccess(true); |
|
setError(false); |
|
setInput(res); |
|
setTransaction(res); |
|
if (refreshUser) { |
|
refreshUser(); |
|
} |
|
}) |
|
.catch(err => { |
|
setLoading(false); |
|
console.log(err); |
|
setError(err.data); |
|
}); |
|
}; |
|
|
|
return ( |
|
<div> |
|
<Header size='medium'>Edit Transaction</Header> |
|
|
|
<Form onSubmit={handleSubmit}> |
|
<TransactionEditor input={input} setInput={setInput} error={error} /> |
|
|
|
<Form.Button loading={loading} error={error.non_field_errors}> |
|
Save |
|
</Form.Button> |
|
{success && <div>Success!</div>} |
|
</Form> |
|
</div> |
|
); |
|
}; |
|
|
|
export function TransactionList(props) { |
|
const { transactions } = props; |
|
|
|
return ( |
|
<Table basic='very'> |
|
<Table.Header> |
|
<Table.Row> |
|
<Table.HeaderCell>Date</Table.HeaderCell> |
|
<Table.HeaderCell>Amount</Table.HeaderCell> |
|
<Table.HeaderCell>Account</Table.HeaderCell> |
|
<Table.HeaderCell>Memo</Table.HeaderCell> |
|
</Table.Row> |
|
</Table.Header> |
|
|
|
<Table.Body> |
|
{transactions.length ? |
|
transactions.slice().sort((a, b) => a.date < b.date ? 1 : -1).map(x => |
|
<Table.Row key={x.id}> |
|
<Table.Cell> |
|
<Link to={'/transactions/'+x.id}>{x.date}</Link> |
|
</Table.Cell> |
|
<Table.Cell>${x.amount}</Table.Cell> |
|
<Table.Cell>{x.account_type}</Table.Cell> |
|
<Table.Cell>{x.memo}</Table.Cell> |
|
</Table.Row> |
|
) |
|
: |
|
<Table.Row><Table.Cell>None</Table.Cell></Table.Row> |
|
} |
|
</Table.Body> |
|
</Table> |
|
); |
|
}; |
|
|
|
export function Transactions(props) { |
|
const { user } = props; |
|
|
|
return ( |
|
<Container> |
|
<Header size='large'>Transactions</Header> |
|
|
|
<TransactionList transactions={user.transactions} /> |
|
|
|
</Container> |
|
); |
|
}; |
|
|
|
export function TransactionDetail(props) { |
|
const { token, user } = props; |
|
const { id } = useParams(); |
|
const ownTransaction = user.transactions.find(x => x.id == id); |
|
const [transaction, setTransaction] = useState(ownTransaction || false); |
|
const [error, setError] = useState(false); |
|
|
|
useEffect(() => { |
|
requester('/transactions/'+id+'/', 'GET', token) |
|
.then(res => { |
|
setTransaction(res); |
|
setError(false); |
|
}) |
|
.catch(err => { |
|
console.log(err); |
|
setError(true); |
|
}); |
|
}, []); |
|
|
|
return ( |
|
<Container> |
|
{!error ? |
|
transaction ? |
|
<div> |
|
<Header size='large'>Transaction Receipt</Header> |
|
|
|
<Grid stackable columns={2}> |
|
<Grid.Column> |
|
<BasicTable> |
|
<Table.Body> |
|
<Table.Row> |
|
<Table.Cell>Member:</Table.Cell> |
|
<Table.Cell>{transaction.member_name}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>ID:</Table.Cell> |
|
<Table.Cell>{transaction.id}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Date:</Table.Cell> |
|
<Table.Cell>{transaction.date}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Amount:</Table.Cell> |
|
<Table.Cell>${transaction.amount}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Category:</Table.Cell> |
|
<Table.Cell>{transaction.category}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Account:</Table.Cell> |
|
<Table.Cell>{transaction.account_type}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Payment Method</Table.Cell> |
|
<Table.Cell>{transaction.payment_method}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Info Source:</Table.Cell> |
|
<Table.Cell>{transaction.info_source}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Reference:</Table.Cell> |
|
<Table.Cell>{transaction.reference_number}</Table.Cell> |
|
</Table.Row> |
|
<Table.Row> |
|
<Table.Cell>Memo:</Table.Cell> |
|
<Table.Cell>{transaction.memo}</Table.Cell> |
|
</Table.Row> |
|
</Table.Body> |
|
</BasicTable> |
|
</Grid.Column> |
|
|
|
<Grid.Column> |
|
{isAdmin(user) && <Segment padded> |
|
<EditTransaction transaction={transaction} setTransaction={setTransaction} {...props} /> |
|
</Segment>} |
|
</Grid.Column> |
|
</Grid> |
|
|
|
</div> |
|
: |
|
<p>Loading...</p> |
|
: |
|
<NotFound /> |
|
} |
|
</Container> |
|
); |
|
}; |
|
|
|
|