Verified Commit 0bb64aff authored by insert's avatar insert

Add functionality to done button.

parent 7e7deeff
......@@ -22,8 +22,8 @@ export async function fetchAssignments(): Promise<Assignment[]> {
}
export interface PartialAssignment {
id?: string,
nonce: string,
id: string,
nonce?: string,
subject?: string,
type?: AssignmentType,
......
......@@ -4,6 +4,8 @@ import { ListItem, ListItemIcon, ListItemText, Typography, Card, CardActions, Bu
import DoneIcon from '@material-ui/icons/Done';
import EditIcon from '@material-ui/icons/Edit';
import DeleteIcon from '@material-ui/icons/Delete';
import DoneOutlineIcon from '@material-ui/icons/DoneOutline';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
import { SubjectIcon } from '../util/Icon';
import moment from 'moment';
......@@ -13,15 +15,16 @@ export function validDate(d: any): boolean {
return moment(d).isValid();
}
export default memo((props: Assignment & { active: boolean, select: (id: string) => boolean, edit: (id: string) => void, delete: (task: Assignment) => void }) => {
const due = props.dueDate || undefined;
export default memo((props: { task: Assignment, active: boolean, select: (id: string) => boolean, toggle: (task: Assignment) => void, edit: (id: string) => void, delete: (task: Assignment) => void }) => {
const task = props.task,
due = task.dueDate || undefined;
return (
<Fragment>
<ListItem button
onClick={() => props.select(props.id)}>
onClick={() => props.select(task.id)}>
<ListItemIcon>
<SubjectIcon subject={props.subject} />
<SubjectIcon subject={task.subject} />
</ListItemIcon>
<ListItemText
primary={
......@@ -29,10 +32,10 @@ export default memo((props: Assignment & { active: boolean, select: (id: string)
<Typography
variant="overline"
style={{ lineHeight: 0, fontSize: '0.6em' }}>
{props.subject}
{task.subject}
{
props.class &&
` — ${props.class}`
task.class &&
` — ${task.class}`
}
{
validDate(due) &&
......@@ -40,12 +43,18 @@ export default memo((props: Assignment & { active: boolean, select: (id: string)
}
</Typography>
<br />
{props.title}
{ task.complete &&
<DoneOutlineIcon
fontSize="inherit"
style={{ marginRight: 6, color: 'green' }}
/>
}
{task.title}
</Fragment>
}
secondary={
<Fragment>
{props.description}
{task.description}
</Fragment>
}
/>
......@@ -55,20 +64,21 @@ export default memo((props: Assignment & { active: boolean, select: (id: string)
<Card style={{ boxShadow: 'none' }}>
<CardActions>
<Button
startIcon={<DoneIcon />}
size="small">
done
startIcon={task.complete ? <ArrowForwardIcon/> : <DoneIcon />}
size="small"
onClick={() => props.toggle(task)}>
{ task.complete ? 'start' : 'done' }
</Button>
<Button
startIcon={<EditIcon />}
size="small"
onClick={() => props.edit(props.id)}>
onClick={() => props.edit(task.id)}>
edit
</Button>
<Button
startIcon={<DeleteIcon />}
size="small"
onClick={() => props.delete(props)}>
onClick={() => props.delete(task)}>
delete
</Button>
</CardActions>
......
import React, { useContext, useState, Fragment } from 'react';
import { Container, Typography, List, ListItem, ListItemText, ListItemIcon, Card, CardActions, Button, Tooltip, Dialog, AppBar, Toolbar, IconButton, Slide, TextField, DialogContent, DialogActions, Snackbar, DialogTitle, Checkbox, FormControlLabel } from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { TransitionProps } from '@material-ui/core/transitions/transition';
import { KeyboardDatePicker } from '@material-ui/pickers';
import { useStyles, UserContext } from './Loader';
import AddIcon from '@material-ui/icons/Add';
import DoneIcon from '@material-ui/icons/Done';
import EditIcon from '@material-ui/icons/Edit';
import CloseIcon from '@material-ui/icons/Close';
import DeleteIcon from '@material-ui/icons/Delete';
import { SubjectIcon } from '../util/Icon';
import moment from 'moment';
import Axios from 'axios';
const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
function sort(source: any[]) {
const sorted = source.filter(x => x.dueDate);
sorted.sort((a: any, b: any) => +new Date(a.dueDate) - +new Date(b.dueDate));
return [
...sorted,
...source.filter(x => !x.dueDate)
];
}
export default () => {
let classes = useStyles(),
userInfo = useContext(UserContext);
let [ active, setActive ] = useState(-1);
let [ open, setOpen ] = useState(false);
let [ confirmDialog, setConfirmDialog ] = useState(false);
let [ assignments, setAssignments ] = useState<any[]>([]);
let [ loading, setLoading ] = useState(0);
let [ error, setError ] = useState('');
if (loading === 0) {
setLoading(1);
Axios.get('/api/homework/list')
.then(x => {
setActive(-1);
setAssignments(sort(x.data));
setLoading(2);
})
.catch(err => setError(`Failed to load list! ${err}`));
}
const [ useDueDate, setUseDueDate ] = useState(false),
[ editing, setEditing ] = useState(-1),
[ selectedDate, setSelectedDate ] = React.useState<Date | null>(null);
const [ dialogData, setData ] = useState({ title: '', subject: '', description: '', class: '' });
const loadX = (x: any) => {
setData({ title: '', subject: '', description: '', class: '', ...x });
setSelectedDate(x.dueDate ? new Date(x.dueDate) : null);
setUseDueDate(!!x.dueDate);
};
const handleClose = () => {
loadX({ });
setOpen(false);
setEditing(-1);
};
const handleDateChange = (date: Date | null) => {
setSelectedDate(date);
setUseDueDate(true);
};
return (
<Fragment>
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Hello, {userInfo.givenName}!
</Typography>
<Typography variant="overline">
your assignments
<Tooltip title="Add a new assignment." aria-label="add a new assignment">
<AddIcon
style={{ marginLeft: 8, position: 'relative', top: 1 }}
fontSize="inherit"
onClick={() => setOpen(true)}
/>
</Tooltip>
</Typography>
<List>
{
loading < 2 ?
<Typography variant="overline">
loading
</Typography>
:
assignments.map((x, i) =>
<Fragment>
<ListItem button
onClick={() => setActive(active === i ? -1 : i)}>
<ListItemIcon>
<SubjectIcon subject={x.subject} />
</ListItemIcon>
<ListItemText
primary={
<Fragment>
<Typography
variant="overline"
style={{ lineHeight: 0, fontSize: '0.6em' }}>
{x.subject}
{
x.class &&
` — ${x.class}`
}
{
moment(x.dueDate).isValid() &&
` · due ${moment(x.dueDate).fromNow()}`
}
</Typography>
<br />
{x.title}
</Fragment>
}
secondary={
<Fragment>
{x.description}
</Fragment>
}
/>
</ListItem>
{
active === i &&
<Card style={{ boxShadow: 'none' }}>
<CardActions>
<Button
startIcon={<DoneIcon />}
size="small">
done
</Button>
<Button
startIcon={<EditIcon />}
size="small"
onClick={
() => {
setEditing(i);
setOpen(true);
loadX(x);
}
}>
edit
</Button>
<Button
startIcon={<DeleteIcon />}
size="small"
onClick={() => setConfirmDialog(true)}>
delete
</Button>
</CardActions>
</Card>
}
</Fragment>
)
}
</List>
</div>
</Container>
<Dialog
open={open}
onClose={handleClose}
TransitionComponent={Transition}
fullWidth>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton edge="start" color="inherit" onClick={handleClose} aria-label="close">
<CloseIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
Edit an assignment
</Typography>
</Toolbar>
</AppBar>
<DialogContent>
<Container fixed>
<Autocomplete
freeSolo
id="subject"
value={dialogData.subject}
onChange={(e, subject) => setData({ ...dialogData, subject })}
options={[ 'english', 'mathematics', 'biology', 'physics', 'chemistry', 'computer science', 'history', 'geography', 'economics', 'psychology', 'religious studies', 'art', 'music', 'drama', 'textiles', 'food tech', 'product design' ]}
renderInput={params =>
<TextField {...params}
label="Subject"
margin="normal"
required
fullWidth
onChange={e => setData({ ...dialogData, subject: e.currentTarget.value })}
/>
}
/>
<TextField
margin="normal"
required
id="title"
label="Title"
name="title"
autoComplete="title"
autoFocus
fullWidth
value={dialogData.title}
onChange={e => setData({ ...dialogData, title: e.currentTarget.value })}
/>
<TextField
margin="normal"
id="description"
label="Description"
name="description"
autoComplete="description"
fullWidth
value={dialogData.description}
onChange={e => setData({ ...dialogData, description: e.currentTarget.value })}
/>
<TextField
margin="normal"
id="class"
label="Class or teacher"
name="class"
autoComplete="class"
fullWidth
value={dialogData.class}
onChange={e => setData({ ...dialogData, class: e.currentTarget.value })}
/>
<FormControlLabel
control={
<Checkbox
checked={useDueDate}
onChange={v => setUseDueDate(v.currentTarget.checked)}
/>
}
label="Include due date"
/>
<KeyboardDatePicker
margin="normal"
id="due-date"
label="Due date"
format="dd/MM/yyyy"
value={selectedDate}
onChange={handleDateChange}
fullWidth
/>
</Container>
</DialogContent>
<DialogActions>
<Button onClick={
() => {
const { title, subject, description, class: classFor } = dialogData;
handleClose();
setActive(-1);
Axios
.post('/api/homework/update', {
id: editing !== -1 ? (assignments[editing].id) : undefined,
title,
subject,
description,
class: classFor,
dueDate: useDueDate && selectedDate,
nonce: Math.random()
})
.then(() => setLoading(0))
.catch(err => setError(`Failed to update! ${err}`));
}
} color="primary">
Save
</Button>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
</DialogActions>
</Dialog>
<Dialog
open={confirmDialog}
onClose={() => setConfirmDialog(false)}
>
<DialogTitle>Delete assignment?</DialogTitle>
<DialogActions>
<Button onClick={() => setConfirmDialog(false)} color="primary">
Cancel
</Button>
<Button onClick={
() => {
setConfirmDialog(false);
Axios
.post('/api/homework/delete', { id: assignments[active].id })
.then(() => setLoading(0))
.catch(err => setError(`Failed to delete! ${err}`));
}
} color="primary" autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
<Snackbar
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
open={!!error}
onClose={() => setError('')}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{error}</span>}
/>
</Fragment>
);
};
\ No newline at end of file
import React, { useState, Fragment, createContext } from 'react';
import Login from '../pages/Login';
import { Typography, CssBaseline, Container, makeStyles } from '@material-ui/core';
import { MuiPickersUtilsProvider } from '@material-ui/pickers';
import DateFnsUtils from '@date-io/date-fns';
import { fetchUserInfo, UserInfo } from '../api/info';
import App from './App';
export const UserContext = createContext<UserInfo>({} as any);
const Loader = () => {
let [ userInfo, setUserInfo ] = useState<UserInfo>({} as any);
let [ status, setStatus ] = useState(0);
if (status === 0) {
setStatus(1);
fetchUserInfo()
.then(res => {
setUserInfo(res);
setStatus(3);
})
.catch(err => setStatus(2));
}
if (status < 2) {
return (
);
}
if (status < 3) {
return (
<Login />
);
}
return (
<UserContext.Provider value={userInfo}>
<App />
</UserContext.Provider>
);
}
export default () => {
return (
<Fragment>
<CssBaseline />
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Loader />
</MuiPickersUtilsProvider>
</Fragment>
);
}
......@@ -36,7 +36,8 @@ function sort(source: any[]) {
return [
...sorted,
...source.filter(x => !validDate(x.dueDate))
];
]
.filter(x => (validDate(x.dueDate) && x.complete && new Date(x.dueDate) < new Date()) ? false : true);
}
export default useStyles(class extends Component<AppState & { classes: any }, HomeState> {
......@@ -53,6 +54,7 @@ export default useStyles(class extends Component<AppState & { classes: any }, Ho
}
this.selectTask = this.selectTask.bind(this);
this.toggleTask = this.toggleTask.bind(this);
this.editTask = this.editTask.bind(this);
this.deleteTask = this.deleteTask.bind(this);
this.sync = this.sync.bind(this);
......@@ -127,6 +129,13 @@ export default useStyles(class extends Component<AppState & { classes: any }, Ho
return newState === id;
}
toggleTask(task: Assignment) {
let db = this.props.database;
if (db) {
db.update(Object.assign({}, task, { complete: !task.complete }));
}
}
editTask(id: string) {
this.setState({
editing: true
......@@ -173,9 +182,10 @@ export default useStyles(class extends Component<AppState & { classes: any }, Ho
key={x.id}
active={this.state.selected === x.id}
select={this.selectTask}
toggle={this.toggleTask}
edit={this.editTask}
delete={this.deleteTask}
{...x}
task={x}
/>
) }
</List>
......
......@@ -174,7 +174,7 @@ export default memo((props: DialogProps) => {
if (database) {
database.update({
id: 'local_' + ulid(),
id: props.selected ? props.selected : 'local_' + ulid(),
nonce: ulid(),
updatedAt: new Date().toUTCString(),
type: 0,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment