This commit is contained in:
E
2021-03-10 19:17:23 -07:00
parent 15023b0e24
commit c29a64b1cc
24 changed files with 220 additions and 155 deletions

View File

@@ -47,6 +47,10 @@
margin: 2rem;
}
.dashboard-form {
margin: auto;
}
.ant-card {
margin: 1rem;
}
@@ -54,3 +58,12 @@
.ant-card img {
max-width: 250px;
}
.loading-bar-container {
background: #282c34;
width: 100%;
}
.loading-bar {
background: green;
}

View File

@@ -1,23 +1,42 @@
import { Client } from '../types'
import axios from 'axios'
import { message } from 'antd'
import { Status } from '../components/StatusChip'
if (process.env.NODE_ENV === 'development') {
const dev = process.env.NODE_ENV === 'development'
if (dev) {
const host = 'http://192.168.1.107:5000'
axios.defaults.baseURL = host
}
const mock = false
export const createClient = async (body: Omit<Client, 'has_photos'>) => {
if (mock) return 'test'
const res = await axios.post<{ client_id: string }>(`/api/clients`, body)
return res.data.client_id
}
export const getClient = async (id: string): Promise<Client> => {
if (mock)
return {
name: 'Test Client',
has_photos: false,
email: 'test@test.test',
phone: 1234567890,
}
const res = await axios.get<Client>(`/api/clients/${id}`)
return res.data
}
export const startSession = async (clientId: string) => {
const res = await axios.post(`/api/clients/${clientId}/session`)
return res.data // session data
try {
const res = await axios.post(`/api/clients/${clientId}/session`)
return res.data
} catch (err) {
message.error('Something went wrong, check connection with the machine')
return err
}
}
export const getSession = async (clientId: string) => {
@@ -38,6 +57,11 @@ export const restartSession = async (clientId: string) => {
// TOOD: Get status
export const getStatus = async (): Promise<Status> => {
const res = await axios.get<{ status: Status }>('/api/status')
return res.data.status
}
// Someday
export const getClients = async (): Promise<Client[]> => {

View File

@@ -11,16 +11,9 @@ export const SessionPictures = ({ clientId }: Props) => {
const [activeUrl, setActiveUrl] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const handleSync = async () => {
message.info('Syncing photos...')
const { photos } = await getSession(clientId)
if (photos.length) setUrls(photos)
}
useEffect(() => {
const get = async () => {
if (urls && urls.length >= 89 * 2) {
setLoading(false)
if (urls && urls.length >= 89 * 1) {
return
}
@@ -53,23 +46,25 @@ export const SessionPictures = ({ clientId }: Props) => {
width="100%"
onClick={closeModal}
src={`${host}/output/${clientId}/${activeUrl}`}
alt="large image"
alt="large modal"
></img>
</Modal>
<PageHeader
title="Session Pictures"
subTitle={`${urls.length}/${89 * 2} loaded`}
></PageHeader>
subTitle={`${urls.length}/${89 * 1} loaded`}
>
<div className="loading-bar-container">
<div className="loading-bar"></div>
</div>
</PageHeader>
<div className="photo-wall">
{urls ? (
urls
.sort((a, b) => a.split('_')[0].localeCompare(b.split('_')[0]))
.map((src) => (
<Card className="photo" title={src.split('_')[0]}>
<Card key={src} className="photo" title={src.split('_')[0]}>
<img
onClick={() => setActiveUrl(src)}
key={src}
id={src}
src={`${host}/output/${clientId}/${src}`}
alt="lol"
/>

View File

@@ -0,0 +1,47 @@
import { Tag } from 'antd'
import { PresetColorType } from 'antd/lib/_util/colors'
import { useEffect, useState } from 'react'
import { getStatus } from '../api'
export enum Status {
'Standing By...',
'Warming Up...',
'Capturing Photo',
'Capturing Grid',
'Writing To Disk',
'Downloading!',
}
const colors: Partial<PresetColorType>[] = [
'lime',
'gold',
'volcano',
'magenta',
'geekblue',
]
type Props = {
poll: boolean
}
export const StatusChip = ({ poll }: Props) => {
const [status, setStatus] = useState<Status>(Status['Standing By...'])
useEffect(() => {
const get = async () => {
if (!poll) return
const status = await getStatus()
setStatus(status)
}
const interval = setInterval(get, 1000 / 4)
return () => clearInterval(interval)
}, [])
return (
<Tag color={colors[status]} style={{ display: 'flex' }}>
<span style={{ margin: 'auto' }}>{Status[status]}</span>
</Tag>
)
}

View File

@@ -1,4 +1,6 @@
import { Button, Divider, message, PageHeader } from 'antd'
import { Button, Divider, Form, Input, message, PageHeader, Row } from 'antd'
import FormItem from 'antd/lib/form/FormItem'
import { Store } from 'antd/lib/form/interface'
import { Content } from 'antd/lib/layout/layout'
import React, { FormEvent } from 'react'
import { useState } from 'react'
@@ -8,21 +10,11 @@ import { createClient } from '../api'
export const Dashboard = () => {
const history = useHistory()
const [error, setError] = useState<string | null>(null)
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const handleReset = () => {
//
setName('')
setEmail('')
setPhone('')
}
const handleReset = () => {}
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
if (phone.length < 10) {
const handleSubmit = async (values: Store) => {
if (values.phone.length < 10) {
// helpful message
message.error('Check all fields!')
setError('Phone number needs to be a length of at least 10')
@@ -30,9 +22,9 @@ export const Dashboard = () => {
}
const client_id = await createClient({
name,
email,
phone: parseInt(phone),
name: values.name,
email: values.email,
phone: parseInt(values.phone.replace(/\D/g, '')),
})
history.push(`/sessions/${client_id}`)
@@ -45,42 +37,31 @@ export const Dashboard = () => {
subTitle="Enter the name, email and phone number of the subject"
></PageHeader>
<Divider />
<form onSubmit={handleSubmit}>
<label htmlFor="name">
Name:
<input
minLength={3}
value={name}
onChange={(e) => setName(e.target.value)}
name="name"
/>
</label>
<label htmlFor="email">
Email:
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
name="email"
/>
</label>
<label htmlFor="phone">
Phone:
<input
value={phone}
onChange={(e) => setPhone(e.target.value)}
type="tel"
name="phone"
/>
</label>
<Button danger onClick={handleReset}>
Reset
</Button>
<Button htmlType="submit" type="primary">
Start Session
</Button>
<Form
className="dashboard-form"
onFinish={handleSubmit}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
>
<FormItem label="name" name="name">
<Input minLength={3} />
</FormItem>
<FormItem label="email" name="email">
<Input type="email" />
</FormItem>
<FormItem label="phone" name="phone">
<Input type="tel" minLength={10} />
</FormItem>
<Row justify="space-between">
<Button danger onClick={handleReset}>
Reset
</Button>
<Button htmlType="submit" type="primary">
Start Session
</Button>
</Row>
{error && <p className="error">{error}</p>}
</form>
</Form>
</Content>
)
}

View File

@@ -1,16 +1,10 @@
import React, { useEffect, useState } from 'react'
import { RouteComponentProps, useHistory } from 'react-router-dom'
import { getClient, killSession, restartSession, startSession } from '../api'
import { SessionPictures } from './SessionPictures'
import {
Button,
Divider,
message,
PageHeader,
Popconfirm,
Row,
Tag,
} from 'antd'
import { SessionPictures } from '../components/SessionPictures'
import { StatusChip } from '../components/StatusChip'
import { Button, Divider, message, Popconfirm, Row, Typography } from 'antd'
import { Content } from 'antd/lib/layout/layout'
import { Client } from '../types'
@@ -59,50 +53,61 @@ export const Session = (props: Props) => {
return (
<Content>
<PageHeader
ghost={false}
onBack={() => history.goBack()}
title={`Session for ${clientId}`}
tags={active ? <Tag color="lime">Active</Tag> : <Tag>Inactive</Tag>}
subTitle={`session has ${active ? 'started' : 'not started'}`}
extra={[
<Button
key="startsession"
disabled={active}
type="primary"
onClick={handleStartSession}
>
Capture
</Button>,
<Popconfirm
key="retry"
title="Re-capture set?"
onConfirm={handleRestartSession}
>
<Button type="default" disabled={!active}>
Retry Capture
</Button>
</Popconfirm>,
<Popconfirm
key="nuke"
title="Delete all photos and return to dashboard?"
onConfirm={handleNuke}
>
<Button danger disabled={!active}>
Abort Session
</Button>
</Popconfirm>,
<Button
key="finish"
ghost
type="primary"
disabled={!active}
onClick={handleExit}
>
Finish Session
</Button>,
]}
></PageHeader>
<Row justify="center">
<Typography.Title>Client: {client?.name}</Typography.Title>
</Row>
<Row justify="space-around" style={{ width: '60%', margin: 'auto' }}>
<Typography.Text>
<strong>email:</strong> {client?.email}
</Typography.Text>
<Typography.Text>
<strong>phone:</strong> {client?.phone}
</Typography.Text>
</Row>
<Divider />
<Row justify="center" className="session-header">
<Button
key="startsession"
disabled={active}
type="primary"
onClick={handleStartSession}
>
Capture
</Button>
<Popconfirm
disabled={!active}
key="retry"
title="Re-capture set?"
onConfirm={handleRestartSession}
>
<Button type="default" disabled={!active}>
Retry Capture
</Button>
</Popconfirm>
<Popconfirm
key="nuke"
disabled={!active}
title="Delete all photos and return to dashboard?"
onConfirm={handleNuke}
>
<Button danger disabled={!active}>
Abort Session
</Button>
</Popconfirm>
<Button
key="finish"
ghost
type="primary"
disabled={!active}
onClick={handleExit}
>
Finish Session
</Button>
<StatusChip poll={true} />
</Row>
<Divider />
<Row className="controls">
{active && <SessionPictures clientId={clientId} />}