Skip to content

Commit

Permalink
Merge pull request #33 from weaponsforge/dev
Browse files Browse the repository at this point in the history
v1.0.3
  • Loading branch information
weaponsforge authored Aug 25, 2022
2 parents 3e9f8d5 + e9d602d commit e2822f2
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 43 deletions.
1 change: 1 addition & 0 deletions client/components/layout/apploading/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { styled } from '@mui/material/styles'
const FullSizeBox = styled(Box)(() => ({
display: 'grid',
width: '100%',
minHeight: '400px',
height: '100%',
placeContent: 'center',
border: '1px solid rgba(224, 224, 224, 1)',
Expand Down
102 changes: 102 additions & 0 deletions client/components/todo/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import Link from 'next/link'
import { useSelector } from 'react-redux'

// MUI
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import ButtonGroup from '@mui/material/ButtonGroup'
import CardActions from '@mui/material/CardActions'
import TextField from '@mui/material/TextField'
import Typography from '@mui/material/Typography'
import styles from './styles'

// Layout
import AppContainer from '@/components/layout/appcontainer'
import AppCard from '@/layout/appcard'
import SimpleContainer from '@/layout/simplecontainer'

function CreateTodo ({
state,
inputStatus,
onTextChange,
onTextClick,
onSaveClick,
onResetClick,
onCancelClick
}) {
const { loading, error } = useSelector((state) => state.todos)

return (
<AppContainer maxWidth='sm'>
<AppCard>
<h1>Create a Todo</h1>

<Box sx={styles.inputContainer}>
<TextField
id='title'
label='Title'
placeholder='Enter a title'
variant='outlined'
disabled={loading === 'pending'}
value={state.title}
onChange={onTextChange}
onClick={onTextClick}
/>

<TextField
id='description'
label='Description'
placeholder='Enter a description'
variant='outlined'
disabled={loading === 'pending'}
value={state.description}
onChange={onTextChange}
onClick={onTextClick}
/>

<TextField
id='content'
label='Content'
placeholder='Enter content'
variant='outlined'
disabled={loading === 'pending'}
value={state.content}
onChange={onTextChange}
onClick={onTextClick}
multiline
rows={9}
/>
</Box>

<Box sx={{ textAlign: 'left', color: 'red' }}>
<Typography variant='caption'>
{error || inputStatus || <br />}
</Typography>
</Box>

<CardActions sx={styles.buttons}>
<ButtonGroup
variant='outlined'
disabled={loading === 'pending'}
>
<Button onClick={onCancelClick}>Cancel</Button>
<Button onClick={onResetClick}>Clear</Button>
<Button
variant='contained'
onClick={onSaveClick}
>
Save
</Button>
</ButtonGroup>
</CardActions>
</AppCard>

<SimpleContainer>
<Link href='/'>Home</Link>&nbsp; | &nbsp;
<Link href='/todo'>Todos</Link>
</SimpleContainer>
</AppContainer>
)
}

export default CreateTodo
89 changes: 54 additions & 35 deletions client/components/todo/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useSelector } from 'react-redux'
import Link from 'next/link'
import { useRouter } from 'next/router'
import Link from 'next/link'

// MUI
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography'
import { DataGrid } from '@mui/x-data-grid'

Expand All @@ -28,42 +29,60 @@ function Todo () {
<AppCard>
<h1>Todo List</h1>

<Box sx={{ height: 400, width: '100%' }}>
<DataGrid
rows={Object.values(todos.entities).map(x => ({
...x,
createdAt: new Date(x.createdAt).toDateString(),
updatedAt: new Date(x.updatedAt).toDateString()
}))}
columns={columns}
getRowId={(row) => row._id}
pageSize={5}
rowsPerPageOptions={[5]}
checkboxSelection
disableSelectionOnClick
loading={todos.loading === 'pending'}
rowHeight={48}
initialState={{
columns: {
columnVisibilityModel: { _id: false }
}
<Box sx={{ height: '100%', width: '100%', textAlign: 'left' }}>
<Button
variant='outlined'
disableElevation
onClick={() => {
router.push('/todo/create')
}}
components={{
NoRowsOverlay: () => (
<Box
sx={{ display: 'grid', width: '100%', height: '100%', placeContent: 'center' }}
style={{ color: (todos.error !== '') ? 'red' : '#000' }}>
{todos.error !== ''
? <Typography variant='caption'>{todos.error}</Typography>
: <Typography variant='caption'>No rows available</Typography>
}
</Box>
)
sx={{
marginBottom: (theme) => theme.spacing(2)
}}
onRowClick={({ id }) => {
router.push(`/todo/${id}`)
}}
/>
>
Create Todo
</Button>

<Box sx={{ height: 450, width: '100%', textAlign: 'left' }}>
<DataGrid
rows={Object.values(todos.entities).map(x => ({
...x,
createdAt: new Date(x.createdAt).toDateString(),
updatedAt: new Date(x.updatedAt).toDateString()
}))}
columns={columns}
getRowId={(row) => row._id}
// autoHeight={rows.length >= 3}
disableDensitySelector
// pagination
pageSize={7}
rowsPerPageOptions={[3, 10, 20]}
checkboxSelection
disableSelectionOnClick
loading={todos.loading === 'pending'}
rowHeight={48}
initialState={{
columns: {
columnVisibilityModel: { _id: false }
}
}}
components={{
NoRowsOverlay: () => (
<Box
sx={{ display: 'grid', width: '100%', height: '100%', placeContent: 'center' }}
style={{ color: (todos.error !== '') ? 'red' : '#000' }}>
{todos.error !== ''
? <Typography variant='caption'>{todos.error}</Typography>
: <Typography variant='caption'>No rows available</Typography>
}
</Box>
)
}}
onRowClick={({ id }) => {
router.push(`/todo/${id}`)
}}
/>
</Box>
</Box>
</AppCard>

Expand Down
19 changes: 19 additions & 0 deletions client/components/todo/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const styles = {
inputContainer: {
minHeight: 400,
width: '100%',
textAlign: 'left',
'& .MuiTextField-root': {
width: '100%',
marginBottom: '16px !important'
}
},
buttons: {
float: 'right',
'& button': {
width: '80px'
}
}
}

export default styles
77 changes: 77 additions & 0 deletions client/pages/todo/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { unwrapResult } from '@reduxjs/toolkit'
import { useRouter } from 'next/router'
import { createNewTodo, todosReset } from '@/store/todo/todoSlice'

import CreateTodo from '@/components/todo/create'

const defaultState = {
title: '', description: '', content: ''
}

function CreateTodoContainer () {
const [state, setState] = useState(defaultState)
const [inputStatus, setStatus] = useState('')
const dispatch = useDispatch()
const router = useRouter()
const error = useSelector((states) => states.todos.error)

const resetErrorsMessages = () => {
if (error !== '') {
dispatch(todosReset())
}

if (inputStatus !== '') {
setStatus('')
}
}

const handleInputChange = (e) => {
const { id, value } = e.target
setState({ ...state, [id]: value })
resetErrorsMessages()
}

const handleSave = () => {
if (!Object.values(state).every(field => field !== '')) {
setStatus('Please check your input.')
return
}

dispatch(createNewTodo(state))
.then(unwrapResult)
.then((todo) => {
router.push(`/todo/${todo._id}`)
})
}

const handleCancel = () => {
router.push('/todo')
}

const handleReset = () => {
resetErrorsMessages()
setState(defaultState)
}

const handleInputClick = (e) => {
const { id } = e.target
setState({ ...state, [id]: '' })
resetErrorsMessages()
}

return (
<CreateTodo
state={state}
inputStatus={inputStatus}
onTextChange={handleInputChange}
onTextClick={handleInputClick}
onSaveClick={handleSave}
onResetClick={handleReset}
onCancelClick={handleCancel}
/>
)
}

export default CreateTodoContainer
9 changes: 4 additions & 5 deletions client/pages/todo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ import { useEffect, useRef } from 'react'
import Todo from '@/components/todo'

// Redux
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import { fetchTodos, fetchTodo } from '@/store/todo/todoSlice'

function TodoContainer () {
const dispatch = useDispatch()
const ids = useSelector((state) => state.todos.ids)
const mounted = useRef(null)

useEffect(() => {
// Load the Todo list only once (initial load)
if (mounted.current === null && ids.length === 0) {
// Reload Todo list
if (mounted.current === null) {
mounted.current = true
dispatch(fetchTodos())
}
}, [dispatch, ids.length])
}, [dispatch])

const handleGetTodo = (id) => {
dispatch(fetchTodo(id))
Expand Down
4 changes: 3 additions & 1 deletion client/src/services/todo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ const TODO = new Todo()

const listTodos = TODO.listTodos.bind(TODO)
const getTodo = TODO.getTodo.bind(TODO)
const createTodo = TODO.createTodo.bind(TODO)

export {
listTodos,
getTodo
getTodo,
createTodo
}
10 changes: 10 additions & 0 deletions client/src/services/todo/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ class Todo {
const result = await axios.get(`${this.BASE_API_URL}/todo/${id}`)
return result.data
}

async createTodo (todo) {
const result = await axios({
data: todo,
url: `${this.BASE_API_URL}/todo`,
method: 'POST'
})

return result.data
}
}

export default Todo
Loading

0 comments on commit e2822f2

Please sign in to comment.