diff --git a/webclient/src/Account.js b/webclient/src/Account.js index fe28b905..9d884ac6 100644 --- a/webclient/src/Account.js +++ b/webclient/src/Account.js @@ -1,12 +1,19 @@ import React, { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import * as loadImage from 'blueimp-load-image'; import ReactCrop from 'react-image-crop'; import 'react-image-crop/dist/ReactCrop.css'; import './light.css'; -import { Button, Container, Checkbox, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; -import { BasicTable, staticUrl, requester } from './utils.js'; -import { LoginForm, SignupForm } from './LoginSignup.js'; +import { + Button, + Container, + Checkbox, + Form, + Grid, + Header, + Segment, +} from 'semantic-ui-react'; +import { requester } from './utils.js'; function LogoutEverywhere(props) { const { token } = props; @@ -20,16 +27,16 @@ function LogoutEverywhere(props) { if (loading) return; setLoading(true); requester('/rest-auth/logout/', 'POST', token, {}) - .then(res => { - setYousure(false); - history.push('/'); - window.scrollTo(0, 0); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setYousure(false); + history.push('/'); + window.scrollTo(0, 0); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); } else { setYousure(true); } @@ -37,7 +44,7 @@ function LogoutEverywhere(props) { return (
-
Log Out from Everywhere
+
Log Out from Everywhere

Use this to log out from all sessions on all computers.

@@ -48,7 +55,7 @@ function LogoutEverywhere(props) {
); -}; +} function ChangePasswordForm(props) { const { token } = props; @@ -58,22 +65,21 @@ function ChangePasswordForm(props) { const history = useHistory(); 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 handleSubmit = (e) => { if (loading) return; setLoading(true); requester('/password/change/', 'POST', token, input) - .then(res => { - setError({}); - history.push('/'); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setError({}); + history.push('/'); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const makeProps = (name) => ({ @@ -85,23 +91,23 @@ function ChangePasswordForm(props) { return (
-
Change Password
+
Change Password
@@ -111,8 +117,7 @@ function ChangePasswordForm(props) { ); -}; - +} export function ImageCrop(props) { const { file, crop, setCrop } = props; @@ -120,10 +125,10 @@ export function ImageCrop(props) { useEffect(() => { setSrc(false); - setCrop({ unit: '%', height: 100, aspect: 3/4 }); + setCrop({ unit: '%', height: 100, aspect: 3 / 4 }); loadImage( file, - img => { + (img) => { setSrc(img.toDataURL()); }, { @@ -136,16 +141,15 @@ export function ImageCrop(props) { ); }, [file]); - return ( - src ? - setCrop(percentCrop)} - /> - : -

Loading...

+ return src ? ( + setCrop(percentCrop)} + /> + ) : ( +

Loading...

); } @@ -159,25 +163,30 @@ export function AccountForm(props) { const history = useHistory(); const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); - const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] }); + 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) => { if (loading) return; setLoading(true); - const data = { ...input, email: input.email.toLowerCase(), crop: JSON.stringify(crop) }; + const data = { + ...input, + email: input.email.toLowerCase(), + crop: JSON.stringify(crop), + }; requester('/members/' + member.id + '/', 'PATCH', token, data) - .then(res => { - setError({}); - refreshUser(); - history.push('/'); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setError({}); + refreshUser(); + history.push('/'); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const makeProps = (name) => ({ @@ -185,27 +194,27 @@ export function AccountForm(props) { onChange: handleChange, value: input[name] || '', error: error[name], - ...(input[name] ? {} : {icon: 'edit'}), + ...(input[name] ? {} : { icon: 'edit' }), }); return (
-
Member Details
+
Member Details
@@ -213,56 +222,64 @@ export function AccountForm(props) { - {input.is_minor && } - {input.is_minor && } + {input.is_minor && ( + + )} + {input.is_minor && ( + + )} - {input.photo && + {input.photo && ( <> - - {crop && crop.width === crop.height ? + + {crop && crop.width === crop.height ? (

It's the perfect size!

- : + ) : (

Move the box above to crop your image.

- } + )} - } + )} Submit ); -}; +} export function BioNotesForm(props) { const { token, user, refreshUser } = props; @@ -273,7 +290,8 @@ export function BioNotesForm(props) { const history = useHistory(); const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); - const handleUpload = (e, v) => setInput({ ...input, [v.name]: e.target.files[0] }); + 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 }); @@ -281,16 +299,16 @@ export function BioNotesForm(props) { if (loading) return; setLoading(true); requester('/members/' + member.id + '/', 'PATCH', token, input) - .then(res => { - setError({}); - refreshUser(); - history.push('/'); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setError({}); + refreshUser(); + history.push('/'); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const makeProps = (name) => ({ @@ -302,17 +320,30 @@ export function BioNotesForm(props) { return (
-
Bio / Notes
+
Bio / Notes
400 ? ' — ' + input.public_bio.length + ' / 512' : '')} + label={ + 'Public Bio' + + (input.public_bio && input.public_bio.length > 400 + ? ' — ' + input.public_bio.length + ' / 512' + : '') + } {...makeProps('public_bio')} /> -

Bio shared with members. Example: contact info, allergies, hobbies, etc.

+

+ Bio shared with members. Example: contact info, allergies, + hobbies, etc. +

400 ? ' — ' + input.private_notes.length + ' / 512' : '')} + label={ + 'Private Notes' + + (input.private_notes && input.private_notes.length > 400 + ? ' — ' + input.private_notes.length + ' / 512' + : '') + } {...makeProps('private_notes')} /> @@ -323,22 +354,30 @@ export function BioNotesForm(props) { ); -}; +} export function Account(props) { return ( -
Account Settings
+
Account Settings
- + + + - - - + + + + + + + + +
); -}; +} diff --git a/webclient/src/Admin.js b/webclient/src/Admin.js index 5828818e..113e9ecd 100644 --- a/webclient/src/Admin.js +++ b/webclient/src/Admin.js @@ -1,19 +1,24 @@ import React, { useState, useEffect, useReducer } from 'react'; -import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import './light.css'; -import { Button, Container, Checkbox, Dimmer, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; +import { + Button, + Container, + Checkbox, + Header, + Icon, + Table, +} from 'semantic-ui-react'; import moment from 'moment-timezone'; -import { apiUrl, statusColor, BasicTable, staticUrl, requester } from './utils.js'; -import { NotFound } from './Misc.js'; +import { apiUrl, statusColor, requester } from './utils.js'; let vettingCache = false; let historyCache = false; let excludeSystemCache = true; let focusCache = false; - export function AdminVet(props) { - const { token, user, member, refreshVetting } = props; + const { token, member, refreshVetting } = props; const [loading, setLoading] = useState(false); const [yousure, setYousure] = useState(false); @@ -22,98 +27,129 @@ export function AdminVet(props) { if (yousure) { setLoading(true); - const data = {vetted_date: moment.utc().tz('America/Edmonton').format('YYYY-MM-DD')} + const data = { + vetted_date: moment + .utc() + .tz('America/Edmonton') + .format('YYYY-MM-DD'), + }; requester('/members/' + member.id + '/', 'PATCH', token, data) - .then(res => { - refreshVetting(); - }) - .catch(err => { - console.log(err); - }); + .then((res) => { + refreshVetting(); + }) + .catch((err) => { + console.log(err); + }); } else { setYousure(true); } }; return ( - ); } export function AdminVetting(props) { - const { token, user } = props; + const { token } = props; const [vetting, setVetting] = useState(vettingCache); - const [refreshCount, refreshVetting] = useReducer(x => x + 1, 0); - const [error, setError] = useState(false); + const [refreshCount, refreshVetting] = useReducer((x) => x + 1, 0); + const [error] = useState(false); useEffect(() => { requester('/vetting/', 'GET', token) - .then(res => { - setVetting(res.results); - vettingCache = res.results; - }) - .catch(err => { - console.log(err); - }); + .then((res) => { + setVetting(res.results); + vettingCache = res.results; + }) + .catch((err) => { + console.log(err); + }); }, [refreshCount]); return (
- {!error ? - vetting ? + {!error ? ( + vetting ? ( <> - +
Name Status - Start Date + + Start Date + - {vetting.map(x => + {vetting.map((x) => ( - {x.first_name} {x.last_name} - Email - + + {x.first_name} {x.last_name} + + + + + Email + + + + {x.status || 'Unknown'} - {x.current_start_date} - + + {x.current_start_date} + + + + - )} + ))}

- ↳ x.email).join(',')}>Email All + ↳{' '} + x.email).join(',') + } + > + Email All +

- : + ) : (

Loading...

- : + ) + ) : (

Error loading.

- } + )}
); } export function AdminHistory(props) { - const { token, user } = props; + const { token } = props; const [history, setHistory] = useState(historyCache); const [excludeSystem, setExcludeSystem] = useState(excludeSystemCache); const [focus, setFocus] = useState(focusCache); - const [error, setError] = useState(false); + const [error] = useState(false); const handleExcludeSystem = (e, v) => { setExcludeSystem(v.checked); @@ -122,93 +158,159 @@ export function AdminHistory(props) { useEffect(() => { const extra = excludeSystem ? '?exclude_system' : ''; - requester('/history/'+extra, 'GET', token) - .then(res => { - setHistory(res.results); - historyCache = res.results; - }) - .catch(err => { - console.log(err); - }); + requester('/history/' + extra, 'GET', token) + .then((res) => { + setHistory(res.results); + historyCache = res.results; + }) + .catch((err) => { + console.log(err); + }); }, [excludeSystem]); return (
- {!error ? - history ? + {!error ? ( + history ? ( <> - +
Date - Username + + Username + Type Owner Object - Changed Fields + + Changed Fields + - {history.map(x => + {history.map((x) => ( - setFocus(x.id)}> - {moment.utc(x.history_date).tz('America/Edmonton').format('YYYY-MM-DD')} + + setFocus(x.id) + } + > + {moment + .utc(x.history_date) + .tz('America/Edmonton') + .format('YYYY-MM-DD')} - {x.is_system ? 'System' : (x.history_user || 'Deleted User')} - {x.history_type} - {x.owner_name} - {x.object_name} - {x.changes.map(x => x.field).join(', ')} + + {x.is_system + ? 'System' + : x.history_user || + 'Deleted User'} + + + {x.history_type} + + + {x.owner_name} + + + {x.object_name} + + + {x.changes + .map((x) => x.field) + .join(', ')} + - {focus == x.id && - + + )} - )} + ))}
-

Object ID: {x.object_id}, Database Revert

- {!!x.changes.length && - - - - Change - Before - After - - - - {x.changes.map(y => - - {y.field} - {y.old} - {y.new} + {focus === x.id && ( + + - } + + + {x.changes.map( + (y) => ( + + + { + y.field + } + + + { + y.old + } + + + { + y.new + } + + + ) + )} + +
+

+ Object ID: {x.object_id} + ,{' '} + + Database Revert + +

+ {!!x.changes.length && ( + + + + + Change + + + Before + + + After + - )} - -
- } -
+ )} +
- : + ) : (

Loading...

- : + ) + ) : (

Error loading.

- } + )}
); -}; +} let backupsCache = false; @@ -218,67 +320,88 @@ export function AdminBackups(props) { useEffect(() => { requester('/backup/', 'GET') - .then(res => { - setBackups(res); - backupsCache = res; - }) - .catch(err => { - console.log(err); - }); + .then((res) => { + setBackups(res); + backupsCache = res; + }) + .catch((err) => { + console.log(err); + }); }, []); return (
- {!error ? - backups ? - + {!error ? ( + backups ? ( +
Username - Last Downloaded - Less than 24 hours ago? + + Last Downloaded + + + Less than 24 hours ago? + - {backups.filter(x => x.download_time).map(x => - - {x.backup_user} - {moment.utc(x.download_time).tz('America/Edmonton').format('LLLL')} - {x.less_than_24h ? 'Yes' : 'No - please investigate'} - - )} + {backups + .filter((x) => x.download_time) + .map((x) => ( + + {x.backup_user} + + {moment + .utc(x.download_time) + .tz('America/Edmonton') + .format('LLLL')} + + + {x.less_than_24h + ? 'Yes' + : 'No - please investigate'} + + + ))}
- : + ) : (

Loading...

- : + ) + ) : (

Error loading.

- } + )}
); -}; +} export function Admin(props) { return ( -
Portal Admin
+
Portal Admin
-
Ready to Vet
-

Members who are Current or Due, and past their probationary period.

+
Ready to Vet
+

+ Members who are Current or Due, and past their probationary + period. +

-
Member Data Backup
-

Spaceport backups are created daily. 14 days are kept on the server.

+
Member Data Backup
+

+ Spaceport backups are created daily. 14 days are kept on the + server. +

-
Backup Downloads
+
Backup Downloads
-
History
+
History

Last 50 database changes:

-
); -}; +} diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 79411a16..459ea548 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -1,11 +1,20 @@ import React, { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; +import { Link, useParams } from 'react-router-dom'; import './light.css'; -import { Button, Container, Checkbox, Dimmer, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; +import { + Button, + Checkbox, + Dimmer, + Form, + Header, + Icon, + Image, + Segment, + Table, +} from 'semantic-ui-react'; import moment from 'moment-timezone'; import { statusColor, BasicTable, staticUrl, requester } from './utils.js'; import { TrainingList } from './Training.js'; -import { NotFound } from './Misc.js'; function AdminCardDetail(props) { const { token, result, card } = props; @@ -17,40 +26,38 @@ function AdminCardDetail(props) { const id = card.id; 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) => { if (loading) return; setLoading(true); setSuccess(false); const data = { ...input, member_id: result.member.id }; - requester('/cards/'+id+'/', 'PUT', token, data) - .then(res => { - setLoading(false); - setSuccess(true); - setError(false); - setInput(res); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + requester('/cards/' + id + '/', 'PUT', token, data) + .then((res) => { + setLoading(false); + setSuccess(true); + setError(false); + setInput(res); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const handleDelete = (e) => { e.preventDefault(); if (yousure) { - requester('/cards/'+id+'/', 'DELETE', token) - .then(res => { - setInput(false); - }) - .catch(err => { - console.log(err); - }); + requester('/cards/' + id + '/', 'DELETE', token) + .then((res) => { + setInput(false); + }) + .catch((err) => { + console.log(err); + }); } else { setYousure(true); } @@ -70,49 +77,44 @@ function AdminCardDetail(props) { { key: '3', text: 'Card Member Blocked', value: 'card_member_blocked' }, ]; - return ( - input ? - -
- - - - - - - {success ? 'Saved.' : 'Save'} - - - - {yousure ? 'You Sure?' : 'Delete'} - - - + return input ? ( + + + + + - Notes: {input.notes || 'None'}, - Last Seen: {input.last_seen_at || 'Unknown'} - -
- : - - Deleted card: {card.card_number} - + + + {success ? 'Saved.' : 'Save'} + + + + {yousure ? 'You Sure?' : 'Delete'} + + + + Notes: {input.notes || 'None'}, Last Seen:{' '} + {input.last_seen_at || 'Unknown'} + +
+ ) : ( + + Deleted card: {card.card_number} + ); -}; +} export function AdminMemberCards(props) { const { token, result, refreshResult } = props; @@ -132,7 +134,6 @@ export function AdminMemberCards(props) { }, [result.member]); 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 }); @@ -142,29 +143,29 @@ export function AdminMemberCards(props) { setSuccess(false); const data = { ...input, member_id: result.member.id }; requester('/cards/', 'POST', token, data) - .then(res => { - setLoading(false); - setSuccess(true); - setError(false); - refreshResult(); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setLoading(false); + setSuccess(true); + setError(false); + refreshResult(); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const getCardPhoto = (e) => { e.preventDefault(); requester('/members/' + id + '/card_photo/', 'GET', token) - .then(res => res.blob()) - .then(res => { - setCardPhoto(URL.createObjectURL(res)); - }) - .catch(err => { - console.log(err); - }); + .then((res) => res.blob()) + .then((res) => { + setCardPhoto(URL.createObjectURL(res)); + }) + .catch((err) => { + console.log(err); + }); }; const makeProps = (name) => ({ @@ -183,94 +184,106 @@ export function AdminMemberCards(props) { return (
-
Edit Member Cards
+
Edit Member Cards
-
Add a Card
+
Add a Card
- {result.member.photo_large ? + {result.member.photo_large ? (

- +

- : + ) : (

No card image, member photo missing!

- } - - {cardPhoto && <> -

- -

- -
How to Print a Card
-
    -
  1. Right click on image > Save image as...
  2. -
  3. Right click on file > Print
  4. -
  5. Select Datacard Printer, Print
  6. -
  7. Plug card scanner in
  8. -
  9. Open "RfidReader" on desktop
  10. -
  11. Scan card, add the number
  12. -
  13. Have them test their card
  14. -
- } - - + )} + + {cardPhoto && ( + <> +

+ +

+ +
How to Print a Card
+
    +
  1. Right click on image > Save image as...
  2. +
  3. Right click on file > Print
  4. +
  5. Select Datacard Printer, Print
  6. +
  7. Plug card scanner in
  8. +
  9. Open "RfidReader" on desktop
  10. +
  11. Scan card, add the number
  12. +
  13. + Have them test their card +
  14. +
+ + )} + + - + Submit {success &&
Success!
} -
Current Cards
+
Current Cards
- {cards.length ? - cards.map(x => + {cards.length ? ( + cards.map((x) => ( - ) - : + )) + ) : (

None

- } + )}

- Member paused, {cards.length} card{cards.length === 1 ? '' : 's'} ignored anyway. + Member paused, {cards.length} card + {cards.length === 1 ? '' : 's'} ignored anyway.

- +

-
); -}; +} export function AdminMemberPause(props) { const { token, result, refreshResult } = props; @@ -290,17 +303,17 @@ export function AdminMemberPause(props) { setLoading(true); setSuccess(false); requester('/members/' + id + '/pause/', 'POST', token, {}) - .then(res => { - setYousure(false); - setSuccess(true); - setError(false); - refreshResult(); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(true); - }); + .then((res) => { + setYousure(false); + setSuccess(true); + setError(false); + refreshResult(); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(true); + }); } else { setYousure(true); } @@ -311,41 +324,41 @@ export function AdminMemberPause(props) { setLoading(true); setSuccess(false); requester('/members/' + id + '/unpause/', 'POST', token, {}) - .then(res => { - setSuccess(true); - setError(false); - refreshResult(); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(true); - }); + .then((res) => { + setSuccess(true); + setError(false); + refreshResult(); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(true); + }); }; return (
-
Pause / Unpause Membership
+
Pause / Unpause Membership

Pause members who are inactive, former, or on vacation.

- {result.member.paused_date ? + {result.member.paused_date ? ( - : + ) : ( - } + )}

{success &&
Success!
} {error &&

Error, something went wrong.

}
); -}; +} export function AdminMemberForm(props) { const { token, result, refreshResult } = props; @@ -360,7 +373,6 @@ export function AdminMemberForm(props) { }, [result.member]); 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 }); @@ -370,17 +382,17 @@ export function AdminMemberForm(props) { setSuccess(false); const data = { ...input, email: input.email.toLowerCase() }; requester('/members/' + id + '/', 'PATCH', token, data) - .then(res => { - setLoading(false); - setSuccess(true); - setError(false); - refreshResult(); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setLoading(false); + setSuccess(true); + setError(false); + refreshResult(); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; const makeProps = (name) => ({ @@ -393,63 +405,61 @@ export function AdminMemberForm(props) { return (
-
Edit Member Details
+
Edit Member Details
- + - + - + @@ -460,25 +470,30 @@ export function AdminMemberForm(props) {
); -}; +} export function AdminMemberInfo(props) { const member = props.result.member; return (
-
Admin Details
+
Admin Details
Name: - {member.first_name} {member.last_name} + + {member.first_name} {member.last_name} + Status: - + {member.status || 'Unknown'} @@ -487,10 +502,12 @@ export function AdminMemberInfo(props) { Expire Date: {member.expire_date} - {member.paused_date && - Paused Date: - {member.paused_date} - } + {member.paused_date && ( + + Paused Date: + {member.paused_date} + + )} Phone: @@ -499,24 +516,34 @@ export function AdminMemberInfo(props) { Minor: - {member.is_minor ? 'Yes' : 'No'} + + {member.is_minor ? 'Yes' : 'No'} + - {member.is_minor && - Birthdate: - {member.birthdate} - } - {member.is_minor && - Guardian: - {member.guardian_name} - } + {member.is_minor && ( + + Birthdate: + {member.birthdate} + + )} + {member.is_minor && ( + + Guardian: + {member.guardian_name} + + )} Emergency Contact Name: - {member.emergency_contact_name || 'None'} + + {member.emergency_contact_name || 'None'} + Emergency Contact Phone: - {member.emergency_contact_phone || 'None'} + + {member.emergency_contact_phone || 'None'} + @@ -530,35 +557,37 @@ export function AdminMemberInfo(props) { -

- {member.public_bio || 'None yet.'} -

- - {member.member_forms &&

- - View application forms - -

} +

{member.public_bio || 'None yet.'}

+ + {member.member_forms && ( +

+ + View application forms + +

+ )}
); -}; +} export function AdminMemberTraining(props) { const training = props.result.training; return (
-
Member Training
+
Member Training
- {training.length ? + {training.length ? ( - : + ) : (

None

- } - + )}
); -}; +} export function AdminCert(props) { const { token, result, name, field, refreshResult } = props; @@ -572,12 +601,12 @@ export function AdminCert(props) { let data = Object(); data[field] = moment.utc().tz('America/Edmonton').format('YYYY-MM-DD'); requester('/members/' + member.id + '/', 'PATCH', token, data) - .then(res => { - refreshResult(); - }) - .catch(err => { - console.log(err); - }); + .then((res) => { + refreshResult(); + }) + .catch((err) => { + console.log(err); + }); }; const handleUncert = (e) => { @@ -587,34 +616,26 @@ export function AdminCert(props) { let data = Object(); data[field] = null; requester('/members/' + member.id + '/', 'PATCH', token, data) - .then(res => { - refreshResult(); - }) - .catch(err => { - console.log(err); - }); + .then((res) => { + refreshResult(); + }) + .catch((err) => { + console.log(err); + }); }; useEffect(() => { setLoading(false); }, [member[field]]); - return ( - member[field] ? - - : - + return member[field] ? ( + + ) : ( + ); } @@ -623,11 +644,11 @@ export function AdminMemberCertifications(props) { return (
-
Member Certifications
+
Member Certifications

These certifications control access to the lockouts.

- +
Name @@ -640,55 +661,165 @@ export function AdminMemberCertifications(props) { Common - {member.vetted_date || member.orientation_date ? 'Yes' : 'No'} - New Members: Orientation and Basic Safety - + + {member.vetted_date || member.orientation_date + ? 'Yes' + : 'No'} + + + + New Members: Orientation and Basic Safety + + + + + Wood 1 - {member.wood_cert_date ? 'Yes, ' + member.wood_cert_date : 'No'} - Woodworking Tools 1: Intro to Saws - + + {member.wood_cert_date + ? 'Yes, ' + member.wood_cert_date + : 'No'} + + + + Woodworking Tools 1: Intro to Saws + + + + + Wood 2 - {member.wood2_cert_date ? 'Yes, ' + member.wood2_cert_date : 'No'} - Woodworking Tools 2: Jointer, Thickness Planer, Drum Sander - + + {member.wood2_cert_date + ? 'Yes, ' + member.wood2_cert_date + : 'No'} + + + + Woodworking Tools 2: Jointer, Thickness Planer, + Drum Sander + + + + + Lathe - {member.lathe_cert_date ? 'Yes, ' + member.lathe_cert_date : 'No'} - Metal: Metal Cutting & Manual Lathe - + + {member.lathe_cert_date + ? 'Yes, ' + member.lathe_cert_date + : 'No'} + + + + Metal: Metal Cutting & Manual Lathe + + + + + Mill - {member.mill_cert_date ? 'Yes, ' + member.mill_cert_date : 'No'} - Metal: Manual Mill & Advanced Lathe - + + {member.mill_cert_date + ? 'Yes, ' + member.mill_cert_date + : 'No'} + + + + Metal: Manual Mill & Advanced Lathe + + + + + CNC - {member.cnc_cert_date ? 'Yes, ' + member.cnc_cert_date : 'No'} - Tormach: CAM and Tormach Intro - + + {member.cnc_cert_date + ? 'Yes, ' + member.cnc_cert_date + : 'No'} + + + + Tormach: CAM and Tormach Intro + + + + + Rabbit Laser - {member.rabbit_cert_date ? 'Yes, ' + member.rabbit_cert_date : 'No'} - Laser: Cutting and Engraving - + + {member.rabbit_cert_date + ? 'Yes, ' + member.rabbit_cert_date + : 'No'} + + + + Laser: Cutting and Engraving + + + + + Trotec Laser - {member.trotec_cert_date ? 'Yes, ' + member.trotec_cert_date : 'No'} - Laser: Trotec Course - + + {member.trotec_cert_date + ? 'Yes, ' + member.trotec_cert_date + : 'No'} + + + Laser: Trotec Course + + + +
-
); -}; +} diff --git a/webclient/src/AdminTransactions.js b/webclient/src/AdminTransactions.js index 0ef6fd50..9e1e47be 100644 --- a/webclient/src/AdminTransactions.js +++ b/webclient/src/AdminTransactions.js @@ -1,52 +1,52 @@ import React, { useState, useEffect } from 'react'; -import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import './light.css'; -import { Button, Container, Checkbox, Dimmer, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; +import { Container, Checkbox, Form, Header, Segment } from 'semantic-ui-react'; import * as Datetime from 'react-datetime'; import 'react-datetime/css/react-datetime.css'; import moment from 'moment'; -import { statusColor, BasicTable, staticUrl, requester } from './utils.js'; +import { requester } from './utils.js'; import { TransactionList, TransactionEditor } from './Transactions.js'; -import { NotFound } from './Misc.js'; export function AdminReportedTransactions(props) { - const { token, user } = props; + const { token } = props; const [transactions, setTransactions] = useState(false); const [error, setError] = useState(false); useEffect(() => { requester('/transactions/', 'GET', token) - .then(res => { - setTransactions(res.results); - setError(false); - }) - .catch(err => { - console.log(err); - setError(true); - }); + .then((res) => { + setTransactions(res.results); + setError(false); + }) + .catch((err) => { + console.log(err); + setError(true); + }); }, []); return (
- {!error ? - transactions ? + {!error ? ( + transactions ? (
- : + ) : (

Loading...

- : + ) + ) : (

Error loading.

- } + )}
); -}; +} let transactionsCache = false; let excludePayPalCache = false; export function AdminHistoricalTransactions(props) { - const { token, user } = props; + const { token } = props; const [input, setInput] = useState({ month: moment() }); const [transactions, setTransactions] = useState(transactionsCache); const [excludePayPal, setExcludePayPal] = useState(excludePayPalCache); @@ -65,17 +65,17 @@ export function AdminHistoricalTransactions(props) { setLoading(true); const month = input.month.format('YYYY-MM'); requester('/transactions/?month=' + month, 'GET', token) - .then(res => { - setLoading(false); - setError(false); - setTransactions(res.results); - transactionsCache = res.results; - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(true); - }); + .then((res) => { + setLoading(false); + setError(false); + setTransactions(res.results); + transactionsCache = res.results; + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(true); + }); }; return ( @@ -85,45 +85,59 @@ export function AdminHistoricalTransactions(props) { - - Submit - + Submit - {!error ? - transactions &&
-

Found {transactions.length} transactions.

- {!!transactions.length && -
{moment(transactions[0].date, 'YYYY-MM-DD').format('MMMM YYYY')} Transactions
- } - - - - !excludePayPal || x.account_type !== 'PayPal')} /> -
- : + {!error ? ( + transactions && ( +
+

Found {transactions.length} transactions.

+ {!!transactions.length && ( +
+ {moment( + transactions[0].date, + 'YYYY-MM-DD' + ).format('MMMM YYYY')}{' '} + Transactions +
+ )} + + + + + !excludePayPal || + x.account_type !== 'PayPal' + )} + /> +
+ ) + ) : (

Error loading transactions.

- } + )} ); -}; +} export function AdminAddTransaction(props) { const { token } = props; - const [open, setOpen] = useState(false); - const [input, setInput] = useState({ date: moment().format('YYYY-MM-DD'), info_source: 'Web' }); + const [input, setInput] = useState({ + date: moment().format('YYYY-MM-DD'), + info_source: 'Web', + }); const [error, setError] = useState(false); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); @@ -133,69 +147,79 @@ export function AdminAddTransaction(props) { setLoading(true); setSuccess(false); requester('/transactions/', 'POST', token, input) - .then(res => { - setSuccess(res.id); - setInput({}); - setLoading(false); - setError(false); - }) - .catch(err => { - setLoading(false); - console.log(err); - setError(err.data); - }); + .then((res) => { + setSuccess(res.id); + setInput({}); + setLoading(false); + setError(false); + }) + .catch((err) => { + setLoading(false); + console.log(err); + setError(err.data); + }); }; return (
- + Submit - {success &&

Added! View the transaction.

} + {success && ( +

+ Added!{' '} + + View the transaction. + +

+ )} ); -}; +} export function AdminTransactions(props) { return ( -
Admin Transactions
+
Admin Transactions
-
Add a Transaction
+
Add a Transaction
-
Reported
+
Reported
-
Historical
+
Historical
); } export function AdminMemberTransactions(props) { - const { token, result, refreshResult } = props; + const { result } = props; const transactions = result.transactions; - const { id } = useParams(); return (
-
Member Transactions
+
Member Transactions
- Add a transaction + Add a transaction -
Current Transactions
+
Current Transactions
- {transactions.length ? + {transactions.length ? ( - : + ) : (

None

- } - + )}
); -}; +} diff --git a/webclient/src/App.js b/webclient/src/App.js index c3d52e92..9343d085 100644 --- a/webclient/src/App.js +++ b/webclient/src/App.js @@ -1,9 +1,9 @@ -import React, { useState, useEffect, useReducer, useContext } from 'react'; -import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; +import React, { useState, useEffect, useReducer } from 'react'; +import { Switch, Route, Link, useHistory } from 'react-router-dom'; import './semantic-ui/semantic.min.css'; import './light.css'; import './dark.css'; -import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; +import { Container, Dropdown, Menu } from 'semantic-ui-react'; import Darkmode from 'darkmode-js'; import { isAdmin, requester } from './utils.js'; import { ManageScroll } from './ManageScroll.js'; @@ -27,8 +27,10 @@ import { Footer } from './Footer.js'; function App() { const [token, setToken] = useState(localStorage.getItem('token', '')); - const [user, setUser] = useState(JSON.parse(localStorage.getItem('user', 'false'))); - const [refreshCount, refreshUser] = useReducer(x => x + 1, 0); + const [user, setUser] = useState( + JSON.parse(localStorage.getItem('user', 'false')) + ); + const [refreshCount, refreshUser] = useReducer((x) => x + 1, 0); const [yousure, setYousure] = useState(false); const history = useHistory(); @@ -44,13 +46,13 @@ function App() { useEffect(() => { requester('/user/', 'GET', token) - .then(res => { - setUserCache(res); - }) - .catch(err => { - console.log(err); - setUserCache(null); - }); + .then((res) => { + setUserCache(res); + }) + .catch((err) => { + console.log(err); + setUserCache(null); + }); }, [token, refreshCount]); function logout() { @@ -73,14 +75,14 @@ function App() { path: history.location.pathname, }; requester('/ping/', 'POST', token, data) - .then() - .catch(err => { - console.log(err); - if (err.data && err.data.detail === 'Invalid token.') { - logout(); // You Sure? - logout(); - } - }); + .then() + .catch((err) => { + console.log(err); + if (err.data && err.data.detail === 'Invalid token.') { + logout(); // You Sure? + logout(); + } + }); } }, [history.location]); @@ -91,7 +93,7 @@ function App() { buttonColorDark: '#666', buttonColorLight: '#aaa', label: '🌙', - } + }; const darkmode = new Darkmode(options); darkmode.showWidget(); }, []); @@ -100,208 +102,242 @@ function App() {
-
-
- - -
- - - -
- - {window.location.hostname !== 'my.protospace.ca' && -

~~~~~ Development site ~~~~~

- } -
- - - - - - - - - - - - - - - - - - - - - - - {user && isAdmin(user) && } - - {user && isAdmin(user) && } - - - - {user && - +
+ +
+ + Logo + +
+ + {window.location.hostname !== 'my.protospace.ca' && ( +

+ ~~~~~ Development site ~~~~~ +

+ )} +
+ + + + + + + + + + + + + + + + + + + + + + + {user && isAdmin(user) && ( + + )} + + {user && isAdmin(user) && ( + + )} + + + + {user && ( + + + + + )} + + + + + - - } - -
- - - - - -
- - - - - - - - - - - - - - - - - - - {user && user.member.set_details ? +
- - - - - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - {user && isAdmin(user) && - - + {user && user.member.set_details ? ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {user && isAdmin(user) && ( + + + + )} + + {user && isAdmin(user) && ( + + + + )} + + + + + + ) : ( + + - } - - {user && isAdmin(user) && - - - - } - - - - + )} - : - - - - } - -
- -
+
+