Skip to content

Dev include bug #2

Open
wants to merge 7 commits into
base: replay-2-base-d7ff49b
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
40 changes: 40 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local
.env
Comment on lines +27 to +29

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if environment template files exist in the repository

# Search for .env.example, .env.template, or similar files
fd -t f '^\.env\.(example|template|sample)$'

Repository: admin-coderabbit/todo-review-test

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Description: Check if environment template files exist in the repository (including hidden files)

# Search for .env.example, .env.template, .env.sample, or similar files
fd -H -t f '\.env\.(example|template|sample)' 

# Also check for any .env* files to see what exists
echo "=== All .env* files in repository ==="
fd -H -t f '^\.env'

Repository: admin-coderabbit/todo-review-test

Length of output: 114


Add a .env.example or .env.template file to the repository.

The repository correctly ignores .env files for security, but developers need a template showing required environment variables. Add a .env.example file with placeholder values for all necessary environment variables.

🤖 Prompt for AI Agents
In @.gitignore around lines 27 - 29, The repo ignores .env and .env*.local (see
.env*.local and .env in the diff) but lacks a template for required variables;
add a new file named .env.example (or .env.template) to the repository
containing all required environment variable names with safe placeholder values
and brief inline comments describing each one, ensure the example is committed
(not ignored) and kept in sync with any changes to how the app reads env vars
(e.g., variables referenced in startup code or config).


# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# prisma
/prisma/*.db
/prisma/*.db-journal
98 changes: 97 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,97 @@
# todo-review-test
# TODO管理アプリ

簡易的なTODO管理Webアプリケーションです。

## 技術スタック

- **フレームワーク**: Next.js 14 (App Router)
- **言語**: TypeScript
- **データベース**: SQLite (Prisma ORM)
- **スタイリング**: Tailwind CSS
- **テスト**: Vitest + Testing Library

## 機能

- TODOの追加
- TODOの一覧表示
- TODOの完了/未完了の切り替え
- TODOの削除
- 完了済みTODOと未完了TODOの分類表示

## セットアップ

### 1. 依存関係のインストール

```bash
npm install
```

### 2. 環境変数の設定

`.env`ファイルを作成し、以下の内容を追加してください:

```
DATABASE_URL="file:./prisma/dev.db"
```
Comment on lines +33 to +35

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language specifier to fenced code block.

The static analysis tool flagged this code block as missing a language specifier. Consider adding dotenv or text for syntax highlighting and linter compliance.

📝 Proposed fix
-```
+```dotenv
 DATABASE_URL="file:./prisma/dev.db"
</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.18.1)

33-33: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@README.md` around lines 33 - 35, Update the fenced code block around the
DATABASE_URL example to include a language specifier (e.g., use "dotenv" or
"text") so the linter and syntax highlighter recognize it; locate the
triple-backtick block containing DATABASE_URL="file:./prisma/dev.db" and change
the opening fence to ```dotenv (or ```text) while leaving the content unchanged.


### 3. データベースの初期化

```bash
npm run db:push
npm run db:generate
```

### 4. 開発サーバーの起動

```bash
npm run dev
```

ブラウザで [http://localhost:3000](http://localhost:3000) を開いてください。

## スクリプト

- `npm run dev` - 開発サーバーを起動
- `npm run build` - プロダクションビルド
- `npm run start` - プロダクションサーバーを起動
- `npm run lint` - ESLintでコードをチェック
- `npm run test` - テストを実行
- `npm run test:watch` - ウォッチモードでテストを実行
- `npm run db:push` - データベーススキーマをプッシュ
- `npm run db:generate` - Prismaクライアントを生成
- `npm run db:studio` - Prisma Studioを起動

## プロジェクト構造

```
todo-review-test/
├── app/
│ ├── api/
│ │ └── todos/ # TODO APIエンドポイント
│ ├── globals.css # グローバルスタイル
│ ├── layout.tsx # ルートレイアウト
│ └── page.tsx # ホームページ
├── components/
│ ├── TodoForm.tsx # TODO追加フォーム
│ ├── TodoList.tsx # TODO一覧
│ └── TodoItem.tsx # TODOアイテム
├── lib/
│ └── prisma.ts # Prismaクライアント
├── prisma/
│ └── schema.prisma # データベーススキーマ
└── tests/ # テストファイル
```
Comment on lines +66 to +83

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language specifier to fenced code block.

The project structure code block is missing a language specifier. Use text for directory tree structures.

📝 Proposed fix
-```
+```text
 todo-review-test/
 ├── app/
📝 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.

Suggested change
```
todo-review-test/
├── app/
│ ├── api/
│ │ └── todos/ # TODO APIエンドポイント
│ ├── globals.css # グローバルスタイル
│ ├── layout.tsx # ルートレイアウト
│ └── page.tsx # ホームページ
├── components/
│ ├── TodoForm.tsx # TODO追加フォーム
│ ├── TodoList.tsx # TODO一覧
│ └── TodoItem.tsx # TODOアイテム
├── lib/
│ └── prisma.ts # Prismaクライアント
├── prisma/
│ └── schema.prisma # データベーススキーマ
└── tests/ # テストファイル
```
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

66-66: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@README.md` around lines 66 - 83, Update the fenced code block in README.md
that shows the project tree (starts with the line "todo-review-test/" and the
tree lines like "├── app/") to include a language specifier by changing the
opening backticks from ``` to ```text so the directory structure is rendered
correctly as plain text.


## 拡張性

このアプリケーションは以下の点で拡張しやすく設計されています:

- **型安全性**: TypeScriptとPrismaによる完全な型安全性
- **モジュール設計**: コンポーネントとAPIルートの分離
- **データベース**: SQLiteからPostgreSQLなどへの移行が容易
- **テスト**: 包括的なテストカバレッジ
- **バリデーション**: Zodによる入力検証

## ライセンス

MIT
101 changes: 101 additions & 0 deletions app/api/todos/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { z } from 'zod'

const updateTodoSchema = z.object({
title: z.string().min(1, 'タイトルは必須です').max(200, 'タイトルは200文字以内で入力してください').optional(),
description: z.string().max(1000, '説明は1000文字以内で入力してください').optional(),
completed: z.boolean().optional(),
})

// GET: 特定のTODOを取得
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const todo = await prisma.todo.findUnique({
where: { id },
})

if (!todo) {
return NextResponse.json(
{ error: 'TODOが見つかりません' },
{ status: 404 }
)
}

return NextResponse.json(todo)
} catch (error) {
console.error('Error fetching todo:', error)
return NextResponse.json(
{ error: 'TODOの取得に失敗しました' },
{ status: 500 }
)
}
}

// PATCH: TODOを更新
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const body = await request.json()
const validatedData = updateTodoSchema.parse(body)

const todo = await prisma.todo.update({
where: { id },
data: validatedData,
})

return NextResponse.json(todo)
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: error.errors[0].message },
{ status: 400 }
)
}
if (error instanceof Error && error.message.includes('Record to update not found')) {
return NextResponse.json(
{ error: 'TODOが見つかりません' },
{ status: 404 }
)
}
console.error('Error updating todo:', error)
return NextResponse.json(
{ error: 'TODOの更新に失敗しました' },
{ status: 500 }
)
}
}

// DELETE: TODOを削除
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
await prisma.todo.delete({
where: { id },
})

return NextResponse.json({ message: 'TODOを削除しました' })
} catch (error) {
if (error instanceof Error && error.message.includes('Record to delete does not exist')) {
return NextResponse.json(
{ error: 'TODOが見つかりません' },
{ status: 404 }
)
}
console.error('Error deleting todo:', error)
return NextResponse.json(
{ error: 'TODOの削除に失敗しました' },
{ status: 500 }
)
}
}
55 changes: 55 additions & 0 deletions app/api/todos/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
import { z } from 'zod'

const todoSchema = z.object({
title: z.string().min(1, 'タイトルは必須です').max(200, 'タイトルは200文字以内で入力してください'),
description: z.string().max(1000, '説明は1000文字以内で入力してください').optional(),
})

// GET: すべてのTODOを取得
export async function GET() {
try {
const todos = await prisma.todo.findMany({
orderBy: {
createdAt: 'desc',
},
})
return NextResponse.json(todos)
} catch (error) {
console.error('Error fetching todos:', error)
return NextResponse.json(
{ error: 'TODOの取得に失敗しました' },
{ status: 500 }
)
}
}

// POST: 新しいTODOを作成
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const validatedData = todoSchema.parse(body)

const todo = await prisma.todo.create({
data: {
title: validatedData.title,
description: validatedData.description,
},
})

return NextResponse.json(todo, { status: 201 })
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: error.errors[0].message },
{ status: 400 }
)
}
console.error('Error creating todo:', error)
return NextResponse.json(
{ error: 'TODOの作成に失敗しました' },
{ status: 500 }
)
}
}
27 changes: 27 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--background: #ffffff;
--foreground: #171717;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}
19 changes: 19 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
title: "TODO管理アプリ",
description: "簡易的なTODO管理Webアプリケーション",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja">
<body>{children}</body>
</html>
);
}
20 changes: 20 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import TodoList from '@/components/TodoList'
import TodoForm from '@/components/TodoForm'

export default function Home() {
return (
<main className="min-h-screen bg-gray-50 py-8">
<div className="max-w-2xl mx-auto px-4">
<h1 className="text-3xl font-bold text-gray-900 mb-8 text-center">
TODO管理アプリ
</h1>
<div className="bg-white rounded-lg shadow-md p-6">
<TodoForm />
<div className="mt-8">
<TodoList />
</div>
</div>
</div>
</main>
)
}
Loading