Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Project Todos

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?


## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://reliable-torte-e7250c.netlify.app/
551 changes: 532 additions & 19 deletions code/package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@
"private": true,
"dependencies": {
"@babel/eslint-parser": "^7.18.9",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@reduxjs/toolkit": "^1.9.4",
"eslint": "^8.21.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"styled-components": "^5.3.9",
"uniqid": "^5.4.0"
},
"scripts": {
"start": "react-scripts start",
Expand Down
8 changes: 6 additions & 2 deletions code/public/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Expand All @@ -13,6 +12,12 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Raleway&display=swap"
rel="stylesheet"
/>
<title>Technigo React App</title>
</head>

Expand All @@ -30,5 +35,4 @@
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>

</html>
28 changes: 25 additions & 3 deletions code/src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import React from 'react';
import { Provider } from 'react-redux'
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import styled from 'styled-components/macro'
import { tasks } from './reducers/tasks'

import Todos from './components/Todos'

const StyledContainer = styled.div`
background-color: var(--background-color);
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 100vh;`

const reducer = combineReducers({
tasks: tasks.reducer
})

const store = configureStore({ reducer })

export const App = () => {
return (
<div>
Find me in src/app.js!
</div>
<Provider store={store}>
<StyledContainer>
<Todos />
</StyledContainer>
</Provider>
);
}
19 changes: 19 additions & 0 deletions code/src/components/CheckBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'
import { useDispatch } from 'react-redux';
import { tasks } from 'reducers/tasks'
import styled from 'styled-components/macro'

const StyledCheckbox = styled.input``

const CheckBox = ({ id }) => {
const dispatch = useDispatch();
const checkComplete = () => {
dispatch(tasks.actions.completeTask(id))
}
return (

<StyledCheckbox type="checkbox" text="completed?" onClick={checkComplete} />
);
}

export default CheckBox;
24 changes: 24 additions & 0 deletions code/src/components/DeleteButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import { useDispatch } from 'react-redux';
import { tasks } from 'reducers/tasks'
import styled from 'styled-components/macro'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faX } from '@fortawesome/free-solid-svg-icons'

const StyledButton = styled.button`
background-color: inherit;
border: none;
transition: color 0.2s ease-out;
cursor: pointer;

`
const DeleteButton = ({ id, completed }) => {
const dispatch = useDispatch();

const handleDelete = () => {
dispatch(tasks.actions.deleteTask(id))
}
return (<StyledButton onClick={handleDelete}><FontAwesomeIcon icon={faX} style={{ color: `var(${completed ? '--background-color' : '--foreground-primary-color'})` }} /></StyledButton>);
}

export default DeleteButton;
55 changes: 55 additions & 0 deletions code/src/components/Task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import styled from 'styled-components/macro'
import DeleteButton from './DeleteButton'
import { tasks } from '../reducers/tasks'

const StyledContainer = styled.div`
display: flex;
padding: 5px;
flex-direction: row;
align-items: center;
justify-content: space-around;

`

const StyledText = styled.p`
font-weight: 400;
font-size: 15px;
color: var(--foreground-primary-color);
flex: 1;
text-align: center;
transition: color 0.1s ease-out;
cursor: pointer;



&.completed {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat way of adding some conditional styling. Didn't know you could target classnames in styled components like this, but it makes sense once you see it.

color: var(--background-color);;
}

`

const Task = ({ id }) => {
const dispatch = useDispatch();
const checkComplete = () => {
dispatch(tasks.actions.completeTask(id))
}
const task = useSelector((state) => state.tasks.todos.find((t) => t.id === id))
return (
<StyledContainer>

<StyledText
className={
task.complete
&& 'completed'
}
onClick={checkComplete}>
{task.text}
</StyledText>
<DeleteButton id={task.id} completed={task.complete} />
</StyledContainer>
);
}

export default Task;
47 changes: 47 additions & 0 deletions code/src/components/TaskAdder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { tasks } from 'reducers/tasks'
import styled from 'styled-components/macro'
import uniqid from 'uniqid'

const StyledContainer = styled.div`
padding: 5px;
display: flex;
flex-direction: row;
background-color: var(--foreground-primary-color);
`

const StyledInput = styled.input`
border: none;
height: 20px;
font-family: "Raleway";
`

const StyledButton = styled.button`
background-color: var(--foreground-primary-color);
font-weight: 400;
color: white;
border: none;
font-family: "Raleway";
font-size: 15px;
cursor: pointer;

`

const TaskAdder = () => {
const [todoText, setTodoText] = useState('');
const dispatch = useDispatch();

const handleSubmit = () => {
dispatch(tasks.actions.addTask({ id: uniqid(), text: todoText, complete: false }))
}

return (
<StyledContainer>
<StyledInput value={todoText} onChange={(e) => setTodoText(e.target.value)} />
<StyledButton onClick={handleSubmit}>task</StyledButton>
</StyledContainer>
);
}

export default TaskAdder;
116 changes: 116 additions & 0 deletions code/src/components/TaskList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useState } from 'react'
import styled from 'styled-components/macro'
import { useSelector, useDispatch } from 'react-redux'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { tasks } from '../reducers/tasks'
import Task from './Task'

const StyledContainer = styled.div`
display: flex;
flex-direction: column;

width: 100%;`

const StyledTasklistBar = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
padding: 5px;
align-items: center;
background-color: var(--foreground-primary-color);

h1 {
font-size: 15px;
flex: 1;
color: white;
text-align: center;
}

`

const StyledActionBar = styled.div`
display: none;
height: 0px;
opacity: 0;
transition: opacity 1s ease-out;
overflow: hidden;
background-color: var(--foreground-primary-color);



&.active {
opacity: 1;
height: auto;
display: flex;
flex-direction: row;
justify-content: center;
gap: 10px;
padding: 2px;
}
`

const HamburgerMenuButton = styled.button`
width: 30px;
height: 30px;
background-color: inherit;
border: none;
cursor: pointer;
`

const CompleteAllButton = styled.button`
font-weight: 400;
color: white;
border: none;
font-family: "Raleway";
font-size: 12px;
width: 50%;
padding: 5px;
background-color: inherit;
cursor: pointer;

`

const DeleteAllButton = styled(CompleteAllButton)`
text-decoration: underline;
text-decoration-color: red;
text-underline-offset: 4px;
`

const TaskList = () => {
const [toggledActionBar, setToggledActionBar] = useState(true);

const toggleActionBar = () => {
setToggledActionBar(!toggledActionBar);
}

const dispatch = useDispatch();

const handleCompleteAll = () => {
dispatch(tasks.actions.completeAllTasks())
}
const handleDeleteAll = () => {
if (window.confirm('Are you sure you want to delete all?')) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice touch adding the window. Adds a lot to the user experience that you can't accidentally just delete everything with a single click.

dispatch(tasks.actions.deleteAllTasks())
}
}
const tasksData = useSelector((state) => state.tasks.todos)
return (
<StyledContainer>
<StyledTasklistBar>
<HamburgerMenuButton onClick={toggleActionBar}>
<FontAwesomeIcon icon={faBars} style={{ color: '#ffffff' }} />
</HamburgerMenuButton>
<h1>tasks</h1>
</StyledTasklistBar>
<StyledActionBar className={toggledActionBar && 'active'}>
<CompleteAllButton value="Complete All" onClick={handleCompleteAll}>complete all</CompleteAllButton>
<DeleteAllButton value="Delete All" onClick={handleDeleteAll}>delete all</DeleteAllButton>
</StyledActionBar>
{tasksData.map((task) => (
<Task id={task.id} key={task.id} />))}
</StyledContainer>
);
}

export default TaskList;
25 changes: 25 additions & 0 deletions code/src/components/Todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import styled from 'styled-components/macro'
import TaskAdder from './TaskAdder';
import TaskList from './TaskList';

const TodoSection = styled.section`
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--foreground-secondary-color);
overflow: visible;
box-shadow:
0.3em 0.3em 1em rgba(0, 0, 0, 0.3);
`

const Todos = () => {
return (
<TodoSection>
<TaskList />
<TaskAdder />
</TodoSection>
);
}

export default Todos;
Loading