-
Notifications
You must be signed in to change notification settings - Fork 419
MellowList #441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
MellowList #441
Changes from all commits
c005192
24d48d9
10f32c9
df62836
e952492
29b13a2
5e51fb2
113209c
82ca35f
0f44657
95bcb47
a5e8ca5
ebca4af
97a254b
39874b0
ed01e06
1697c0b
a372e08
93eda20
7ee9cd2
47d886f
12d74b4
d0772ad
948c342
202da28
e0cef83
89f833b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,51 @@ | ||
| import React from 'react'; | ||
| // src/App.js | ||
| import React from 'react' | ||
| import { Provider } from 'react-redux' | ||
| import { combineReducers, configureStore } from '@reduxjs/toolkit' | ||
| import { projects } from 'reducers/projects' | ||
| import styled from 'styled-components'; | ||
| import AddTask from './components/AddTask' | ||
| import { tasks } from './reducers/tasks' | ||
| import TaskList from './components/TaskList' | ||
| import AddProject from './components/AddProject' | ||
| import ProjectList from './components/ProjectList' | ||
| import TaskCounter from './components/TaskCounter' | ||
| import CompleteAllButton from './components/CompleteAllButton' | ||
| import Playlist from './components/Playlist'; | ||
| import { playlist } from './reducers/playlist'; | ||
|
|
||
| const reducer = combineReducers({ | ||
| tasks: tasks.reducer, | ||
| projects: projects.reducer, | ||
| playlist: playlist.reducer | ||
| }) | ||
|
|
||
| const store = configureStore({ reducer }) | ||
|
|
||
| const AppWrapper = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: 5px 20px; | ||
| gap:1rem; | ||
| background-color: #0f1029; | ||
| `; | ||
|
|
||
| export const App = () => { | ||
| return ( | ||
| <div> | ||
| Find me in src/app.js! | ||
| </div> | ||
| ); | ||
| <Provider store={store}> | ||
| <Playlist /> | ||
| <AppWrapper> | ||
| <h1>MellowList</h1> | ||
| <img src={`${process.env.PUBLIC_URL}/lofigirl.gif`} alt="lofi girl gif" /> | ||
| <AddProject /> | ||
| <ProjectList /> | ||
| <AddTask /> | ||
| <TaskList /> | ||
| <CompleteAllButton /> | ||
| <TaskCounter /> | ||
| </AppWrapper> | ||
| </Provider> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import React, { useState } from 'react'; | ||
| import { useDispatch } from 'react-redux'; | ||
| import styled from 'styled-components'; | ||
| import { addProject } from '../reducers/projects'; | ||
|
|
||
| const Form = styled.form` | ||
| display: flex; | ||
| align-items: center; | ||
| `; | ||
|
|
||
| const Input = styled.input` | ||
| padding: 0.5em; | ||
| border: 1px solid #ccc; | ||
| border-radius: 5px; | ||
| margin-right: 1em; | ||
| color:#0f1029; | ||
| width:250px; | ||
| font-family: 'Montserrat', sans-serif; | ||
| font-weight:400; | ||
| font-size: 16px; | ||
| `; | ||
|
|
||
| const Button = styled.button` | ||
| background-color: #b8b4ba; | ||
| color: white; | ||
| padding: 0.5em 1em; | ||
| border: none; | ||
| border-radius: 5px; | ||
| cursor: pointer; | ||
| font-family: 'Montserrat', sans-serif; | ||
| font-weight:400; | ||
| font-size: 16px; | ||
|
|
||
| &:hover { | ||
| background-color:#586535 | ||
| ; | ||
| } | ||
| `; | ||
|
|
||
| const AddProject = () => { | ||
| const [input, setInput] = useState('') | ||
| const dispatch = useDispatch() | ||
|
|
||
| const handleSubmit = (e) => { | ||
| e.preventDefault() | ||
| if (input.trim()) { | ||
| dispatch(addProject({ name: input.trim() })) | ||
| setInput('') | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <Form onSubmit={handleSubmit}> | ||
| <Input | ||
| type="text" | ||
| value={input} | ||
| onChange={(e) => setInput(e.target.value)} | ||
| placeholder="Add a new project" /> | ||
| <Button type="submit">Add</Button> | ||
| </Form> | ||
|
|
||
| ) | ||
| } | ||
|
|
||
| export default AddProject |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| /* eslint-disable no-case-declarations */ | ||
| /* eslint-disable no-use-before-define */ | ||
| // src/components/AddTask.js | ||
| import React, { useState } from 'react' | ||
| import { useDispatch, useSelector } from 'react-redux' | ||
| import styled from 'styled-components'; | ||
| import { addTask } from '../reducers/tasks' | ||
|
|
||
| // eslint-disable-next-line no-lone-blocks | ||
| { /* const capitalize = (stringToCapitalise) => { | ||
| return stringToCapitalise.charAt(0).toUpperCase() + stringToCapitalise.slice(1); | ||
| } */ } | ||
|
|
||
| const Form = styled.form` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 1em; | ||
| `; | ||
|
|
||
| const Select = styled.select` | ||
| padding: 0.5em; | ||
| border: 1px solid #ccc; | ||
| border-radius: 5px; | ||
| font-family: 'Montserrat', sans-serif; | ||
| font-weight:400; | ||
| font-size: 16px; | ||
| color: #878894; | ||
|
|
||
| `; | ||
|
|
||
| const Input = styled.input` | ||
| padding: 0.5em; | ||
| border: 1px solid #ccc; | ||
| border-radius: 5px; | ||
| width: 300px; | ||
| color: #2d2e38; | ||
| color: #a0a0b4; | ||
| font-family: 'Montserrat', sans-serif; | ||
| font-weight:400; | ||
| font-size: 16px; | ||
| `; | ||
|
|
||
| const Button = styled.button` | ||
|
|
||
| padding: 0.5em 1em; | ||
| border: none; | ||
| border-radius: 8px; | ||
| cursor: pointer; | ||
| background-color: #b8b4ba; | ||
| color:white; | ||
| font-size:16px; | ||
|
|
||
|
|
||
| &:hover { | ||
| background-color: #4d5d3e; | ||
| } | ||
| `; | ||
|
|
||
| const AddTask = () => { | ||
| const [input, setInput] = useState('') | ||
| const [selectedProject, setSelectedProject] = useState('') | ||
| const [selectedDate, setSelectedDate] = useState('today') | ||
| const [selectedDueDate, setSelectedDueDate] = useState('') | ||
| const dispatch = useDispatch() | ||
| const projectList = useSelector((state) => state.projects) | ||
|
|
||
| const handleSubmit = (e) => { | ||
| e.preventDefault() | ||
| if (input.trim() && selectedProject) { | ||
| const dueDate = getDueDate(selectedDate) | ||
| dispatch(addTask({ | ||
| text: input.trim(), | ||
| complete: false, | ||
| projectId: parseInt(selectedProject, 10), | ||
| dueDate | ||
| })) | ||
| setInput('') | ||
| setSelectedProject('') | ||
| setSelectedDate('today') | ||
| setSelectedDueDate('') | ||
| } | ||
| } | ||
|
|
||
| const getDueDate = (date) => { | ||
| switch (date) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice use case of switch / case |
||
| case 'tomorrow': | ||
| const tomorrow = new Date() | ||
| tomorrow.setDate(tomorrow.getDate() + 1) | ||
| return tomorrow.toISOString() | ||
| case 'later': | ||
| const later = new Date() | ||
| later.setDate(later.getDate() + 7) | ||
| return later.toISOString() | ||
| default: | ||
| return new Date().toISOString() | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <Form onSubmit={handleSubmit}> | ||
| <Select | ||
| value={selectedProject} | ||
| onChange={(e) => setSelectedProject(e.target.value)}> | ||
| <option value="">Select a project</option> | ||
| {projectList.map((project) => ( | ||
| <option key={project.id} value={project.id}> | ||
| {project.name} | ||
| </option> | ||
| ))} | ||
| </Select> | ||
| <Input | ||
| type="text" | ||
| value={input} | ||
| onChange={(e) => setInput(e.target.value)} | ||
| placeholder="Add a new task" /> | ||
| <Select | ||
| value={selectedDate} | ||
| onChange={(e) => setSelectedDate(e.target.value)}> | ||
| <option value="today">Today</option> | ||
| <option value="tomorrow">Tomorrow</option> | ||
| <option value="later">Later this week</option> | ||
| </Select> | ||
| {selectedDate === 'later' && ( | ||
| <div> | ||
| <input | ||
| type="date" | ||
| value={selectedDueDate} | ||
| onChange={(e) => setSelectedDueDate(e.target.value)} /> | ||
| <Button type="button" onClick={() => setSelectedDate('custom')}>Cancel</Button> | ||
| </div> | ||
| )} | ||
| {selectedDate === 'custom' && ( | ||
| <div> | ||
| <input | ||
| type="date" | ||
| value={selectedDueDate} | ||
| onChange={(e) => setSelectedDueDate(e.target.value)} /> | ||
| </div> | ||
| )} | ||
| <Button type="submit">Add</Button> | ||
| </Form> | ||
| ) | ||
| } | ||
|
|
||
| export default AddTask | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import React from 'react'; | ||
| import { useDispatch } from 'react-redux'; | ||
| import styled from 'styled-components'; | ||
| import { completeAll } from '../reducers/tasks'; | ||
|
|
||
| const Button = styled.button` | ||
| background-color: #878894; | ||
| padding: 0.5em 1em; | ||
| border: none; | ||
| border-radius: 10px; | ||
| cursor: pointer; | ||
| width:200px; | ||
| color:white; | ||
| font-family: 'Montserrat', sans-serif; | ||
| font-weight:400; | ||
| font-size: 16px; | ||
| margin-bottom: 20px; | ||
|
|
||
|
|
||
| &:hover { | ||
| background-color:#586535 ; | ||
| } | ||
| `; | ||
|
|
||
| const CompleteAllButton = () => { | ||
| const dispatch = useDispatch(); | ||
|
|
||
| const handleClick = () => { | ||
| dispatch(completeAll()); | ||
| }; | ||
|
|
||
| return ( | ||
| <Button type="button" onClick={handleClick}> | ||
| Complete All | ||
| </Button> | ||
| ); | ||
| }; | ||
|
|
||
| export default CompleteAllButton; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import React from 'react'; | ||
| import { useSelector } from 'react-redux'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| const PlaylistContainer = styled.div` | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| background-color: #301f48; | ||
| padding: 10px; | ||
| color: white; | ||
|
|
||
| `; | ||
|
|
||
| const PlaylistLink = styled.a` | ||
| color: white; | ||
| text-decoration: underline; | ||
| margin-left: 10px; | ||
| `; | ||
|
|
||
| const Playlist = () => { | ||
| const { name, link } = useSelector((state) => state.playlist); | ||
|
|
||
| return ( | ||
| <PlaylistContainer> | ||
| <span>{name}</span> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might want to add css rule to display: none on smaller screen-sizes for a more clean look |
||
| <PlaylistLink href={link} target="_blank" rel="noopener noreferrer"> | ||
| Get in the zone and conquer your to-do list. | ||
| </PlaylistLink> | ||
| </PlaylistContainer> | ||
| ); | ||
| }; | ||
|
|
||
| export default Playlist; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great that you added meta data :D