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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# Todo
## links
- github: https://github.com/alex91-html/js-project-todo
- netlify: https://alextodo-app.netlify.app/


<!--
## Project Structure
src/
├── App.js # Main App component with routing
├── store/
│ └── todoStore.jsx # Zustand store for state management
├── components/
│ ├── StyledComponents.jsx # Reusable styled components
│ ├── Header.jsx # App header with date and controls
│ ├── TaskItem.jsx # Individual task component
│ ├── AddTask.jsx # Form to add new tasks
│ ├── TaskList.jsx # List container for tasks
│ └── TaskFilters.jsx # Filter and action buttons
├── pages/
│ └── TodoPage.jsx # Main todo page
└── styles/
└── GlobalStyles.jsx

-->
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@
"preview": "vite preview"
},
"dependencies": {
"@lottiefiles/dotlottie-react": "^0.13.5",
"date-fns": "^4.1.0",
"lottie-react": "^2.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.6.1",
"styled-components": "^6.1.18",
"zustand": "^5.0.5"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
Expand Down
4 changes: 3 additions & 1 deletion pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Please include your Netlify link here.
## links
- github: https://github.com/alex91-html/js-project-todo
- netlify: https://alextodo-app.netlify.app/Please include your Netlify link here.
25 changes: 22 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import { BrowserRouter as Router } from 'react-router-dom';
import { GlobalStyle } from './styles/GlobalStyles';
import { MainContainer } from './components/StyledComponents';
import { useThemeStore } from './store/themeStore';
import Header from './components/Header';
import AddTask from './components/AddTask';
import TaskList from './components/TaskList';

export const App = () => {
const theme = useThemeStore((s) => s.theme);

return (
<h1>React Boilerplate</h1>
)
}
<Router>
<GlobalStyle theme={theme} />
<MainContainer theme={theme}>
<Header />
<main>
<AddTask />
<TaskList />
</main>
</MainContainer>
</Router>
);
};
56 changes: 56 additions & 0 deletions src/components/AddTask.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useState } from 'react';
import { useThemeStore } from '../store/themeStore';

import styled from 'styled-components';
import AddTaskModal from './AddTaskModal';

const AddButton = styled.button`
position: fixed;
right: 24px;
bottom: 24px;
width: 56px;
height: 56px;
border-radius: 20%;
background: ${({ theme }) => theme === 'dark' ? '#b4b4b4' : '#111'};
color: ${({ theme }) => theme === 'dark' ? '#4e4e4e' : '#fff'};
font-size: 2.2rem;
font-weight: 200;
font-family: 'Arial', 'Helvetica Neue', Arial, sans-serif;
border: none;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
cursor: pointer;
z-index: 100;
transition: background 0.2s, color 0.2s, box-shadow 0.18s, transform 0.18s;


&:hover {
background: ${({ theme }) => theme === 'dark' ? '#FAE179' : '#40405c'};
}

@media (min-width: 1024px) {
position: absolute;
right: 40px;
bottom: 40px;
}

`;

const AddTask = () => {
const [open, setOpen] = useState(false);
const theme = useThemeStore((s) => s.theme);


return (
<>
<AddButton onClick={() => setOpen(true)} aria-label="Add task" theme={theme}>
</AddButton>
{open && <AddTaskModal onClose={() => setOpen(false)} />}
</>
);
};

export default AddTask;
192 changes: 192 additions & 0 deletions src/components/AddTaskModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import styled from 'styled-components';
import { useState } from 'react';
import { useTodoStore } from '../store/todoStore';
import { useThemeStore } from '../store/themeStore';


const ModalOverlay = styled.div`
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
display: flex;
align-items: flex-end;
justify-content: center;
z-index: 200;

@media (min-width: 1024px) {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: 32px;

}
`;

const Modal = styled.div`
background: ${({ theme }) => theme === 'dark' ? '#23272f' : '#fff'};
color: ${({ theme }) => theme === 'dark' ? '#fafafa' : '#222'};
width: 100%;
max-width: 600px;
border-radius: 32px 32px 0 0;
padding: 32px 24px 24px 24px;
box-shadow: 0 -2px 24px rgba(0,0,0,0.08);
margin-bottom: 0;

@media (min-width: 1024px) {
width: 100%;
max-width: 600px;
border-radius: 32px;
padding: 48px 40px;
box-shadow: 0 8px 32px rgba(0,0,0,0.08);
}

`;

const ModalBar = styled.div`
width: 60px;
height: 6px;
background: #e0e3e7;
border-radius: 3px;
margin: 0 auto 18px auto;
`;

const ModalTitle = styled.h2`
color: ${({ theme }) => theme === 'dark' ? '#fafafa' : '#222'};
text-align: center;
font-size: 2rem;
margin: 0 0 24px 0;
`;

const Input = styled.input`
width: 100%;
padding: 18px;
border-radius: 16px;
border: 2px solid #b5cdfa;
background: #f7f8fa;
font-size: 1.15rem;
margin-bottom: 18px;
outline: none;
color: #222;
&::placeholder {
color: #aab2bb;
font-size: 1.1rem;
}
`;

const Select = styled.select`
width: 100%;
padding: 16px;
border-radius: 12px;
border: none;
background: #f7f8fa;
font-size: 1.1rem;
margin-bottom: 28px;
color: #222;
`;

const ButtonRow = styled.div`
display: flex;
gap: 16px;
`;

const CancelButton = styled.button`
flex: 1;
padding: 18px 0;
border-radius: 16px;
border: none;
background: #f7f8fa;
color: #6b7280;

&:hover {
background:#e57373;
color: #fff;
}


`;

const SubmitButton = styled.button`
flex: 2;
padding: 18px 0;
border-radius: 16px;
border: none;
background: #888;
color: #fff;

&:hover {
background: #40405c;;
}

`;

const AddTaskModal = ({ onClose }) => {
const [value, setValue] = useState('');
const [category, setCategory] = useState('General');
const addTask = useTodoStore((s) => s.addTask);
const theme = useThemeStore((s) => s.theme);


const handleSubmit = (e) => {
e.preventDefault();
if (value.trim()) {
addTask({
id: Date.now(),
title: value,
category,
completed: false,
createdAt: new Date().toISOString(),
});
setValue('');
setCategory('General');
onClose();
}
};

const handleOverlayClick = (e) => {
if (e.target === e.currentTarget) onClose();
};

return (
<ModalOverlay onClick={handleOverlayClick}>
<Modal theme={theme}>
<ModalBar />
<ModalTitle theme={theme}>Add New Task</ModalTitle>
<form onSubmit={handleSubmit}>
<Input
type="text"
placeholder="What needs to be done?"
value={value}
onChange={(e) => setValue(e.target.value)}
aria-label="Task name"
autoFocus
/>
<Select
value={category}
onChange={(e) => setCategory(e.target.value)}
aria-label="Category"
>
<option value="General">General</option>
<option value="Finance">Finance</option>
<option value="Freelance">Freelance</option>
<option value="Design">Design</option>
<option value="Shopping List">Shopping List</option>
<option value="Personal">Personal</option>
<option value="Health">Health</option>
</Select>
<ButtonRow>
<CancelButton type="button" onClick={onClose}>
Cancel
</CancelButton>
<SubmitButton type="submit">
Add Task
</SubmitButton>
</ButtonRow>
</form>
</Modal>
</ModalOverlay>
);
};

export default AddTaskModal;
Loading