Improve tables on mobile

master
Tanner Collin 11 months ago
parent d7928e7578
commit 6091d255d2
  1. 38
      webclient/src/Admin.js
  2. 13
      webclient/src/AdminTransactions.js
  3. 13
      webclient/src/Cards.js
  4. 24
      webclient/src/Courses.js
  5. 34
      webclient/src/Training.js
  6. 13
      webclient/src/Transactions.js

@ -5,7 +5,7 @@ import { Button, Container, Checkbox, Form, Header, Icon, Table } from 'semantic
import * as Datetime from 'react-datetime';
import moment from 'moment-timezone';
import download from 'downloadjs';
import { apiUrl, statusColor, requester } from './utils.js';
import { apiUrl, statusColor, requester, useIsMobile } from './utils.js';
let vettingCache = false;
let historyCache = false;
@ -123,6 +123,7 @@ export function AdminHistory(props) {
const [excludeSystem, setExcludeSystem] = useState(excludeSystemCache);
const [focus, setFocus] = useState(focusCache);
const [error, setError] = useState(false);
const isMobile = useIsMobile();
const handleExcludeSystem = (e, v) => {
setExcludeSystem(v.checked);
@ -154,7 +155,7 @@ export function AdminHistory(props) {
/>
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Date</Table.HeaderCell>
<Table.HeaderCell>Username</Table.HeaderCell>
@ -163,7 +164,7 @@ export function AdminHistory(props) {
<Table.HeaderCell>Object</Table.HeaderCell>
<Table.HeaderCell>Changed Fields</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{history.map(x =>
@ -174,11 +175,11 @@ export function AdminHistory(props) {
{moment.utc(x.history_date).tz('America/Edmonton').format('YYYY-MM-DD')}
</a>
</Table.Cell>
<Table.Cell>{x.is_system ? 'System' : (x.history_user || 'Deleted User')}</Table.Cell>
<Table.Cell>{x.history_type}</Table.Cell>
<Table.Cell>{x.owner_name}</Table.Cell>
<Table.Cell>{x.object_name}</Table.Cell>
<Table.Cell>{x.changes.map(x => x.field).join(', ')}</Table.Cell>
<Table.Cell>{isMobile && 'User: '}{x.is_system ? 'System' : (x.history_user || 'Deleted User')}</Table.Cell>
<Table.Cell>{isMobile && 'Type: '}{x.history_type}</Table.Cell>
<Table.Cell>{isMobile && 'Owner: '}{x.owner_name}</Table.Cell>
<Table.Cell>{isMobile && 'Object: '}{x.object_name}</Table.Cell>
<Table.Cell>{isMobile && 'Changed: '}{x.changes.map(x => x.field).join(', ')}</Table.Cell>
</Table.Row>
{focus === x.id &&
@ -186,19 +187,19 @@ export function AdminHistory(props) {
<p>Object ID: {x.object_id}, <a href={apiUrl+x.revert_url} target='_blank'>Database Revert</a></p>
{!!x.changes.length &&
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Change</Table.HeaderCell>
<Table.HeaderCell>Before</Table.HeaderCell>
<Table.HeaderCell>After</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{x.changes.map(y =>
<Table.Row key={y.field}>
<Table.Cell>{y.field}</Table.Cell>
<Table.Cell>{y.old}</Table.Cell>
<Table.Cell>{y.new}</Table.Cell>
<Table.Cell>{isMobile && 'Change: '}{y.field}</Table.Cell>
<Table.Cell>{isMobile && 'Before: '}{y.old}</Table.Cell>
<Table.Cell>{isMobile && 'After: '}{y.new}</Table.Cell>
</Table.Row>
)}
</Table.Body>
@ -225,6 +226,7 @@ let backupsCache = false;
export function AdminBackups(props) {
const [backups, setBackups] = useState(backupsCache);
const [error, setError] = useState(false);
const isMobile = useIsMobile();
useEffect(() => {
requester('/backup/', 'GET')
@ -243,20 +245,20 @@ export function AdminBackups(props) {
{!error ?
backups ?
<Table collapsing basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Username</Table.HeaderCell>
<Table.HeaderCell>Last Downloaded</Table.HeaderCell>
<Table.HeaderCell>Less than 24 hours ago?</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{backups.filter(x => x.download_time).map(x =>
<Table.Row key={x.backup_user}>
<Table.Cell>{x.backup_user}</Table.Cell>
<Table.Cell>{moment.utc(x.download_time).tz('America/Edmonton').format('LLLL')}</Table.Cell>
<Table.Cell>{x.less_than_24h ? 'Yes' : 'No - please investigate'}</Table.Cell>
<Table.Cell>{isMobile && 'User: '}{x.backup_user}</Table.Cell>
<Table.Cell>{isMobile && 'Last: '}{moment.utc(x.download_time).tz('America/Edmonton').format('LLLL')}</Table.Cell>
<Table.Cell>{isMobile && '24h ago: '}{x.less_than_24h ? 'Yes' : 'No - please investigate'}</Table.Cell>
</Table.Row>
)}
</Table.Body>

@ -5,7 +5,7 @@ import { Container, Checkbox, Form, Header, Segment, Table } from 'semantic-ui-r
import * as Datetime from 'react-datetime';
import 'react-datetime/css/react-datetime.css';
import moment from 'moment';
import { requester } from './utils.js';
import { requester, useIsMobile } from './utils.js';
import { TransactionList, TransactionEditor } from './Transactions.js';
export function AdminReportedTransactions(props) {
@ -53,6 +53,7 @@ export function AdminHistoricalTransactions(props) {
const [excludeSnacks, setExcludeSnacks] = useState(true);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const isMobile = useIsMobile();
const handleDatetime = (v) => setInput({ ...input, month: v });
@ -129,20 +130,20 @@ export function AdminHistoricalTransactions(props) {
<Header size='small'>Summary</Header>
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Category</Table.HeaderCell>
<Table.HeaderCell>Dollar</Table.HeaderCell>
<Table.HeaderCell>Protocoin</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{summary.map(x =>
<Table.Row key={x.category}>
<Table.Cell>{x.category}</Table.Cell>
<Table.Cell>{'$' + x.dollar.toFixed(2)}</Table.Cell>
<Table.Cell>{'₱' + x.protocoin.toFixed(2)}</Table.Cell>
<Table.Cell>{isMobile && 'Category: '}{x.category}</Table.Cell>
<Table.Cell>{isMobile && 'Dollar: '}{'$' + x.dollar.toFixed(2)}</Table.Cell>
<Table.Cell>{isMobile && 'Protocoin: '}{'₱' + x.protocoin.toFixed(2)}</Table.Cell>
</Table.Row>
)}
</Table.Body>

@ -2,11 +2,12 @@ import React, { useState } from 'react';
import './light.css';
import moment from 'moment-timezone';
import { Button, Container, Header, Table } from 'semantic-ui-react';
import { BasicTable, staticUrl } from './utils.js';
import { BasicTable, staticUrl, useIsMobile } from './utils.js';
export function Cards(props) {
const { user, token } = props;
const [open, setOpen] = useState(false);
const isMobile = useIsMobile();
const cardStatus = (c) => c.active_status === 'card_active' ? 'Yes' : 'No';
const card = user.cards[0];
@ -30,22 +31,22 @@ export function Cards(props) {
{user.cards.length ?
user.cards.length > 1 ?
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Number</Table.HeaderCell>
<Table.HeaderCell>Notes</Table.HeaderCell>
<Table.HeaderCell>Last Scan</Table.HeaderCell>
<Table.HeaderCell>Active</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{user.cards.map(x =>
<Table.Row key={x.id}>
<Table.Cell>{x.card_number}</Table.Cell>
<Table.Cell>{x.notes}</Table.Cell>
<Table.Cell>{isMobile && 'Notes: '}{x.notes}</Table.Cell>
<Table.Cell>
{x.last_seen ?
{isMobile && 'Last Scan: '}{x.last_seen ?
x.last_seen > '2021-11-14T02:01:35.415685Z' ?
moment.utc(x.last_seen).tz('America/Edmonton').format('lll')
:
@ -54,7 +55,7 @@ export function Cards(props) {
'Unknown'
}
</Table.Cell>
<Table.Cell>{cardStatus(x)}</Table.Cell>
<Table.Cell>{isMobile && 'Active: '}{cardStatus(x)}</Table.Cell>
</Table.Row>
)}
</Table.Body>

@ -3,7 +3,7 @@ import { Link, useParams } from 'react-router-dom';
import './light.css';
import { Button, Label, Container, Header, Segment, Table } from 'semantic-ui-react';
import moment from 'moment-timezone';
import { isInstructor, getInstructor, requester } from './utils.js';
import { isInstructor, getInstructor, requester, useIsMobile } from './utils.js';
import { NotFound } from './Misc.js';
import { InstructorCourseList, InstructorCourseDetail } from './InstructorCourses.js';
import { InstructorClassList } from './InstructorClasses.js';
@ -31,6 +31,7 @@ export function Courses(props) {
const [courses, setCourses] = useState(courseCache);
const [tagFilter, setTagFilter] = useState(tagFilterCache);
const { token, user } = props;
const isMobile = useIsMobile();
useEffect(() => {
requester('/courses/', 'GET', token)
@ -95,13 +96,13 @@ export function Courses(props) {
{courses ?
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Interest</Table.HeaderCell>
<Table.HeaderCell></Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{courses.length ?
@ -111,7 +112,7 @@ export function Courses(props) {
<Link to={'/courses/'+x.id}>{x.name}</Link>
</Table.Cell>
<Table.Cell>
{!!x.num_interested &&
{isMobile && 'Interest: '}{!!x.num_interested &&
<>{x.num_interested} member{x.num_interested !== 1 && 's'}</>
}
</Table.Cell>
@ -143,6 +144,7 @@ export function CourseDetail(props) {
const [loading, setLoading] = useState(false);
const { token, user, refreshUser } = props;
const { id } = useParams();
const isMobile = useIsMobile();
const handleInterest = () => {
if (loading) return;
@ -223,7 +225,7 @@ export function CourseDetail(props) {
</Segment>}
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Date</Table.HeaderCell>
<Table.HeaderCell>Time</Table.HeaderCell>
@ -231,7 +233,7 @@ export function CourseDetail(props) {
<Table.HeaderCell>Cost</Table.HeaderCell>
<Table.HeaderCell>Students</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{course.sessions.length ?
@ -239,14 +241,14 @@ export function CourseDetail(props) {
<Table.Row key={x.id} active={x.datetime < now || x.is_cancelled}>
<Table.Cell>
<Link to={'/classes/'+x.id}>
&nbsp;{moment.utc(x.datetime).tz('America/Edmonton').format('ll')}
{!isMobile && <span>&nbsp;</span>}{moment.utc(x.datetime).tz('America/Edmonton').format('ll')}
</Link>
</Table.Cell>
<Table.Cell>{x.is_cancelled ? 'Cancelled' : moment.utc(x.datetime).tz('America/Edmonton').format('LT')}</Table.Cell>
<Table.Cell>{getInstructor(x)}</Table.Cell>
<Table.Cell>{x.cost === '0.00' ? 'Free' : '$'+x.cost}</Table.Cell>
<Table.Cell>{isMobile && 'Time: '}{x.is_cancelled ? 'Cancelled' : moment.utc(x.datetime).tz('America/Edmonton').format('LT')}</Table.Cell>
<Table.Cell>{isMobile && 'Instructor: '}{getInstructor(x)}</Table.Cell>
<Table.Cell>{isMobile && 'Cost: '}{x.cost === '0.00' ? 'Free' : '$'+x.cost}</Table.Cell>
<Table.Cell>
{!!x.max_students ?
{isMobile && 'Students: '}{!!x.max_students ?
x.max_students <= x.student_count ?
'Full'
:

@ -3,10 +3,11 @@ import { Link } from 'react-router-dom';
import './light.css';
import { Container, Header, Popup, Table } from 'semantic-ui-react';
import moment from 'moment-timezone';
import { getInstructor } from './utils.js';
import { getInstructor, useIsMobile } from './utils.js';
export function CertList(props) {
const { member } = props;
const isMobile = useIsMobile();
const MoreCert = (tools) => (<Popup content={
<>
@ -17,58 +18,58 @@ export function CertList(props) {
return (
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>Certification</Table.HeaderCell>
<Table.HeaderCell>Course</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
<Table.Row>
<Table.Cell>Common {MoreCert('Anything larger than a screwdriver.')}</Table.Cell>
<Table.Cell>{member.vetted_date || member.orientation_date ? 'Yes' : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.vetted_date || member.orientation_date ? 'Yes' : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/249'>New Members: Orientation and Basic Safety</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Wood 1 {MoreCert('Table saw, band saw, chop saw, router.')}</Table.Cell>
<Table.Cell>{member.wood_cert_date ? 'Yes, ' + member.wood_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.wood_cert_date ? 'Yes, ' + member.wood_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/261'>Woodworking Tools 1: Intro to Saws</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Wood 2 {MoreCert('Jointer, thickness planer, drum sander.')}</Table.Cell>
<Table.Cell>{member.wood2_cert_date ? 'Yes, ' + member.wood2_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.wood2_cert_date ? 'Yes, ' + member.wood2_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/401'>Woodworking Tools 2: Jointer, Thickness Planer, Drum Sander</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Lathe {MoreCert('Manual metal lathe.')}</Table.Cell>
<Table.Cell>{member.lathe_cert_date ? 'Yes, ' + member.lathe_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.lathe_cert_date ? 'Yes, ' + member.lathe_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/281'>Metal: Metal Cutting & Manual Lathe</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Mill {MoreCert('Manual metal mill.')}</Table.Cell>
<Table.Cell>{member.mill_cert_date ? 'Yes, ' + member.mill_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.mill_cert_date ? 'Yes, ' + member.mill_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/283'>Metal: Manual Mill & Advanced Lathe</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Tormach CNC</Table.Cell>
<Table.Cell>{member.tormach_cnc_cert_date ? 'Yes, ' + member.tormach_cnc_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.tormach_cnc_cert_date ? 'Yes, ' + member.tormach_cnc_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/259'>Tormach: CAM and Tormach Intro</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Precix CNC</Table.Cell>
<Table.Cell>{member.precix_cnc_cert_date ? 'Yes, ' + member.precix_cnc_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.precix_cnc_cert_date ? 'Yes, ' + member.precix_cnc_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/428'>Basic CNC Wood Router</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Rabbit Laser</Table.Cell>
<Table.Cell>{member.rabbit_cert_date ? 'Yes, ' + member.rabbit_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.rabbit_cert_date ? 'Yes, ' + member.rabbit_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/247'>Laser: Cutting and Engraving</Link></Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>Trotec Laser</Table.Cell>
<Table.Cell>{member.trotec_cert_date ? 'Yes, ' + member.trotec_cert_date : 'No'}</Table.Cell>
<Table.Cell>{isMobile && 'Certified: '}{member.trotec_cert_date ? 'Yes, ' + member.trotec_cert_date : 'No'}</Table.Cell>
<Table.Cell><Link to='/courses/321'>Laser: Trotec Course</Link></Table.Cell>
</Table.Row>
</Table.Body>
@ -78,17 +79,18 @@ export function CertList(props) {
export function TrainingList(props) {
const { training } = props;
const isMobile = useIsMobile();
return (
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Course / Event Name</Table.HeaderCell>
<Table.HeaderCell>Class Date</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Instructor</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{training.slice().sort((a, b) => a.session.datetime < b.session.datetime ? 1 : -1).map(x =>
@ -97,8 +99,8 @@ export function TrainingList(props) {
<Table.Cell>
<Link to={'/classes/'+x.session.id}>{moment(x.session.datetime).tz('America/Edmonton').format('ll')}</Link>
</Table.Cell>
<Table.Cell>{x.attendance_status}</Table.Cell>
<Table.Cell>{getInstructor(x.session)}</Table.Cell>
<Table.Cell>{isMobile && 'Attendance: '}{x.attendance_status}</Table.Cell>
<Table.Cell>{isMobile && 'Instructor: '}{getInstructor(x.session)}</Table.Cell>
</Table.Row>
)}
</Table.Body>

@ -5,7 +5,7 @@ import ReactToPrint from 'react-to-print';
import './light.css';
import { Button, Container, Form, Grid, Header, Message, Segment, Table } from 'semantic-ui-react';
import { MembersDropdown } from './Members.js';
import { isAdmin, BasicTable, requester } from './utils.js';
import { isAdmin, BasicTable, requester, useIsMobile } from './utils.js';
import { NotFound } from './Misc.js';
export function TransactionEditor(props) {
@ -214,10 +214,11 @@ function EditTransaction(props) {
export function TransactionList(props) {
const { transactions, noMember, noCategory } = props;
const isMobile = useIsMobile();
return (
<Table basic='very'>
<Table.Header>
{!isMobile && <Table.Header>
<Table.Row>
<Table.HeaderCell>Date</Table.HeaderCell>
{!noMember && <Table.HeaderCell>Member</Table.HeaderCell>}
@ -226,7 +227,7 @@ export function TransactionList(props) {
{!noCategory && <Table.HeaderCell>Category</Table.HeaderCell>}
<Table.HeaderCell>Memo</Table.HeaderCell>
</Table.Row>
</Table.Header>
</Table.Header>}
<Table.Body>
{transactions.length ?
@ -244,9 +245,9 @@ export function TransactionList(props) {
x.member_name
}
</Table.Cell>}
<Table.Cell style={{ minWidth: '8rem' }}>{x.protocoin !== '0.00' ? '₱' + x.protocoin : '$' + x.amount}</Table.Cell>
<Table.Cell>{x.account_type}</Table.Cell>
{!noCategory && <Table.Cell>{x.category}</Table.Cell>}
<Table.Cell style={{ minWidth: '8rem' }}>{isMobile && 'Amount: '}{x.protocoin !== '0.00' ? '₱' + x.protocoin : '$' + x.amount}</Table.Cell>
<Table.Cell>{isMobile && 'Method: '}{x.account_type}</Table.Cell>
{!noCategory && <Table.Cell>{isMobile && 'Category: '}{x.category}</Table.Cell>}
<Table.Cell>{x.memo || x.report_memo}</Table.Cell>
</Table.Row>
)

Loading…
Cancel
Save