Add comprehensive to-do list application with local storage - #2
Riskiandikayt wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a complete React/TypeScript todo-list application. It establishes the project structure with Vite and TypeScript, defines domain types and utilities for filtering/sorting/persistence, implements a state-managed App component, and builds a component hierarchy for form creation, filtering, item editing, list rendering, and statistics display, plus comprehensive styling and documentation. ChangesReact Todo-List Application
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Gss |
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (2)
todo-list/src/index.css (1)
26-44: 💤 Low valueNote: Scrollbar styling is WebKit-only.
The custom scrollbar styles only work in WebKit-based browsers (Chrome, Safari, Edge). Firefox users will see default scrollbars. This is acceptable as progressive enhancement, but consider documenting this browser limitation if cross-browser consistency is important.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@todo-list/src/index.css` around lines 26 - 44, The current custom scrollbar rules use WebKit-only pseudo-elements (:: -webkit-scrollbar, ::-webkit-scrollbar-thumb, ::-webkit-scrollbar-track) and won't affect Firefox; update the CSS to either (A) add a short inline comment above these rules calling out the WebKit-only limitation for future maintainers, or (B) add complementary Firefox properties (e.g., using scrollbar-width and scrollbar-color on the root or target elements) so Firefox users get a similar thin/colored scrollbar; reference the existing selectors (:: -webkit-scrollbar, ::-webkit-scrollbar-thumb, ::-webkit-scrollbar-track) and add the chosen documentation or fallback properties in the same file to ensure the limitation is explicit or progressively enhanced for non-WebKit browsers.todo-list/src/components/TodoForm/TodoForm.tsx (1)
80-80: ⚡ Quick winRemove
as anyin priorityonChangehandler (TodoForm.tsx line 80).
as anybypasses theTodo['priority']union type; cast toTodo['priority']instead.Proposed fix
- onChange={(e) => setPriority(e.target.value as any)} + onChange={(e) => setPriority(e.target.value as Todo['priority'])}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@todo-list/src/components/TodoForm/TodoForm.tsx` at line 80, In the TodoForm component replace the unsafe cast in the onChange handler for priority (currently using setPriority(e.target.value as any)) with a proper cast to the Todo priority union (e.g., setPriority(e.target.value as Todo['priority'])) or type the event as ChangeEvent<HTMLSelectElement> and use its typed value; update the onChange signature to accept the correct event type and cast to Todo['priority'] so you no longer bypass the union type enforcement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@todo-list/package.json`:
- Around line 11-21: Update the toolchain dependencies in package.json: bump
"react" and "react-dom" to the newer stable release (e.g., 19.2.6), update
"vite" to at least 6.4.2 (preferably the latest, e.g., 8.0.14) to address the
path-traversal advisory, upgrade "typescript" to the newer release (e.g., 6.0.3)
and "`@vitejs/plugin-react`" to the compatible latest (e.g., 6.0.2); also refresh
"`@types/react`" and "`@types/react-dom`" to versions compatible with React 19.
After editing the dependencies/devDependencies entries, run the package manager
install and then run npm/yarn audit (or pnpm audit) and the project's dev
build/tests to verify compatibility and fix any type or plugin breaking changes.
In `@todo-list/README.md`:
- Around line 56-58: The fenced code block containing "http://localhost:3001" in
README.md is missing a language identifier; update that code fence to include a
language (e.g., use ```text) so markdown linters and syntax highlighters
recognize it—locate the code block in README.md and change the opening fence
from ``` to ```text (or another appropriate language) and ensure the closing
fence remains ``` to preserve formatting.
- Around line 70-93: The README.md project-structure fenced code block is
missing a language identifier; update the triple-backtick fence that wraps the
tree (the block beginning with "todo-list/") to include a language token (e.g.,
change ``` to ```text) so the block becomes ```text and satisfies markdown
linting and syntax highlighting.
In `@todo-list/src/App.css`:
- Line 12: Rename the keyframe identifiers from camelCase to kebab-case to
satisfy stylelint: change slideDown -> slide-down and fadeIn -> fade-in across
the file, update their `@keyframes` declarations (e.g., `@keyframes` slide-down,
`@keyframes` fade-in) and all usages in animation/animation-name rules (e.g.,
animation: slide-down 0.6s ease-out) so the names match; ensure you update every
occurrence including any references near the animation declarations and the
`@keyframes` blocks for the symbols slideDown and fadeIn.
In `@todo-list/src/App.tsx`:
- Around line 29-58: The handlers handleAddTodo, handleToggleTodo,
handleDeleteTodo, handleUpdateTodo, and handleClearCompleted use the closed-over
todos array and should use functional state updaters to avoid stale closures;
update each setTodos call to the functional form setTodos(prev => /* compute new
array from prev */) so you prepend newTodo to prev in handleAddTodo, map/filter
prev in toggle/delete/update handlers (preserving updatedAt logic in
handleUpdateTodo), and filter prev in handleClearCompleted after confirmation.
In `@todo-list/src/components/TodoFilters/TodoFilters.css`:
- Line 7: The CSS keyframes name slideUp should be renamed to kebab-case
slide-up and all references updated: change the `@keyframes` declaration named
slideUp to `@keyframes` slide-up and update the animation usage (e.g., the
animation property on the TodoFilters component that currently uses "slideUp")
to use "slide-up" so Stylelint accepts the kebab-case keyframe name.
In `@todo-list/src/components/TodoForm/TodoForm.css`:
- Line 7: The keyframe names use camelCase and must be kebab-case to satisfy
stylelint: rename `@keyframes` slideUp and `@keyframes` slideDown to `@keyframes`
slide-up and `@keyframes` slide-down, and update every animation reference—e.g.,
the shorthand declarations animation: slideUp and animation: slideDown—as well
as any animation-name usages to use slide-up and slide-down; ensure the renamed
identifiers are consistently replaced throughout TodoForm.css (including
vendor-prefixed or shorthand animation properties) so no references remain to
the old camelCase names.
In `@todo-list/src/components/TodoItem/TodoItem.css`:
- Line 62: Replace deprecated word-break: break-word and word-wrap: break-word
in TodoItem.css with the modern overflow-wrap value (e.g., overflow-wrap:
anywhere or overflow-wrap: break-word) to handle text wrapping, and rename the
animation/keyframes from camelCase slideIn to kebab-case slide-in; update the
animation declaration (animation: slide-in ...) and the `@keyframes` rule
(`@keyframes` slide-in { ... }) accordingly so both the usage and definition
match.
In `@todo-list/src/components/TodoStats/TodoStats.css`:
- Line 75: The keyframe name uses camelCase "fadeIn" which violates the
kebab-case lint rule; rename the `@keyframes` rule from "fadeIn" to a kebab-case
name like "fade-in" and update every animation usage that references it (e.g.,
the CSS declaration "animation: fadeIn ..." on Line 6) to "animation: fade-in
..."; also update any other occurrences (animation-name or shorthand) in the
TodoStats stylesheet to match the new "fade-in" identifier.
In `@todo-list/src/index.tsx`:
- Line 6: ReactDOM.createRoot(document.getElementById('root')!).render(...) uses
a non-null assertion on document.getElementById('root'); change this to first
assign const rootEl = document.getElementById('root'), check if rootEl is null
and handle gracefully (e.g., log error and return or throw a clear error) before
calling ReactDOM.createRoot(rootEl). This ensures ReactDOM.createRoot and render
are only called when the 'root' element actually exists.
In `@todo-list/src/utils/helpers.ts`:
- Around line 64-67: The comparator passed to sorted.sort incorrectly returns 1
when a.dueDate is falsy even if both a.dueDate and b.dueDate are missing,
causing unstable sorts; update the comparator in the sorted.sort callback to
explicitly handle all three cases: if both a.dueDate and b.dueDate are missing
return 0, if only a.dueDate is missing return 1, if only b.dueDate is missing
return -1, and otherwise return the numeric difference of new
Date(a.dueDate).getTime() - new Date(b.dueDate).getTime(); ensure the comparator
always returns a number so sort stability is preserved.
In `@todo-list/src/utils/storage.ts`:
- Around line 9-10: The code trusts
JSON.parse(localStorage.getItem(STORAGE_KEY)) as Todo[]; instead add robust
validation and safe parsing: wrap JSON.parse in try/catch, ensure the parsed
value is an array (Array.isArray), and filter/map items using a type-guard that
checks required Todo properties (e.g., title is string, priority is one of
expected values, completed is boolean, id if present is string/number) before
returning; return [] as a safe fallback for any malformed or non-array payload.
Apply the same pattern where JSON.parse is used elsewhere (the other block
handling STORAGE_KEY at the later section) so both imports and persisted data
are validated before being treated as Todo[].
---
Nitpick comments:
In `@todo-list/src/components/TodoForm/TodoForm.tsx`:
- Line 80: In the TodoForm component replace the unsafe cast in the onChange
handler for priority (currently using setPriority(e.target.value as any)) with a
proper cast to the Todo priority union (e.g., setPriority(e.target.value as
Todo['priority'])) or type the event as ChangeEvent<HTMLSelectElement> and use
its typed value; update the onChange signature to accept the correct event type
and cast to Todo['priority'] so you no longer bypass the union type enforcement.
In `@todo-list/src/index.css`:
- Around line 26-44: The current custom scrollbar rules use WebKit-only
pseudo-elements (:: -webkit-scrollbar, ::-webkit-scrollbar-thumb,
::-webkit-scrollbar-track) and won't affect Firefox; update the CSS to either
(A) add a short inline comment above these rules calling out the WebKit-only
limitation for future maintainers, or (B) add complementary Firefox properties
(e.g., using scrollbar-width and scrollbar-color on the root or target elements)
so Firefox users get a similar thin/colored scrollbar; reference the existing
selectors (:: -webkit-scrollbar, ::-webkit-scrollbar-thumb,
::-webkit-scrollbar-track) and add the chosen documentation or fallback
properties in the same file to ensure the limitation is explicit or
progressively enhanced for non-WebKit browsers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0443e9a8-6f5f-4e4e-a1ef-08d07dcec8d1
📒 Files selected for processing (24)
todo-list/.gitignoretodo-list/README.mdtodo-list/index.htmltodo-list/package.jsontodo-list/src/App.csstodo-list/src/App.tsxtodo-list/src/components/TodoFilters/TodoFilters.csstodo-list/src/components/TodoFilters/TodoFilters.tsxtodo-list/src/components/TodoForm/TodoForm.csstodo-list/src/components/TodoForm/TodoForm.tsxtodo-list/src/components/TodoItem/TodoItem.csstodo-list/src/components/TodoItem/TodoItem.tsxtodo-list/src/components/TodoList/TodoList.csstodo-list/src/components/TodoList/TodoList.tsxtodo-list/src/components/TodoStats/TodoStats.csstodo-list/src/components/TodoStats/TodoStats.tsxtodo-list/src/index.csstodo-list/src/index.tsxtodo-list/src/types/index.tstodo-list/src/utils/helpers.tstodo-list/src/utils/storage.tstodo-list/tsconfig.jsontodo-list/tsconfig.node.jsontodo-list/vite.config.ts
| "dependencies": { | ||
| "react": "^18.2.0", | ||
| "react-dom": "^18.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/react": "^18.2.43", | ||
| "@types/react-dom": "^18.2.17", | ||
| "@vitejs/plugin-react": "^4.2.1", | ||
| "typescript": "^5.2.2", | ||
| "vite": "^5.0.8" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for the latest versions and security advisories
echo "=== Checking latest versions on npm ==="
for pkg in react react-dom `@types/react` `@types/react-dom` `@vitejs/plugin-react` typescript vite; do
echo "Package: $pkg"
npm view "$pkg" version 2>/dev/null || echo "Failed to fetch"
done
echo -e "\n=== Checking for security advisories ==="
for pkg in react react-dom vite typescript; do
echo "Checking $pkg..."
gh api graphql -f query="
{
securityVulnerabilities(first: 5, ecosystem: NPM, package: \"$pkg\") {
nodes {
advisory {
summary
severity
publishedAt
}
vulnerableVersionRange
firstPatchedVersion {
identifier
}
}
}
}" 2>/dev/null || echo "No advisories or query failed"
doneRepository: Riskiandikayt/qemu-web-emulator
Length of output: 3444
Update Vite and refresh outdated React toolchain dependencies
- npm has newer versions:
react/react-dom(19.2.6),vite(8.0.14),typescript(6.0.3),@vitejs/plugin-react(6.0.2). - Security: current
vite@^5.0.8falls into the MODERATE advisory range “Vite vulnerable to Path Traversal in Optimized Deps.mapHandling” (<= 6.4.1), patched in6.4.2→ update Vite to>= 6.4.2(preferably latest compatible). - The security advisories returned for
react@18.2.0/react-dom@18.2.0are for much older ranges, and the typescript query returned no advisories; still consider checking@vitejs/plugin-reactand@types/*advisories too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/package.json` around lines 11 - 21, Update the toolchain
dependencies in package.json: bump "react" and "react-dom" to the newer stable
release (e.g., 19.2.6), update "vite" to at least 6.4.2 (preferably the latest,
e.g., 8.0.14) to address the path-traversal advisory, upgrade "typescript" to
the newer release (e.g., 6.0.3) and "`@vitejs/plugin-react`" to the compatible
latest (e.g., 6.0.2); also refresh "`@types/react`" and "`@types/react-dom`" to
versions compatible with React 19. After editing the
dependencies/devDependencies entries, run the package manager install and then
run npm/yarn audit (or pnpm audit) and the project's dev build/tests to verify
compatibility and fix any type or plugin breaking changes.
| ``` | ||
| http://localhost:3001 | ||
| ``` |
There was a problem hiding this comment.
Add language identifier to code block.
The fenced code block should specify a language for proper syntax highlighting and to satisfy markdown linting rules.
📝 Proposed fix
-```
+```text
http://localhost:3001</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 56-56: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/README.md` around lines 56 - 58, The fenced code block containing
"http://localhost:3001" in README.md is missing a language identifier; update
that code fence to include a language (e.g., use ```text) so markdown linters
and syntax highlighters recognize it—locate the code block in README.md and
change the opening fence from ``` to ```text (or another appropriate language)
and ensure the closing fence remains ``` to preserve formatting.
| ``` | ||
| todo-list/ | ||
| ├── src/ | ||
| │ ├── components/ | ||
| │ │ ├── TodoForm/ # Task input form with advanced options | ||
| │ │ ├── TodoList/ # Task list container | ||
| │ │ ├── TodoItem/ # Individual task component | ||
| │ │ ├── TodoStats/ # Statistics dashboard | ||
| │ │ └── TodoFilters/ # Filter and search controls | ||
| │ ├── utils/ | ||
| │ │ ├── storage.ts # LocalStorage operations | ||
| │ │ └── helpers.ts # Utility functions | ||
| │ ├── types/ | ||
| │ │ └── index.ts # TypeScript interfaces | ||
| │ ├── App.tsx # Main application component | ||
| │ ├── App.css # App styles | ||
| │ ├── index.tsx # React entry point | ||
| │ └── index.css # Global styles | ||
| ├── index.html # HTML template | ||
| ├── vite.config.ts # Vite configuration | ||
| ├── tsconfig.json # TypeScript config | ||
| ├── package.json # Dependencies | ||
| └── README.md # This file | ||
| ``` |
There was a problem hiding this comment.
Add language identifier to code block.
The fenced code block showing the project structure should specify a language for proper syntax highlighting and to satisfy markdown linting rules.
📝 Proposed fix
-```
+```text
todo-list/
├── src/📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| todo-list/ | |
| ├── src/ | |
| │ ├── components/ | |
| │ │ ├── TodoForm/ # Task input form with advanced options | |
| │ │ ├── TodoList/ # Task list container | |
| │ │ ├── TodoItem/ # Individual task component | |
| │ │ ├── TodoStats/ # Statistics dashboard | |
| │ │ └── TodoFilters/ # Filter and search controls | |
| │ ├── utils/ | |
| │ │ ├── storage.ts # LocalStorage operations | |
| │ │ └── helpers.ts # Utility functions | |
| │ ├── types/ | |
| │ │ └── index.ts # TypeScript interfaces | |
| │ ├── App.tsx # Main application component | |
| │ ├── App.css # App styles | |
| │ ├── index.tsx # React entry point | |
| │ └── index.css # Global styles | |
| ├── index.html # HTML template | |
| ├── vite.config.ts # Vite configuration | |
| ├── tsconfig.json # TypeScript config | |
| ├── package.json # Dependencies | |
| └── README.md # This file | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 70-70: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/README.md` around lines 70 - 93, The README.md project-structure
fenced code block is missing a language identifier; update the triple-backtick
fence that wraps the tree (the block beginning with "todo-list/") to include a
language token (e.g., change ``` to ```text) so the block becomes ```text and
satisfies markdown linting and syntax highlighting.
| text-align: center; | ||
| margin-bottom: 30px; | ||
| color: white; | ||
| animation: slideDown 0.6s ease-out; |
There was a problem hiding this comment.
Rename keyframes to kebab-case to satisfy stylelint and avoid CI failure.
slideDown and fadeIn violate keyframes-name-pattern; update both declarations and references.
Suggested fix
.app-header {
@@
- animation: slideDown 0.6s ease-out;
+ animation: slide-down 0.6s ease-out;
}
@@
.empty-state {
@@
- animation: fadeIn 0.6s ease-out;
+ animation: fade-in 0.6s ease-out;
}
@@
-@keyframes slideDown {
+@keyframes slide-down {
@@
-@keyframes fadeIn {
+@keyframes fade-in {Also applies to: 32-32, 47-47, 58-58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/App.css` at line 12, Rename the keyframe identifiers from
camelCase to kebab-case to satisfy stylelint: change slideDown -> slide-down and
fadeIn -> fade-in across the file, update their `@keyframes` declarations (e.g.,
`@keyframes` slide-down, `@keyframes` fade-in) and all usages in
animation/animation-name rules (e.g., animation: slide-down 0.6s ease-out) so
the names match; ensure you update every occurrence including any references
near the animation declarations and the `@keyframes` blocks for the symbols
slideDown and fadeIn.
| const handleAddTodo = (newTodo: Todo) => { | ||
| setTodos([newTodo, ...todos]) | ||
| } | ||
|
|
||
| const handleToggleTodo = (id: string) => { | ||
| setTodos( | ||
| todos.map((todo) => | ||
| todo.id === id ? { ...todo, completed: !todo.completed } : todo | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| const handleDeleteTodo = (id: string) => { | ||
| setTodos(todos.filter((todo) => todo.id !== id)) | ||
| } | ||
|
|
||
| const handleUpdateTodo = (id: string, updates: Partial<Todo>) => { | ||
| setTodos( | ||
| todos.map((todo) => | ||
| todo.id === id | ||
| ? { ...todo, ...updates, updatedAt: new Date().toISOString() } | ||
| : todo | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| const handleClearCompleted = () => { | ||
| if (window.confirm('Are you sure you want to delete all completed todos?')) { | ||
| setTodos(todos.filter((todo) => !todo.completed)) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the file
ls -la
find . -maxdepth 4 -type f -path "*todo-list/src/App.tsx" -print
# Show the relevant section with line numbers
sed -n '1,120p' todo-list/src/App.tsx | nl -ba | sed -n '1,140p'
# If file is longer, also show the tail where handlers might continue
wc -l todo-list/src/App.tsx
sed -n '120,220p' todo-list/src/App.tsx | nl -ba | sed -n '1,120p' || true
# Search for any other setTodos usage (to assess batching/stale dependency)
rg -n "setTodos\s*\(" todo-list/src/App.tsx todo-list/src -S || trueRepository: Riskiandikayt/qemu-web-emulator
Length of output: 632
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Basic info
wc -l todo-list/src/App.tsx
echo "----"
# Show lines 1-120 with line numbers (using cat -n)
cat -n todo-list/src/App.tsx | sed -n '1,140p'
echo "----"
# Ensure we capture the referenced region more tightly (approx. 29-58)
cat -n todo-list/src/App.tsx | sed -n '20,80p'
echo "----"
# Find how todos state is declared
rg -n "useState<.*Todo.*>|useState\s*\(|todos\s*,\s*setTodos|setTodos\s*=" todo-list/src/App.tsx todo-list/src -S || true
# Find all setTodos usages to see whether functional updater is already used elsewhere
rg -n "setTodos\s*\(" todo-list/src/App.tsx todo-list/src -S || trueRepository: Riskiandikayt/qemu-web-emulator
Length of output: 8328
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# List relevant files in todo-list/src/components
find todo-list/src/components -maxdepth 3 -type f -print
# Find where App's handlers are called via props
rg -n "onToggle|onDelete|onUpdate|onAddTodo|onClearCompleted" todo-list/src/components -S
# Inspect TodoList and TodoItem implementations around prop usage
for f in todo-list/src/components/TodoList/TodoList.tsx todo-list/src/components/TodoItem/TodoItem.tsx todo-list/src/components/TodoForm/TodoForm.tsx todo-list/src/components/TodoStats/TodoStats.tsx todo-list/src/components/TodoFilters/TodoFilters.tsx; do
if [ -f "$f" ]; then
echo "---- $f"
wc -l "$f"
sed -n '1,200p' "$f"
fi
doneRepository: Riskiandikayt/qemu-web-emulator
Length of output: 15291
Use functional setTodos updates in all todo handlers to avoid stale-closure drops
handleAddTodo,handleToggleTodo,handleDeleteTodo,handleUpdateTodo, andhandleClearCompletedcompute next state from the closed-overtodosvalue (lines 29–58); React’s batching/concurrency can drop intermediate changes—switch these to functional updaters.
Suggested fix
const handleAddTodo = (newTodo: Todo) => {
- setTodos([newTodo, ...todos])
+ setTodos((prev) => [newTodo, ...prev])
}
@@
const handleToggleTodo = (id: string) => {
- setTodos(
- todos.map((todo) =>
+ setTodos((prev) =>
+ prev.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
)
}
@@
const handleDeleteTodo = (id: string) => {
- setTodos(todos.filter((todo) => todo.id !== id))
+ setTodos((prev) => prev.filter((todo) => todo.id !== id))
}
@@
const handleUpdateTodo = (id: string, updates: Partial<Todo>) => {
- setTodos(
- todos.map((todo) =>
+ setTodos((prev) =>
+ prev.map((todo) =>
todo.id === id
? { ...todo, ...updates, updatedAt: new Date().toISOString() }
: todo
)
)
}
@@
const handleClearCompleted = () => {
if (window.confirm('Are you sure you want to delete all completed todos?')) {
- setTodos(todos.filter((todo) => !todo.completed))
+ setTodos((prev) => prev.filter((todo) => !todo.completed))
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleAddTodo = (newTodo: Todo) => { | |
| setTodos([newTodo, ...todos]) | |
| } | |
| const handleToggleTodo = (id: string) => { | |
| setTodos( | |
| todos.map((todo) => | |
| todo.id === id ? { ...todo, completed: !todo.completed } : todo | |
| ) | |
| ) | |
| } | |
| const handleDeleteTodo = (id: string) => { | |
| setTodos(todos.filter((todo) => todo.id !== id)) | |
| } | |
| const handleUpdateTodo = (id: string, updates: Partial<Todo>) => { | |
| setTodos( | |
| todos.map((todo) => | |
| todo.id === id | |
| ? { ...todo, ...updates, updatedAt: new Date().toISOString() } | |
| : todo | |
| ) | |
| ) | |
| } | |
| const handleClearCompleted = () => { | |
| if (window.confirm('Are you sure you want to delete all completed todos?')) { | |
| setTodos(todos.filter((todo) => !todo.completed)) | |
| } | |
| const handleAddTodo = (newTodo: Todo) => { | |
| setTodos((prev) => [newTodo, ...prev]) | |
| } | |
| const handleToggleTodo = (id: string) => { | |
| setTodos((prev) => | |
| prev.map((todo) => | |
| todo.id === id ? { ...todo, completed: !todo.completed } : todo | |
| ) | |
| ) | |
| } | |
| const handleDeleteTodo = (id: string) => { | |
| setTodos((prev) => prev.filter((todo) => todo.id !== id)) | |
| } | |
| const handleUpdateTodo = (id: string, updates: Partial<Todo>) => { | |
| setTodos((prev) => | |
| prev.map((todo) => | |
| todo.id === id | |
| ? { ...todo, ...updates, updatedAt: new Date().toISOString() } | |
| : todo | |
| ) | |
| ) | |
| } | |
| const handleClearCompleted = () => { | |
| if (window.confirm('Are you sure you want to delete all completed todos?')) { | |
| setTodos((prev) => prev.filter((todo) => !todo.completed)) | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/App.tsx` around lines 29 - 58, The handlers handleAddTodo,
handleToggleTodo, handleDeleteTodo, handleUpdateTodo, and handleClearCompleted
use the closed-over todos array and should use functional state updaters to
avoid stale closures; update each setTodos call to the functional form
setTodos(prev => /* compute new array from prev */) so you prepend newTodo to
prev in handleAddTodo, map/filter prev in toggle/delete/update handlers
(preserving updatedAt logic in handleUpdateTodo), and filter prev in
handleClearCompleted after confirmation.
| font-weight: 600; | ||
| color: #333; | ||
| margin: 0; | ||
| word-break: break-word; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify deprecated/warned CSS patterns in TodoItem stylesheet
rg -n 'word-break:\s*break-word|word-wrap:|`@keyframes`\s+[a-z]+[A-Z]|animation:\s*[a-z]+[A-Z]' todo-list/src/components/TodoItem/TodoItem.cssRepository: Riskiandikayt/qemu-web-emulator
Length of output: 196
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="todo-list/src/components/TodoItem/TodoItem.css"
sed -n '1,120p' "$FILE" | nl -ba | sed -n '1,120p'
echo "----"
sed -n '160,240p' "$FILE" | nl -ba | sed -n '160,240p'Repository: Riskiandikayt/qemu-web-emulator
Length of output: 118
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="todo-list/src/components/TodoItem/TodoItem.css"
# Show relevant sections around the flagged lines
nl -ba "$FILE" | sed -n '1,130p'
echo "----"
nl -ba "$FILE" | sed -n '150,230p'Repository: Riskiandikayt/qemu-web-emulator
Length of output: 118
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="todo-list/src/components/TodoItem/TodoItem.css"
echo "---- 1-130 ----"
cat -n "$FILE" | sed -n '1,130p'
echo "---- 150-230 ----"
cat -n "$FILE" | sed -n '150,230p'Repository: Riskiandikayt/qemu-web-emulator
Length of output: 4577
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find Stylelint config locations
ls -1 | rg -n 'stylelint|\.stylelintrc|stylelint\.config|package\.json' || true
fd -t f -a -g '*.json' -g '*.js' -g '*.cjs' -g '*.mjs' -g '.stylelintrc*' . | head -n 50 || true
# Search for relevant lint rules/mentions
rg -n --hidden --glob '!**/node_modules/**' \
'word-break|word-wrap|overflow-wrap|keyframes|slideIn|slide-in' \
. || trueRepository: Riskiandikayt/qemu-web-emulator
Length of output: 1307
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show where stylelint is configured/used
rg -n --hidden --glob '!**/node_modules/**' 'stylelint' .
rg -n --hidden --glob '!**/node_modules/**' 'word-break|word-wrap|overflow-wrap' .
rg -n --hidden --glob '!**/node_modules/**' 'slideIn|slide-in' todo-list . || true
# If stylelint config files exist, show them (small)
fd -t f -a -0 .stylelintrc . -E node_modules 2>/dev/null | xargs -r -n 1 echo
fd -t f -a . -g '*.stylelintrc*' -E node_modules 2>/dev/null | head -n 20
fd -t f -a -g '*.css' todo-list/src/components/TodoItem -E node_modules 2>/dev/null | xargs -r -n 1 echoRepository: Riskiandikayt/qemu-web-emulator
Length of output: 609
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n ./.stylelintrc.json | sed -n '1,200p'Repository: Riskiandikayt/qemu-web-emulator
Length of output: 1701
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n todo-list/package.json | sed -n '1,220p'Repository: Riskiandikayt/qemu-web-emulator
Length of output: 681
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --glob '!**/node_modules/**' \
'keyframes-name-pattern|keyframe-name|slideIn|word-break|word-wrap|overflow-wrap' . || trueRepository: Riskiandikayt/qemu-web-emulator
Length of output: 392
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# List package.json files
fd -t f -e json 'package.json' . -E node_modules
# Search for stylelint usage in package.json(s)
fd -t f 'package.json' . -E node_modules -x rg -n 'stylelint|lint' || true
# Search for stylelint-config-standard-scss installed anywhere
fd -t f 'package.json' . -E node_modules -x rg -n 'stylelint-config-standard-scss' || true
# Search for common keyframes-name-pattern rule mention in repo
rg -n --hidden --glob '!**/node_modules/**' 'keyframes-name-pattern|keyframe-name-pattern' . || trueRepository: Riskiandikayt/qemu-web-emulator
Length of output: 100
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n 'slideIn' --hidden --glob '!**/node_modules/**' .
rg -n 'word-break:\s*break-word|word-wrap:\s*break-word|`@keyframes`\s+slideIn|animation:\s*slideIn' todo-list/src/components/TodoItem/TodoItem.cssRepository: Riskiandikayt/qemu-web-emulator
Length of output: 358
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --glob '!**/node_modules/**' 'stylelint' .
fd -t f -e json 'package-lock.json' 'pnpm-lock.yaml' 'yarn.lock' .Repository: Riskiandikayt/qemu-web-emulator
Length of output: 269
🌐 Web query:
stylelint-config-standard-scss keyframes-name-pattern rule default
💡 Result:
The stylelint-config-standard-scss config does not define a “rule default” for keyframes-name-pattern. In Stylelint, rules like keyframes-name-pattern are configured via the rules object; Stylelint’s docs state that “No rules are turned on by default,” meaning defaults apply only if you enable/configure the rule via extends/rules in your config. [1] For the actual keyframes-name-pattern rule, the rule’s option type is a string/regex pattern; the rule README documents that the option is the pattern you want to enforce (e.g., { "keyframes-name-pattern": "foo-.+" }). It does not document any built-in default pattern value. [2] So, unless stylelint-config-standard-scss (or something it extends) explicitly sets keyframes-name-pattern in its config index.js, there is no rule “default” pattern to look up—only whatever the config enables. The npm page for the config points you to the config source to see exactly which rules it uses. [3]
Citations:
- 1: https://github.com/stylelint/stylelint/blob/master/docs/user-guide/configure.md
- 2: https://github.com/stylelint/stylelint/blob/main/lib/rules/keyframes-name-pattern/README.md
- 3: https://registry.npmjs.org/stylelint-config-standard-scss
🌐 Web query:
stylelint word-wrap property disallowed deprecated overflow-wrap-anywhere break-word
💡 Result:
Stylelint support for your query is mainly about the deprecated CSS property “word-wrap: break-word” (the “word-wrap” property is deprecated), and about using Stylelint rules to disallow deprecated properties/values. 1) Which Stylelint rule disallows the deprecated property? - Use Stylelint’s property-no-deprecated rule. It flags deprecated properties; its own example marks “a { word-wrap: break-word; }” as a deprecated property, and it does not flag “overflow-wrap: break-word” as a problem (showing the intended replacement category). [1][2] 2) What replacement should you use? - The Stylelint rule example indicates “overflow-wrap” is acceptable, and the deprecated “word-wrap” is the one to remove. [1][2] - Also, the MDN overflow-wrap page defines the “anywhere” keyword and explains how it differs from “break-word”, but it does not mention “word-wrap” directly; it does provide the modern mechanism for breaking long unbreakable strings via overflow-wrap: anywhere/break-word. [3] 3) What about “overflow-wrap-anywhere break-word” vs “word-wrap property disallowed deprecated”? - Stylelint property-no-deprecated explicitly targets deprecated properties (like word-wrap) rather than the particular overflow-wrap keywords. It will allow overflow-wrap even when set to break-word (per its “not considered problems” example). [1][2] - If your real goal is to enforce a specific keyword (e.g., require overflow-wrap: anywhere, disallow break-word), Stylelint’s built-in rule set you’d need is different from property-no-deprecated because property-no-deprecated doesn’t enforce specific overflow-wrap values. (You can restrict values via other rules such as declaration-property-value-no-deprecated/blacklists/allowed-lists, but those are separate rules from property-no-deprecated.) [4][1] Practical configuration (to disallow word-wrap) - Add: "rules": { "property-no-deprecated": true } This will flag “word-wrap: break-word” and similar deprecated properties. [1][2] Notes on “deprecated deprecated word-wrap” nuance - Stylelint property-no-deprecated also has ignoreProperties to exempt specific deprecated properties if needed. [1][2] If you tell me whether you want to (a) only disallow the property word-wrap, or (b) also force overflow-wrap: anywhere specifically (and forbid overflow-wrap: break-word), I can point to the exact Stylelint rule(s) to enforce keyword-level policy.
Citations:
- 1: https://stylelint.io/user-guide/rules/property-no-deprecated
- 2: https://stylelint.io/user-guide/rules/property-no-deprecated/
- 3: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/overflow-wrap
- 4: https://stylelint.io/user-guide/rules
🌐 Web query:
CSS word-break: break-word deprecation overflow-wrap anywhere break-word
💡 Result:
The CSS value word-break: break-word is officially deprecated [1][2]. It was originally a non-standard, browser-specific addition that served as a legacy alias [3][4]. Current Status and Recommendation While it remains supported in major browsers for backward compatibility, developers are strongly encouraged to stop using it in new code [3][2][4]. The CSS Working Group added it to the specification temporarily with a note indicating it is deprecated [5][6], advising the use of the standardized overflow-wrap property instead [4]. Comparison of Approaches - word-break: break-word (Deprecated): Effectively acts as a combination of word-break: normal and overflow-wrap: anywhere, regardless of the explicit overflow-wrap value [1][2][7]. - overflow-wrap: anywhere: This is the recommended modern replacement [4]. It prevents overflow by breaking otherwise unbreakable strings (like long URLs or words) at any point if no other break opportunities exist [8]. Unlike the deprecated value, it correctly considers soft wrap opportunities when calculating min-content intrinsic sizes, which helps prevent issues in intrinsic sizing contexts like flex or grid containers [4][7][8]. - overflow-wrap: break-word: Similar to overflow-wrap: anywhere, but it does not consider soft wrap opportunities when calculating min-content intrinsic sizes [8]. In summary, for modern CSS, use overflow-wrap: anywhere to handle text overflow for long, unbreakable strings while ensuring consistent layout behavior [4][8].
Citations:
- 1: https://github.com/mdn/content/blob/main/files/en-us/web/css/reference/properties/word-break/index.md
- 2: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/word-break
- 3: https://stackoverflow.com/questions/47413677/alternative-for-word-break-break-word
- 4: https://deku.posstree.com/en/web/css-overflow-wrap/
- 5: https://lists.w3.org/Archives/Public/public-css-archive/2019Feb/0655.html
- 6: [css-text-3] Normatively disallow break-word w3c/csswg-drafts#4087
- 7: https://stackoverflow.com/questions/56618668/do-overflow-wrap-break-word-and-word-break-break-word-ever-behave-differen
- 8: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/overflow-wrap
🌐 Web query:
stylelint-config-standard-scss keyframes-name-pattern slide-in naming
💡 Result:
In stylelint, the rule keyframes-name-pattern (enabled via stylelint-config-standard-scss) enforces that @keyframes names must be kebab-case. For example, a keyframes name using interpolation like frame-#{$name} can trigger a false positive saying “Expected keyframe name to be kebab-case” even when the interpolated result would be kebab-case, which was reported and addressed as a Stylelint bug/issue. [1][2] So for a keyframes animation like “slide-in” (as in @keyframes slide-in), the expected naming style under keyframes-name-pattern is kebab-case (e.g., slide-in, fade-in), not camelCase (e.g., slideIn). [2][3][4]
Citations:
- 1: keyframes-name-pattern false positive with string interpolation stylelint-scss/stylelint-config-standard-scss#16
- 2: Fix false positives for interpolation in keyframes-name-pattern stylelint/stylelint#5997
- 3: https://stylelint.io/user-guide/rules
- 4: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes
🌐 Web query:
stylelint-config-standard-scss repository keyframes-name-pattern value
💡 Result:
The keyframes-name-pattern rule is a standard Stylelint rule that enforces a naming convention for CSS @keyframes [1]. In the context of stylelint-config-standard-scss, this rule has historically been used to enforce kebab-case naming for keyframes [2]. However, it is important to note that the keyframes-name-pattern rule has been removed from newer versions of Stylelint configs to rely on the core Stylelint defaults or because it was deemed redundant [3]. Specifically, recent updates have deprecated or removed standalone pattern rules (such as keyframes-name-pattern, selector-class-pattern, etc.) in favor of core Stylelint configurations, which now handle these naming conventions directly [3]. If you are encountering this rule or need to define it, the typical configuration value used to enforce kebab-case is: "keyframes-name-pattern": "^([a-z][a-z0-9])(-[a-z0-9]+)$" This regular expression ensures that the keyframe name follows the kebab-case naming convention [2]. If you are experiencing issues with this rule—such as false positives when using SCSS string interpolation—it is often recommended to ensure you are using the latest version of Stylelint, as upstream fixes have been implemented to better handle non-standard syntax [2][4].
Citations:
- 1: https://stylelint.io/user-guide/rules
- 2: keyframes-name-pattern false positive with string interpolation stylelint-scss/stylelint-config-standard-scss#16
- 3: https://github.com/jhae-de/stylelint-config-standard-scss/blob/main/CHANGELOG.md
- 4: Fix false positives for interpolation in keyframes-name-pattern stylelint/stylelint#5997
Fix deprecated CSS text-wrapping + kebab-case keyframe naming in TodoItem.css
todo-list/src/components/TodoItem/TodoItem.css still uses deprecated word-break: break-word (line 62) and word-wrap: break-word (line 87), plus camelCase keyframe naming (animation: slideIn at line 10 and @keyframes slideIn at line 199). Replace with modern overflow-wrap and rename the keyframes to kebab-case.
Proposed fix
- animation: slideIn 0.3s ease-out;
+ animation: slide-in 0.3s ease-out;
...
- word-break: break-word;
+ overflow-wrap: anywhere;
...
- word-wrap: break-word;
+ overflow-wrap: break-word;
...
-@keyframes slideIn {
+@keyframes slide-in {🧰 Tools
🪛 Stylelint (17.12.0)
[error] 62-62: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)
(declaration-property-value-keyword-no-deprecated)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/components/TodoItem/TodoItem.css` at line 62, Replace
deprecated word-break: break-word and word-wrap: break-word in TodoItem.css with
the modern overflow-wrap value (e.g., overflow-wrap: anywhere or overflow-wrap:
break-word) to handle text wrapping, and rename the animation/keyframes from
camelCase slideIn to kebab-case slide-in; update the animation declaration
(animation: slide-in ...) and the `@keyframes` rule (`@keyframes` slide-in { ... })
accordingly so both the usage and definition match.
| font-weight: 600; | ||
| } | ||
|
|
||
| @keyframes fadeIn { |
There was a problem hiding this comment.
Rename keyframe to satisfy configured lint rule.
Line 75 uses fadeIn, but Stylelint expects kebab-case keyframe names. Rename the keyframe and update the animation reference (Line 6 in the component stylesheet usage context for this file is animation: fadeIn ... at Line 6 of this CSS file).
Suggested fix
-.todo-stats {
+.todo-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 12px;
margin-bottom: 25px;
- animation: fadeIn 0.6s ease-out 0.2s both;
+ animation: fade-in 0.6s ease-out 0.2s both;
}
@@
-@keyframes fadeIn {
+@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @keyframes fadeIn { | |
| .todo-stats { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); | |
| gap: 12px; | |
| margin-bottom: 25px; | |
| animation: fade-in 0.6s ease-out 0.2s both; | |
| } | |
| `@keyframes` fade-in { | |
| from { | |
| opacity: 0; | |
| } | |
| to { | |
| opacity: 1; | |
| } | |
| } |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 75-75: Expected keyframe name "fadeIn" to be kebab-case (keyframes-name-pattern)
(keyframes-name-pattern)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/components/TodoStats/TodoStats.css` at line 75, The keyframe
name uses camelCase "fadeIn" which violates the kebab-case lint rule; rename the
`@keyframes` rule from "fadeIn" to a kebab-case name like "fade-in" and update
every animation usage that references it (e.g., the CSS declaration "animation:
fadeIn ..." on Line 6) to "animation: fade-in ..."; also update any other
occurrences (animation-name or shorthand) in the TodoStats stylesheet to match
the new "fade-in" identifier.
| import App from './App' | ||
| import './index.css' | ||
|
|
||
| ReactDOM.createRoot(document.getElementById('root')!).render( |
There was a problem hiding this comment.
Add defensive null check for root element.
The non-null assertion operator assumes the 'root' element always exists. If the HTML file is modified incorrectly in the future, this will cause a runtime error.
🛡️ Proposed fix with null check
-ReactDOM.createRoot(document.getElementById('root')!).render(
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+ throw new Error('Root element not found');
+}
+ReactDOM.createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ReactDOM.createRoot(document.getElementById('root')!).render( | |
| const rootElement = document.getElementById('root'); | |
| if (!rootElement) { | |
| throw new Error('Root element not found'); | |
| } | |
| ReactDOM.createRoot(rootElement).render( | |
| <React.StrictMode> | |
| <App /> | |
| </React.StrictMode>, | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/index.tsx` at line 6,
ReactDOM.createRoot(document.getElementById('root')!).render(...) uses a
non-null assertion on document.getElementById('root'); change this to first
assign const rootEl = document.getElementById('root'), check if rootEl is null
and handle gracefully (e.g., log error and return or throw a clear error) before
calling ReactDOM.createRoot(rootEl). This ensures ReactDOM.createRoot and render
are only called when the 'root' element actually exists.
| return sorted.sort((a, b) => { | ||
| if (!a.dueDate) return 1 | ||
| if (!b.dueDate) return -1 | ||
| return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime() |
There was a problem hiding this comment.
Handle the “both due dates missing” case in comparator.
At Line 65, !a.dueDate returns 1 even when both are missing, making comparisons inconsistent and producing unstable sort results.
Suggested fix
case 'dueDate':
return sorted.sort((a, b) => {
+ if (!a.dueDate && !b.dueDate) return 0
if (!a.dueDate) return 1
if (!b.dueDate) return -1
return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return sorted.sort((a, b) => { | |
| if (!a.dueDate) return 1 | |
| if (!b.dueDate) return -1 | |
| return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime() | |
| return sorted.sort((a, b) => { | |
| if (!a.dueDate && !b.dueDate) return 0 | |
| if (!a.dueDate) return 1 | |
| if (!b.dueDate) return -1 | |
| return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime() | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/utils/helpers.ts` around lines 64 - 67, The comparator passed
to sorted.sort incorrectly returns 1 when a.dueDate is falsy even if both
a.dueDate and b.dueDate are missing, causing unstable sorts; update the
comparator in the sorted.sort callback to explicitly handle all three cases: if
both a.dueDate and b.dueDate are missing return 0, if only a.dueDate is missing
return 1, if only b.dueDate is missing return -1, and otherwise return the
numeric difference of new Date(a.dueDate).getTime() - new
Date(b.dueDate).getTime(); ensure the comparator always returns a number so sort
stability is preserved.
| const data = localStorage.getItem(STORAGE_KEY) | ||
| return data ? JSON.parse(data) : [] |
There was a problem hiding this comment.
Validate persisted/imported payload before treating it as Todo[].
JSON.parse output is trusted as Todo[] without validation. A malformed array/object can break render paths that assume title, priority, completed, etc. exist.
Suggested fix
+const isTodo = (value: unknown): value is Todo => {
+ if (!value || typeof value !== 'object') return false
+ const t = value as Partial<Todo>
+ return (
+ typeof t.id === 'string' &&
+ typeof t.title === 'string' &&
+ typeof t.completed === 'boolean' &&
+ (t.priority === 'low' || t.priority === 'medium' || t.priority === 'high') &&
+ typeof t.createdAt === 'string' &&
+ typeof t.updatedAt === 'string'
+ )
+}
+
export const storageUtils = {
@@
- return data ? JSON.parse(data) : []
+ const parsed = data ? JSON.parse(data) : []
+ return Array.isArray(parsed) ? parsed.filter(isTodo) : []
@@
- const todos = JSON.parse(jsonData)
- if (Array.isArray(todos)) {
- storageUtils.saveTodos(todos)
+ const parsed = JSON.parse(jsonData)
+ if (Array.isArray(parsed)) {
+ const todos = parsed.filter(isTodo)
+ storageUtils.saveTodos(todos)
return true
}Also applies to: 62-67
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@todo-list/src/utils/storage.ts` around lines 9 - 10, The code trusts
JSON.parse(localStorage.getItem(STORAGE_KEY)) as Todo[]; instead add robust
validation and safe parsing: wrap JSON.parse in try/catch, ensure the parsed
value is an array (Array.isArray), and filter/map items using a type-guard that
checks required Todo properties (e.g., title is string, priority is one of
expected values, completed is boolean, id if present is string/number) before
returning; return [] as a safe fallback for any malformed or non-array payload.
Apply the same pattern where JSON.parse is used elsewhere (the other block
handling STORAGE_KEY at the later section) so both imports and persisted data
are validated before being treated as Todo[].
Summary by CodeRabbit