# 🎯 Example: Week 7 D2 - Todo App (HTML/CSS/JS/SQLite)

## 📌 บทนำตัวอย่าง

ไฟล์นี้แสดงตัวอย่างจริงจังของการทำภารกิจ Week 7 (D2) โดยใช้:

- **Frontend:** HTML5, CSS3, Vanilla JavaScript
- **Backend:** Node.js + Express (optional)
- **Database:** SQLite3
- **Project:** Todo Application

เราจะทำตามขั้นตอน **ทีละขั้นตอน** เหมือนคู่มือ wk07-th-doing-step-by-step.md

---

## 🎯 ภารกิจ 7.1: Coding Standards + Git Setup

### ขั้นตอนที่ 1️⃣: Coding Standards สำหรับ Todo App

#### 1.1 สร้างไฟล์ Coding_Standards.md

```markdown
# Coding Standards - Todo App

## 1. HTML Naming Conventions

### IDs (PascalCase + Clear Purpose)

✓ Good:

- id="todoContainer"
- id="addTodoForm"
- id="todoList"

✗ Bad:

- id="tc"
- id="form1"
- id="list"

### Classes (kebab-case + BEM)

✓ Good:

- class="todo-list"
- class="todo-item"
- class="todo-item\_\_checkbox" ← Element (BEM)
- class="todo-item\_\_title" ← Element (BEM)
- class="todo-item--completed" ← Modifier (BEM)
- class="btn btn-primary"
- class="form-group"
- class="empty-state"
- class="empty-state\_\_text" ← Element (BEM)
- class="empty-state--visible" ← Modifier (BEM)

✗ Bad:

- class="todoItem" ← camelCase ❌
- class="todo_item" ← snake_case ❌
- class="todoItemCheckbox" ← camelCase ❌
- class="todo-item-completed" ← Wrong modifier format ❌
- class="active" ← Too generic ❌

### Data Attributes

✓ Good:

- data-todo-id="123"
- data-status="completed"
- data-action="delete"

✗ Bad:

- data-id="123"
- data-status="1"

## 2. CSS Naming Conventions

### Classes (kebab-case + BEM)

**BEM = Block, Element, Modifier**

Block: .todo-list (standalone component)
Element: .todo-list**item (part of block, **)
Modifier: .todo-list--active (state/variant, --)

Pattern: .block\_\_element--modifier
.block--modifier
```

**Examples:**

✓ Good (BEM):

```css
/* Block */
.todo-list {
}

/* Element (depend on block) */
.todo-list__item {
}
.todo-list__item__title {
}
.todo-list__item__description {
}

/* Modifier (state/variant) */
.todo-list--empty {
}
.todo-item--completed {
}
.todo-item--active {
}

/* Multiple modifiers */
.todo-item--completed--archived {
}

/* Reusable blocks */
.btn {
}
.btn--primary {
}
.btn--danger {
}
.btn__text {
}

.form-group {
}
.form-group__label {
}
.form-group__input {
}
.form-group--error {
}

.empty-state {
}
.empty-state__icon {
}
.empty-state__text {
}
.empty-state--visible {
}
```

✗ Bad:

```css
.todoList {
} /* camelCase ❌ */
.TodoItem {
} /* PascalCase ❌ */
.todo_item {
} /* snake_case ❌ */
.todo-item-completed {
} /* Wrong modifier format ❌ */
.todo-item-title {
} /* Should be __ not - ❌ */
.active {
} /* Too generic, no context ❌ */
.completed-item {
} /* Order wrong (modifier-block) ❌ */
```

**Usage in HTML:**

✓ Good:

```html
<ul class="todo-list">
  <li class="todo-item todo-item--completed">
    <input class="todo-item__checkbox" type="checkbox" />
    <div class="todo-item__content">
      <h3 class="todo-item__title">Learn JavaScript</h3>
      <p class="todo-item__description">Complete basics</p>
    </div>
    <div class="todo-item__actions">
      <button class="btn btn--danger btn__text">Delete</button>
    </div>
  </li>
</ul>

<div class="empty-state empty-state--visible">
  <p class="empty-state__text">No todos yet</p>
</div>

<form class="form-group form-group--error">
  <label class="form-group__label">Title</label>
  <input class="form-group__input" type="text" />
  <span class="form-group__error">Required field</span>
</form>
```

✗ Bad:

```html
<!-- Missing BEM structure -->
<ul class="list">
  <li class="item active">
    <input class="checkbox" />
    <h3 class="title">Learn JavaScript</h3>
    <button class="danger-btn">Delete</button>
  </li>
</ul>

<!-- Inconsistent naming -->
<div class="empty_state is-visible">
  <p class="empty-state-text">No todos</p>
</div>
```

### File Organization

```

css/
├── base/
│ ├── reset.css
│ ├── typography.css
│ └── variables.css
├── components/
│ ├── buttons.css
│ ├── forms.css
│ └── todo-item.css
├── layout/
│ ├── header.css
│ ├── container.css
│ └── footer.css
└── main.css

```

### Color & Sizing Variables

✓ Good:

```css
:root {
  --color-primary: #007bff;
  --color-success: #28a745;
  --color-danger: #dc3545;
  --spacing-unit: 8px;
  --border-radius: 4px;
}
```

✗ Bad:

```css
.btn {
  color: #007bff;
}
.success {
  color: green;
}
.space {
  margin: 8px;
}
```

## 3. JavaScript Naming Conventions

### Variables (camelCase)

✓ Good:

```javascript
- const todoList = [];
- let isCompleted = false;
- let itemCount = 0;
```

✗ Bad:

```javascript
- const todo_list = [];
- let IsCompleted = false;
- let count = 0;
```

### Functions (camelCase + Verb)

✓ Good:

```javascript
-function addTodo() {} -
  function deleteTodo(id) {} -
  function toggleTodoStatus(id) {} -
  function getTodosFromStorage() {};
```

✗ Bad:

```javascript
- function add() {}
- function todo(id) {}
- function switch(id) {}
```

### Constants (UPPER_SNAKE_CASE)

✓ Good:

```javascript
- const MAX_TODO_ITEMS = 100;
- const STORAGE_KEY = 'todos';
- const API_URL = 'http://localhost:3000/api';
```

✗ Bad:

```javascript
- const maxTodos = 100;
- const key = 'todos';
- const url = 'http://localhost:3000/api';
```

### Object Properties (camelCase)

✓ Good:

```javascript
const todo = {
  id: 1,
  title: "Learn JavaScript",
  description: "Complete todo app",
  isCompleted: false,
  createdAt: new Date(),
  completedAt: null,
};
```

### Class Names (PascalCase)

✓ Good:

```javascript
class TodoManager {}
class TodoRenderer {}
class StorageService {}
```

## 4. File Organization

### JavaScript Structure

```
js/
├── services/
│   ├── storage-service.js
│   ├── api-service.js
│   └── validation-service.js
├── utils/
│   ├── date-formatter.js
│   ├── dom-helpers.js
│   └── string-helpers.js
├── components/
│   ├── todo-form.js
│   ├── todo-item.js
│   └── todo-list.js
└── app.js
```

## 5. Code Style

### Indentation & Spacing

- Use 4 spaces (or 2 spaces, consistent)
- Space around operators: const x = 5 + 3;
- Space after keywords: if (condition) {}
- Space inside braces: { key: value }

### Comments & Documentation

✓ Good:

```javascript
/**
 * Add new todo to list
 * @param {string} title - Todo title
 * @param {string} description - Todo description
 * @returns {Object} Created todo object
 */
function addTodo(title, description) {
  // Validate inputs
  if (!title || title.trim() === "") {
    throw new Error("Title cannot be empty");
  }
  // ... implementation
}
```

## 6. Error Handling

### Custom Error Messages

✓ Good:

```javascript
class TodoError extends Error {
  constructor(message) {
    super(message);
    this.name = "TodoError";
  }
}

try {
  if (!todo) {
    throw new TodoError("Todo not found");
  }
} catch (error) {
  console.error("Error retrieving todo:", error.message);
  displayErrorNotification(error.message);
}
```

## 7. Testing Standards

### Unit Test Naming

```javascript
describe("TodoManager", () => {
  describe("addTodo", () => {
    test("should add todo with valid title", () => {
      // Test implementation
    });

    test("should throw error when title is empty", () => {
      // Test implementation
    });
  });
});
```

## 8. Code Review Checklist

### การตั้งชื่อและโครงสร้าง

- [ ] ปฏิบัติตามมาตรฐานการตั้งชื่อ (Naming Conventions)
- [ ] ไม่มีค่าที่ hardcoded (ใช้ constants แทน)
- [ ] ไม่มี magic numbers (0, 1, 100 ที่ไม่มีความหมาย)
- [ ] โครงสร้างโฟลเดอร์เป็นระเบียบ

### Error Handling & Logging

- [ ] มีการ handle errors อย่างเหมาะสม
- [ ] มีคำอธิบายสำหรับ logic ที่ซับซ้อน
- [ ] ไม่มี console.log() ในโค้ด production
- [ ] มี logging สำหรับ errors ที่สำคัญ

### Data & API

- [ ] ไม่มี data hardcoded (ใช้ API/database แทน)
- [ ] ไม่มี credentials/secrets ใน code
- [ ] Input validation present

### UI/UX & Accessibility

- [ ] ทดสอบการออกแบบแบบ responsive (mobile, tablet, desktop)
- [ ] มี ARIA labels สำหรับผู้พิการ
- [ ] Keyboard navigation ทำงานได้
- [ ] Focus states ชัดเจน

### Performance & Optimization

- [ ] มี debouncing/throttling ที่จำเป็น
- [ ] ไม่มี N+1 queries
- [ ] Performance optimized (caching, lazy loading)

### Security

- [ ] ตรวจสอบ input validation
- [ ] ป้องกัน XSS (Cross-Site Scripting)
- [ ] ป้องกัน SQL injection
- [ ] No exposed secrets/API keys

---

## Team Approval

✓ Approved by:

- Frontend Lead: John Doe - 2025-01-15
- Backend Lead: Jane Smith - 2025-01-15
- Tech Lead: Bob Wilson - 2025-01-15

```

✅ **Checklist ส่วนนี้:**
- [x] Coding_Standards.md เขียนเสร็จ
- [ ] HTML, CSS, JS naming standards ชัดเจน
- [ ] File organization structure กำหนด
```

---

### ขั้นตอนที่ 2️⃣: ตั้งค่า Git Repository

#### 2.1 สร้าง .gitignore

```
# .gitignore

# Node.js

node_modules/
npm-debug.log
yarn-error.log
package-lock.json

# Database

_.db
_.sqlite
\*.sqlite3

# Environment

.env
.env.local
.env.\*.local

# IDE

.vscode/
.idea/
_.swp
_.swo
_.sublime-project
_.sublime-workspace

# OS

.DS_Store
Thumbs.db

# Dist & Build

dist/
build/
_.min.js
_.min.css

# Logs

logs/
\*.log

```

#### 2.2 สร้าง README.md

```markdown
# Todo Application

A simple and elegant todo app built with HTML5, CSS3, JavaScript, and SQLite.

## Features

- ✅ Add, edit, delete todos
- ✅ Mark todos as complete/incomplete
- ✅ Filter by status (All, Active, Completed)
- ✅ Local storage persistence
- ✅ Responsive design
- ✅ Dark mode support

## Tech Stack

- **Frontend:** HTML5, CSS3, Vanilla JavaScript
- **Backend:** Node.js, Express (optional)
- **Database:** SQLite3
- **Tools:** Webpack (optional), Jest (testing)

## Prerequisites

- Node.js 14+
- npm or yarn
- SQLite3
```

## Installation

1. Clone repository

```
git clone https://github.com/yourname/todo-app.git
cd todo-app
```

2. Install dependencies

```
npm install
```

3. Setup database

```
npm run db:init
```

4. Start development server

```
npm run dev
```

Application runs at: http://localhost:3000

## Project Structure

```
todo-app/
├── src/
│   ├── html/
│   │   └── index.html
│   ├── css/
│   │   ├── base/
│   │   ├── components/
│   │   └── main.css
│   ├── js/
│   │   ├── services/
│   │   ├── components/
│   │   └── app.js
│   └── server.js
├── database/
│   ├── schema.sql
│   └── seeds.sql
├── tests/
│   └── app.test.js
├── .gitignore
├── package.json
└── README.md
```

## Development

### Run tests

```bash
npm test
```

### Code quality

```bash
npm run lint
npm run format
```

### Build for production

```bash
npm run build
```

## API Endpoints

### GET /api/todos

Get all todos

```bash
curl http://localhost:3000/api/todos
```

### POST /api/todos

Create new todo

```bash
curl -X POST http://localhost:3000/api/todos \\
  -H "Content-Type: application/json" \\
  -d '{"title":"Learn JS","description":"Complete todo app"}'
```

### PUT /api/todos/:id

Update todo

```bash
curl -X PUT http://localhost:3000/api/todos/1 \\
  -H "Content-Type: application/json" \\
  -d '{"isCompleted":true}'
```

### DELETE /api/todos/:id

Delete todo

```bash
curl -X DELETE http://localhost:3000/api/todos/1
```

## Coding Standards

See [Coding_Standards.md](Coding_Standards.md)

## Git Workflow

1. Create feature branch: \`git checkout -b feature/add-categories\`
2. Make changes
3. Commit: \`git commit -m "feat: add category support"\`
4. Push: \`git push origin feature/add-categories\`
5. Create PR on GitHub
6. Wait for 2+ approvals
7. Merge to develop

## Contributing

1. Follow Coding Standards
2. Write tests for new features
3. Update documentation
4. Create pull request

## License

MIT License

````

#### 2.3 สร้าง .github/pull_request_template.md

```markdown
## Description

[สั้น ๆ อธิบายการเปลี่ยนแปลง]

Example:

- Added category feature to todos
- Allows users to organize todos by category
- Includes create, edit, delete category functionality

## Related Issue

Closes #123

## Type of Change

- [ ] Bug fix
- [ ] New feature (todos, categories, filters)
- [ ] UI/UX improvement
- [ ] Documentation
- [ ] Performance optimization

## How to Test

1. npm install
2. npm run db:init
3. npm run dev
4. Go to http://localhost:3000
5. Create todo with category
6. Verify category displays correctly

## Screenshots (if UI change)

[Paste images here]

## Checklist

- [ ] Follows Coding Standards
- [ ] Added/updated tests
- [ ] All tests pass: npm test
- [ ] No console errors
- [ ] Responsive on mobile
- [ ] Updated README if needed
- [ ] No hardcoded values
- [ ] No security issues

## Performance Impact

- [ ] No impact
- [ ] Minor (< 10ms)
- [ ] Moderate (10-100ms)
- [ ] Major (> 100ms)

If moderate/major, please explain optimization strategy.
````

#### 2.4 สร้าง .github/workflows/ci.yml

```yaml
name: CI Pipeline

on:
  push:
    branches: [develop, main]
  pull_request:
    branches: [develop, main]

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x, 18.x]

    steps:
      - uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint --if-present

      - name: Run tests
        run: npm test

      - name: Check code coverage
        run: npm run coverage --if-present

      - name: Upload coverage
        uses: codecov/codecov-action@v2
        if: matrix.node-version == '18.x'
```

✅ **Checklist ส่วนนี้:**

- [x] .gitignore ตั้งค่า
- [x] README.md เขียน
- [x] PR template สร้าง
- [x] GitHub Actions CI ตั้งค่า

---

### ขั้นตอนที่ 3️⃣: สร้าง Code Skeleton

#### 3.1 Package.json

```json
{
  "name": "todo-app",
  "version": "1.0.0",
  "description": "A simple todo application with HTML, CSS, JavaScript, and SQLite",
  "main": "src/server.js",
  "scripts": {
    "start": "node src/server.js",
    "dev": "nodemon src/server.js",
    "db:init": "sqlite3 database.db < database/schema.sql",
    "db:seed": "sqlite3 database.db < database/seeds.sql",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "lint": "eslint src/",
    "format": "prettier --write 'src/**/*.{js,css,html}'",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "express": "^4.18.2",
    "sqlite3": "^5.1.6",
    "cors": "^2.8.5",
    "dotenv": "^16.0.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.20",
    "jest": "^29.0.0",
    "eslint": "^8.25.0",
    "prettier": "^2.7.1",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  },
  "engines": {
    "node": ">=14.0.0"
  }
}
```

#### 3.2 สร้างโครงสร้างโฟลเดอร์

```bash
mkdir -p src/{html,css/{base,components,layout},js/{services,utils,components}}
mkdir -p database
mkdir -p tests
```

#### 3.3 src/index.html

```html
<!DOCTYPE html>
<html lang="th">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Todo App - ทำรายการสิ่งที่ต้องทำ</title>

    <!-- CSS -->
    <link rel="stylesheet" href="css/main.css" />

    <!-- Favicon -->
    <link
      rel="icon"
      type="image/svg+xml"
      href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='75' font-size='75'>✓</text></svg>"
    />
  </head>
  <body>
    <div id="app" class="app-container">
      <!-- Header -->
      <header class="app-header">
        <div class="container">
          <h1 class="app-title">📝 Todo App</h1>
          <p class="app-subtitle">จัดการรายการสิ่งที่ต้องทำของคุณ</p>
        </div>
      </header>

      <!-- Main Content -->
      <main class="app-main">
        <div class="container">
          <!-- Add Todo Form -->
          <section class="add-todo-section">
            <form id="addTodoForm" class="add-todo-form">
              <div class="form-group">
                <input
                  type="text"
                  id="todoTitle"
                  class="input-text"
                  placeholder="ชื่องาน (เช่น: เรียน JavaScript)"
                  maxlength="100"
                  required
                />
              </div>
              <div class="form-group">
                <textarea
                  id="todoDescription"
                  class="input-textarea"
                  placeholder="รายละเอียด (ไม่จำเป็น)"
                  maxlength="500"
                  rows="3"
                ></textarea>
              </div>
              <button type="submit" class="btn btn-primary btn-lg">
                ➕ เพิ่มงาน
              </button>
            </form>
          </section>

          <!-- Filter Section -->
          <section class="filter-section">
            <div class="filter-buttons">
              <button
                class="btn btn-filter btn-filter--active"
                data-filter="all"
              >
                ทั้งหมด
              </button>
              <button class="btn btn-filter" data-filter="active">
                กำลังทำ
              </button>
              <button class="btn btn-filter" data-filter="completed">
                เสร็จแล้ว
              </button>
            </div>
            <div class="stats">
              <span id="todoCount" class="stats-item">ทั้งหมด: 0</span>
              <span id="completedCount" class="stats-item">เสร็จแล้ว: 0</span>
            </div>
          </section>

          <!-- Todo List -->
          <section class="todo-list-section">
            <ul id="todoList" class="todo-list" role="list">
              <!-- Items will be inserted here by JavaScript -->
            </ul>

            <!-- Empty State -->
            <div id="emptyState" class="empty-state">
              <p class="empty-state__text">ยังไม่มีงานใด ๆ 🎉</p>
              <p class="empty-state__subtext">เพิ่มงานใหม่เพื่อเริ่มต้น</p>
            </div>
          </section>
        </div>
      </main>

      <!-- Footer -->
      <footer class="app-footer">
        <p>&copy; 2025 Todo App. สร้างด้วย ❤️ สำหรับวิชา 682</p>
      </footer>
    </div>

    <!-- JavaScript -->
    <script src="js/app.js"></script>
  </body>
</html>
```

#### 3.4 src/css/main.css

```css
/* Reset & Base Styles */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --color-primary: #007bff;
  --color-primary-dark: #0056b3;
  --color-primary-light: #e7f1ff;

  --color-success: #28a745;
  --color-danger: #dc3545;
  --color-warning: #ffc107;
  --color-info: #17a2b8;

  --color-white: #ffffff;
  --color-black: #000000;
  --color-gray-50: #f9fafb;
  --color-gray-100: #f3f4f6;
  --color-gray-200: #e5e7eb;
  --color-gray-300: #d1d5db;
  --color-gray-400: #9ca3af;
  --color-gray-500: #6b7280;
  --color-gray-600: #4b5563;
  --color-gray-700: #374151;
  --color-gray-800: #1f2937;
  --color-gray-900: #111827;

  --spacing-unit: 8px;
  --spacing-xs: calc(var(--spacing-unit) * 0.5);
  --spacing-sm: var(--spacing-unit);
  --spacing-md: calc(var(--spacing-unit) * 2);
  --spacing-lg: calc(var(--spacing-unit) * 3);
  --spacing-xl: calc(var(--spacing-unit) * 4);
  --spacing-xxl: calc(var(--spacing-unit) * 6);

  --border-radius: 4px;
  --border-radius-lg: 8px;
  --border-radius-xl: 12px;

  --font-family-base:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  --font-family-mono:
    "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;

  --font-size-xs: 12px;
  --font-size-sm: 14px;
  --font-size-base: 16px;
  --font-size-lg: 18px;
  --font-size-xl: 20px;
  --font-size-2xl: 24px;
  --font-size-3xl: 32px;

  --font-weight-light: 300;
  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;

  --line-height-tight: 1.2;
  --line-height-normal: 1.5;
  --line-height-relaxed: 1.75;

  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);

  --transition-fast: 150ms ease-in-out;
  --transition-base: 300ms ease-in-out;
  --transition-slow: 500ms ease-in-out;
}

/* Dark Mode */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: var(--color-gray-900);
    --color-text: var(--color-gray-100);
    --color-border: var(--color-gray-700);
  }
}

html {
  font-size: 16px;
}

body {
  font-family: var(--font-family-base);
  font-size: var(--font-size-base);
  line-height: var(--line-height-normal);
  color: var(--color-gray-900);
  background-color: var(--color-gray-50);
}

/* Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  margin-bottom: var(--spacing-md);
}

h1 {
  font-size: var(--font-size-3xl);
}
h2 {
  font-size: var(--font-size-2xl);
}
h3 {
  font-size: var(--font-size-xl);
}

p {
  margin-bottom: var(--spacing-md);
}

/* Container */
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 0 var(--spacing-lg);
}

/* App Layout */
.app-container {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background-color: var(--color-white);
}

.app-header {
  background: linear-gradient(
    135deg,
    var(--color-primary) 0%,
    var(--color-primary-dark) 100%
  );
  color: var(--color-white);
  padding: var(--spacing-xl) 0;
  box-shadow: var(--shadow-md);
  text-align: center;
}

.app-title {
  font-size: var(--font-size-3xl);
  margin-bottom: var(--spacing-sm);
}

.app-subtitle {
  font-size: var(--font-size-lg);
  opacity: 0.9;
}

.app-main {
  flex: 1;
  padding: var(--spacing-xl) 0;
}

.app-footer {
  background-color: var(--color-gray-100);
  color: var(--color-gray-600);
  text-align: center;
  padding: var(--spacing-lg);
  font-size: var(--font-size-sm);
}

/* Forms */
.form-group {
  margin-bottom: var(--spacing-md);
}

.input-text,
.input-textarea {
  width: 100%;
  padding: var(--spacing-md) var(--spacing-lg);
  font-family: var(--font-family-base);
  font-size: var(--font-size-base);
  border: 1px solid var(--color-gray-300);
  border-radius: var(--border-radius);
  transition: all var(--transition-base);
}

.input-text:focus,
.input-textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-light);
}

.input-textarea {
  resize: vertical;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-md) var(--spacing-lg);
  font-family: var(--font-family-base);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  border: none;
  border-radius: var(--border-radius);
  cursor: pointer;
  transition: all var(--transition-base);
  text-decoration: none;
}

.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn:active {
  transform: translateY(0);
}

.btn-primary {
  background-color: var(--color-primary);
  color: var(--color-white);
}

.btn-primary:hover {
  background-color: var(--color-primary-dark);
}

.btn-danger {
  background-color: var(--color-danger);
  color: var(--color-white);
  padding: var(--spacing-sm);
}

.btn-danger:hover {
  background-color: #c82333;
}

.btn-lg {
  padding: var(--spacing-lg) var(--spacing-xxl);
  font-size: var(--font-size-lg);
  width: 100%;
}

.btn-filter {
  background-color: var(--color-gray-200);
  color: var(--color-gray-700);
  padding: var(--spacing-md) var(--spacing-xl);
}

.btn-filter--active {
  background-color: var(--color-primary);
  color: var(--color-white);
}

/* Todo Form Section */
.add-todo-section {
  background-color: var(--color-white);
  padding: var(--spacing-xl);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--spacing-xl);
}

.add-todo-form {
  display: grid;
  gap: var(--spacing-lg);
}

/* Filter Section */
.filter-section {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--spacing-xl);
  flex-wrap: wrap;
  gap: var(--spacing-lg);
}

.filter-buttons {
  display: flex;
  gap: var(--spacing-md);
  flex-wrap: wrap;
}

.stats {
  display: flex;
  gap: var(--spacing-lg);
  font-size: var(--font-size-sm);
}

.stats-item {
  color: var(--color-gray-600);
}

/* Todo List */
.todo-list {
  list-style: none;
  display: grid;
  gap: var(--spacing-md);
}

.todo-item {
  background-color: var(--color-white);
  border: 1px solid var(--color-gray-200);
  border-radius: var(--border-radius-lg);
  padding: var(--spacing-lg);
  display: flex;
  gap: var(--spacing-lg);
  align-items: flex-start;
  transition: all var(--transition-base);
  box-shadow: var(--shadow-sm);
}

.todo-item:hover {
  box-shadow: var(--shadow-md);
}

.todo-item--completed {
  background-color: var(--color-gray-50);
  border-color: var(--color-gray-300);
}

.todo-item__checkbox {
  width: 24px;
  height: 24px;
  cursor: pointer;
  accent-color: var(--color-success);
  margin-top: 2px;
  flex-shrink: 0;
}

.todo-item__content {
  flex: 1;
  min-width: 0;
}

.todo-item__title {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  margin-bottom: var(--spacing-sm);
  word-break: break-word;
}

.todo-item--completed .todo-item__title {
  text-decoration: line-through;
  color: var(--color-gray-500);
}

.todo-item__description {
  font-size: var(--font-size-sm);
  color: var(--color-gray-600);
  word-break: break-word;
  margin-bottom: var(--spacing-sm);
}

.todo-item__meta {
  font-size: var(--font-size-xs);
  color: var(--color-gray-400);
}

.todo-item__actions {
  display: flex;
  gap: var(--spacing-sm);
  flex-shrink: 0;
}

/* Empty State */
.empty-state {
  text-align: center;
  padding: var(--spacing-xxl) var(--spacing-xl);
  background-color: var(--color-gray-50);
  border-radius: var(--border-radius-lg);
  border: 2px dashed var(--color-gray-300);
}

.empty-state__text {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-medium);
  color: var(--color-gray-700);
  margin-bottom: var(--spacing-sm);
}

.empty-state__subtext {
  color: var(--color-gray-500);
  margin: 0;
}

/* Responsive */
@media (max-width: 640px) {
  .filter-section {
    flex-direction: column;
    align-items: stretch;
  }

  .filter-buttons {
    width: 100%;
  }

  .btn-filter {
    flex: 1;
  }

  .stats {
    width: 100%;
    justify-content: space-around;
  }

  .todo-item {
    flex-direction: column;
  }
}

/* Utility Classes */
.hidden {
  display: none !important;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
```

#### 3.5 src/js/app.js

```javascript
/**
 * Todo Application
 * Main application logic for todo management
 *
 * @module app
 */

// Constants
const API_URL = "/api/todos";
const STORAGE_KEY = "todos";
const MAX_TODO_ITEMS = 100;

// State
let todos = [];
let currentFilter = "all";

/**
 * Initialize application
 */
function init() {
  loadTodos();
  renderTodos();
  attachEventListeners();
  updateStats();
}

/**
 * Load todos from storage or API
 */
function loadTodos() {
  const stored = localStorage.getItem(STORAGE_KEY);
  if (stored) {
    try {
      todos = JSON.parse(stored);
    } catch (error) {
      console.error("Failed to parse stored todos:", error);
      todos = [];
    }
  }
}

/**
 * Save todos to storage
 */
function saveTodos() {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
}

/**
 * Add new todo
 * @param {string} title - Todo title
 * @param {string} description - Todo description
 */
function addTodo(title, description) {
  // Validation
  if (!title || title.trim() === "") {
    showError("กรุณาใส่ชื่องาน");
    return;
  }

  if (todos.length >= MAX_TODO_ITEMS) {
    showError(`จำกัดสูงสุด ${MAX_TODO_ITEMS} งาน`);
    return;
  }

  // Create todo
  const todo = {
    id: Date.now(),
    title: title.trim(),
    description: description.trim(),
    isCompleted: false,
    createdAt: new Date().toISOString(),
    completedAt: null,
  };

  // Add to list
  todos.unshift(todo);
  saveTodos();

  // Update UI
  renderTodos();
  updateStats();
  resetForm();
  showSuccess("เพิ่มงานสำเร็จ! ✅");
}

/**
 * Delete todo by ID
 * @param {number} id - Todo ID
 */
function deleteTodo(id) {
  if (confirm("ต้องการลบงานนี้หรือไม่?")) {
    todos = todos.filter((todo) => todo.id !== id);
    saveTodos();
    renderTodos();
    updateStats();
    showSuccess("ลบงานสำเร็จ");
  }
}

/**
 * Toggle todo completion status
 * @param {number} id - Todo ID
 */
function toggleTodoStatus(id) {
  const todo = todos.find((t) => t.id === id);
  if (todo) {
    todo.isCompleted = !todo.isCompleted;
    todo.completedAt = todo.isCompleted ? new Date().toISOString() : null;
    saveTodos();
    renderTodos();
    updateStats();
  }
}

/**
 * Filter todos by status
 * @param {string} filter - Filter type: 'all', 'active', 'completed'
 */
function filterTodos(filter) {
  currentFilter = filter;

  // Update active button
  document.querySelectorAll(".btn-filter").forEach((btn) => {
    btn.classList.remove("btn-filter--active");
    if (btn.dataset.filter === filter) {
      btn.classList.add("btn-filter--active");
    }
  });

  renderTodos();
}

/**
 * Get filtered todos
 * @returns {Array} Filtered todos
 */
function getFilteredTodos() {
  switch (currentFilter) {
    case "active":
      return todos.filter((todo) => !todo.isCompleted);
    case "completed":
      return todos.filter((todo) => todo.isCompleted);
    default:
      return todos;
  }
}

/**
 * Render todos to DOM
 */
function renderTodos() {
  const todoList = document.getElementById("todoList");
  const emptyState = document.getElementById("emptyState");
  const filtered = getFilteredTodos();

  todoList.innerHTML = "";

  if (filtered.length === 0) {
    emptyState.classList.remove("hidden");
    return;
  }

  emptyState.classList.add("hidden");

  filtered.forEach((todo) => {
    const li = createTodoElement(todo);
    todoList.appendChild(li);
  });
}

/**
 * Create todo list item element
 * @param {Object} todo - Todo object
 * @returns {HTMLElement} List item element
 */
function createTodoElement(todo) {
  const li = document.createElement("li");
  li.className = `todo-item ${todo.isCompleted ? "todo-item--completed" : ""}`;
  li.dataset.todoId = todo.id;

  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.className = "todo-item__checkbox";
  checkbox.checked = todo.isCompleted;
  checkbox.addEventListener("change", () => toggleTodoStatus(todo.id));

  const content = document.createElement("div");
  content.className = "todo-item__content";

  const title = document.createElement("h3");
  title.className = "todo-item__title";
  title.textContent = todo.title;

  const description = document.createElement("p");
  description.className = "todo-item__description";
  description.textContent = todo.description || "(ไม่มีรายละเอียด)";

  const meta = document.createElement("p");
  meta.className = "todo-item__meta";
  meta.textContent = `สร้างเมื่อ: ${formatDate(todo.createdAt)}`;

  content.appendChild(title);
  if (todo.description) {
    content.appendChild(description);
  }
  content.appendChild(meta);

  const actions = document.createElement("div");
  actions.className = "todo-item__actions";

  const deleteBtn = document.createElement("button");
  deleteBtn.className = "btn btn-danger";
  deleteBtn.textContent = "🗑️";
  deleteBtn.title = "ลบงาน";
  deleteBtn.addEventListener("click", () => deleteTodo(todo.id));

  actions.appendChild(deleteBtn);

  li.appendChild(checkbox);
  li.appendChild(content);
  li.appendChild(actions);

  return li;
}

/**
 * Update statistics
 */
function updateStats() {
  const totalCount = todos.length;
  const completedCount = todos.filter((todo) => todo.isCompleted).length;

  document.getElementById("todoCount").textContent = `ทั้งหมด: ${totalCount}`;
  document.getElementById("completedCount").textContent =
    `เสร็จแล้ว: ${completedCount}`;
}

/**
 * Reset form fields
 */
function resetForm() {
  document.getElementById("addTodoForm").reset();
  document.getElementById("todoTitle").focus();
}

/**
 * Format date to readable string
 * @param {string} dateString - ISO date string
 * @returns {string} Formatted date
 */
function formatDate(dateString) {
  const date = new Date(dateString);
  return date.toLocaleDateString("th-TH", {
    year: "numeric",
    month: "long",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

/**
 * Show error notification
 * @param {string} message - Error message
 */
function showError(message) {
  const notification = createNotification(message, "error");
  document.body.appendChild(notification);

  setTimeout(() => {
    notification.remove();
  }, 3000);
}

/**
 * Show success notification
 * @param {string} message - Success message
 */
function showSuccess(message) {
  const notification = createNotification(message, "success");
  document.body.appendChild(notification);

  setTimeout(() => {
    notification.remove();
  }, 3000);
}

/**
 * Create notification element
 * @param {string} message - Notification message
 * @param {string} type - Notification type: 'success' or 'error'
 * @returns {HTMLElement} Notification element
 */
function createNotification(message, type) {
  const div = document.createElement("div");
  div.className = `notification notification--${type}`;
  div.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background-color: ${type === "success" ? "#28a745" : "#dc3545"};
        color: white;
        padding: 16px 24px;
        border-radius: 4px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        z-index: 9999;
        animation: slideIn 300ms ease-in-out;
    `;
  div.textContent = message;
  return div;
}

/**
 * Attach event listeners
 */
function attachEventListeners() {
  // Add todo form
  document.getElementById("addTodoForm").addEventListener("submit", (e) => {
    e.preventDefault();
    const title = document.getElementById("todoTitle").value;
    const description = document.getElementById("todoDescription").value;
    addTodo(title, description);
  });

  // Filter buttons
  document.querySelectorAll("[data-filter]").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      filterTodos(e.target.dataset.filter);
    });
  });

  // Keyboard shortcut: Ctrl+K to focus input
  document.addEventListener("keydown", (e) => {
    if ((e.ctrlKey || e.metaKey) && e.key === "k") {
      e.preventDefault();
      document.getElementById("todoTitle").focus();
    }
  });
}

// Initialize app when DOM is ready
document.addEventListener("DOMContentLoaded", init);
```

---

## 🎯 ภารกิจ 7.2: Sprint Planning + Development Setup

### ขั้นตอนที่ 1️⃣: Sprint 1 Backlog

#### Sprint_1_Backlog.md

```markdown
# Sprint 1 Backlog - Todo App

## Sprint Information

- **Start Date:** 2025-01-22
- **End Date:** 2025-02-05
- **Duration:** 2 weeks (10 working days)
- **Team Size:** 3 developers
- **Estimated Capacity:** 28-32 story points

## Sprint Goal

"Create a functional Todo application where users can add, edit,
delete, and manage their tasks with a clean and intuitive interface,
ready for adding collaborative features in Sprint 2."

---

## Selected Stories (30 points)

### Story 1: Basic Todo CRUD Operations (8 points)

**Priority:** High | **Assigned:** Dev A

**Description:**

- User can create new todo with title and description
- User can view all todos in a list
- User can mark todo as complete/incomplete
- User can delete todo

**Acceptance Criteria:**

- [ ] Add todo form displays correctly
- [ ] Submit creates todo and adds to list
- [ ] Todos persist in localStorage
- [ ] All todos display with correct information
- [ ] Checkbox toggles completion status
- [ ] Delete button removes todo with confirmation
- [ ] Empty state shows when no todos

**Tasks:**

- Task 1.1: Create HTML form (2h) - Dev A
- Task 1.2: Implement add/delete functions (3h) - Dev A
- Task 1.3: Add localStorage persistence (2h) - Dev A
- Task 1.4: Write unit tests (3h) - QA

**Definition of Done:**

- Code reviewed and approved
- All tests passing
- Tested on mobile
- No console errors
- Follows Coding Standards

---

### Story 2: Filter & Statistics (5 points)

**Priority:** High | **Assigned:** Dev B

**Description:**

- User can filter todos by status (All, Active, Completed)
- Display todo count statistics
- Statistics update when todos change

**Acceptance Criteria:**

- [ ] Filter buttons display and are clickable
- [ ] Active filter button is highlighted
- [ ] Correct todos display for each filter
- [ ] Statistics show total and completed count
- [ ] Statistics update in real-time

**Tasks:**

- Task 2.1: Create filter UI (1h) - Dev B
- Task 2.2: Implement filter logic (2h) - Dev B
- Task 2.3: Add statistics display (1.5h) - Dev B
- Task 2.4: Test filtering (1h) - QA

---

### Story 3: UI/UX Polish (5 points)

**Priority:** Medium | **Assigned:** Dev C

**Description:**

- Responsive design (mobile, tablet, desktop)
- Smooth animations and transitions
- Accessibility (ARIA labels, keyboard navigation)
- Dark mode support

**Acceptance Criteria:**

- [ ] Layout works on 320px - 1920px screens
- [ ] Touch-friendly buttons (48px min)
- [ ] Keyboard navigation works
- [ ] ARIA labels present
- [ ] Dark mode toggle available

**Tasks:**

- Task 3.1: Build responsive layout (3h) - Dev C
- Task 3.2: Add animations & transitions (2h) - Dev C
- Task 3.3: Add accessibility features (2h) - Dev C
- Task 3.4: Test accessibility (2h) - QA

---

### Story 4: Database Integration (7 points)

**Priority:** High | **Assigned:** Dev A

**Description:**

- Create SQLite database schema
- Implement server API endpoints
- Connect frontend to backend API
- Add server-side validation

**Acceptance Criteria:**

- [ ] SQLite database created
- [ ] All CRUD endpoints working
- [ ] Input validation on server
- [ ] Error handling implemented
- [ ] API documentation complete

**Tasks:**

- Task 4.1: Create database schema (2h) - Dev A
- Task 4.2: Build Express server (3h) - Dev A
- Task 4.3: Implement API endpoints (4h) - Dev A
- Task 4.4: Add validation & error handling (2h) - Dev A
- Task 4.5: Test API endpoints (3h) - QA

---

### Story 5: CI/CD Pipeline Setup (5 points)

**Priority:** Medium | **Assigned:** Dev B

**Description:**

- Setup GitHub Actions for automated testing
- Setup linting and code formatting
- Create deployment configuration

**Acceptance Criteria:**

- [ ] Tests run on every push
- [ ] ESLint checks code quality
- [ ] Prettier formats code
- [ ] Build succeeds on main branch
- [ ] Code coverage reported

**Tasks:**

- Task 5.1: Create GitHub Actions workflow (2h) - Dev B
- Task 5.2: Setup ESLint & Prettier (1.5h) - Dev B
- Task 5.3: Add test coverage reporting (1.5h) - Dev B

---

## Sprint Schedule
```

Week 1:

- Monday: Sprint Planning, setup development environment
- Tuesday-Wednesday: Core feature development (CRUD)
- Thursday-Friday: Testing and integration

Week 2:

- Monday-Tuesday: Filter feature and database integration
- Wednesday: UI Polish and accessibility
- Thursday: Testing and bug fixes
- Friday: Code review, demo, and retrospective

```

## Daily Standup
- **Time:** 09:00 AM (10 minutes)
- **Format:** Video call (Zoom/Teams)
- **Attendees:** All team members
- **Questions:**
  1. What did I do yesterday?
  2. What will I do today?
  3. Any blockers?

## Code Review Process
- **When:** Every morning 09:15 - 10:00
- **Requirement:** 2+ approvals to merge
- **Turnaround:** 24 hours max

## Risk Register

| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|-----------|
| Database integration takes longer | Medium | High | Mock API ready, pair programming |
| API responses slow | Low | Medium | Load testing planned, optimize queries |
| Team member sick | Low | High | Cross-training on all features |
| Scope creep from PO | High | High | SM protects scope, change control |

## Success Criteria

- ✅ All 5 stories completed
- ✅ 30 story points delivered
- ✅ Code coverage > 80%
- ✅ Zero critical bugs
- ✅ Team satisfied with pace
- ✅ Ready for demo
```

---

### ขั้นตอนที่ 2️⃣: Database Setup

#### database/schema.sql

```sql
-- Todo App Database Schema
-- SQLite3

-- Create todos table
CREATE TABLE IF NOT EXISTS todos (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    description TEXT,
    is_completed BOOLEAN DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    completed_at DATETIME,

    -- Constraints
    CHECK(LENGTH(title) > 0 AND LENGTH(title) <= 100),
    CHECK(LENGTH(description) <= 500)
);

-- Create indexes
CREATE INDEX idx_todos_completed ON todos(is_completed);
CREATE INDEX idx_todos_created_at ON todos(created_at DESC);

-- Create triggers for updated_at
CREATE TRIGGER update_todos_timestamp
AFTER UPDATE ON todos
BEGIN
    UPDATE todos
    SET updated_at = CURRENT_TIMESTAMP
    WHERE id = NEW.id;
END;
```

#### database/seeds.sql

```sql
-- Sample data for testing

INSERT INTO todos (title, description, is_completed) VALUES
('เรียน JavaScript', 'ศึกษา ES6+ และ async/await', 0),
('สร้าง Todo App', 'สร้างแอปพลิเคชัน Todo แบบเต็มรูป', 1),
('Code Review PR #123', 'ตรวจสอบ pull request จากทีม', 0),
('เขียน Unit Tests', 'เขียน tests สำหรับ core functions', 0),
('ออกแบบ Database Schema', 'ออกแบบ schema สำหรับ Todo App', 1);
```

#### src/server.js

```javascript
/**
 * Express Server for Todo App
 * Handles API endpoints and database operations
 */

const express = require("express");
const sqlite3 = require("sqlite3").verbose();
const cors = require("cors");
const path = require("path");
require("dotenv").config();

const app = express();
const PORT = process.env.PORT || 3000;
const DB_PATH = process.env.DB_PATH || "database.db";

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.static("src"));

// Database setup
const db = new sqlite3.Database(DB_PATH, (err) => {
  if (err) {
    console.error("Database connection failed:", err);
    process.exit(1);
  }
  console.log("✅ Connected to SQLite database");
});

// Enable foreign keys
db.run("PRAGMA foreign_keys = ON");

/**
 * GET /api/todos
 * Get all todos with optional filtering
 * Query params:
 *   - completed: 0|1|undefined (filter by status)
 *   - limit: number (default 100)
 *   - offset: number (default 0)
 */
app.get("/api/todos", (req, res) => {
  const { completed, limit = 100, offset = 0 } = req.query;

  let query = "SELECT * FROM todos";
  const params = [];

  if (completed !== undefined) {
    query += " WHERE is_completed = ?";
    params.push(completed);
  }

  query += " ORDER BY created_at DESC LIMIT ? OFFSET ?";
  params.push(limit, offset);

  db.all(query, params, (err, rows) => {
    if (err) {
      console.error("Database error:", err);
      return res.status(500).json({
        error: "Failed to fetch todos",
        details: err.message,
      });
    }
    res.json(rows || []);
  });
});

/**
 * GET /api/todos/:id
 * Get single todo by ID
 */
app.get("/api/todos/:id", (req, res) => {
  const { id } = req.params;

  // Validation
  if (isNaN(id)) {
    return res.status(400).json({ error: "Invalid todo ID" });
  }

  db.get("SELECT * FROM todos WHERE id = ?", [id], (err, row) => {
    if (err) {
      console.error("Database error:", err);
      return res.status(500).json({ error: "Database error" });
    }

    if (!row) {
      return res.status(404).json({ error: "Todo not found" });
    }

    res.json(row);
  });
});

/**
 * POST /api/todos
 * Create new todo
 * Body: { title, description? }
 */
app.post("/api/todos", (req, res) => {
  const { title, description } = req.body;

  // Validation
  if (!title || title.trim() === "") {
    return res.status(400).json({
      error: "Title is required",
    });
  }

  if (title.length > 100) {
    return res.status(400).json({
      error: "Title must be 100 characters or less",
    });
  }

  if (description && description.length > 500) {
    return res.status(400).json({
      error: "Description must be 500 characters or less",
    });
  }

  const query = `
        INSERT INTO todos (title, description) 
        VALUES (?, ?)
    `;

  db.run(query, [title.trim(), description?.trim() || null], function (err) {
    if (err) {
      console.error("Database error:", err);
      return res.status(500).json({ error: "Failed to create todo" });
    }

    // Get created todo
    db.get("SELECT * FROM todos WHERE id = ?", [this.lastID], (err, row) => {
      if (err) {
        return res.status(500).json({ error: "Failed to fetch created todo" });
      }
      res.status(201).json(row);
    });
  });
});

/**
 * PUT /api/todos/:id
 * Update todo
 * Body: { title?, description?, is_completed? }
 */
app.put("/api/todos/:id", (req, res) => {
  const { id } = req.params;
  const { title, description, is_completed } = req.body;

  // Validation
  if (isNaN(id)) {
    return res.status(400).json({ error: "Invalid todo ID" });
  }

  // Build update query dynamically
  const updates = [];
  const params = [];

  if (title !== undefined) {
    if (!title || title.trim() === "") {
      return res.status(400).json({ error: "Title cannot be empty" });
    }
    if (title.length > 100) {
      return res.status(400).json({ error: "Title too long" });
    }
    updates.push("title = ?");
    params.push(title.trim());
  }

  if (description !== undefined) {
    if (description.length > 500) {
      return res.status(400).json({ error: "Description too long" });
    }
    updates.push("description = ?");
    params.push(description.trim() || null);
  }

  if (is_completed !== undefined) {
    updates.push("is_completed = ?");
    updates.push("completed_at = ?");
    params.push(is_completed ? 1 : 0);
    params.push(is_completed ? new Date().toISOString() : null);
  }

  if (updates.length === 0) {
    return res.status(400).json({ error: "No fields to update" });
  }

  params.push(id);
  const query = `UPDATE todos SET ${updates.join(", ")} WHERE id = ?`;

  db.run(query, params, function (err) {
    if (err) {
      console.error("Database error:", err);
      return res.status(500).json({ error: "Failed to update todo" });
    }

    if (this.changes === 0) {
      return res.status(404).json({ error: "Todo not found" });
    }

    // Return updated todo
    db.get("SELECT * FROM todos WHERE id = ?", [id], (err, row) => {
      if (err) {
        return res.status(500).json({ error: "Failed to fetch updated todo" });
      }
      res.json(row);
    });
  });
});

/**
 * DELETE /api/todos/:id
 * Delete todo
 */
app.delete("/api/todos/:id", (req, res) => {
  const { id } = req.params;

  // Validation
  if (isNaN(id)) {
    return res.status(400).json({ error: "Invalid todo ID" });
  }

  db.run("DELETE FROM todos WHERE id = ?", [id], function (err) {
    if (err) {
      console.error("Database error:", err);
      return res.status(500).json({ error: "Failed to delete todo" });
    }

    if (this.changes === 0) {
      return res.status(404).json({ error: "Todo not found" });
    }

    res.status(204).send();
  });
});

/**
 * Health check endpoint
 */
app.get("/api/health", (req, res) => {
  res.json({
    status: "UP",
    timestamp: new Date().toISOString(),
    database: "connected",
  });
});

/**
 * Root endpoint - serve index.html
 */
app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "html/index.html"));
});

/**
 * Error handling middleware
 */
app.use((err, req, res, next) => {
  console.error("Error:", err);
  res.status(500).json({
    error: "Internal server error",
    details: process.env.NODE_ENV === "development" ? err.message : undefined,
  });
});

/**
 * 404 handler
 */
app.use((req, res) => {
  res.status(404).json({ error: "Endpoint not found" });
});

/**
 * Start server
 */
const server = app.listen(PORT, () => {
  console.log(`
    ========================================
    🚀 Todo App Server Started!
    ========================================
    
    ✅ Server running at: http://localhost:${PORT}
    📦 Database: ${DB_PATH}
    🔍 API Docs: http://localhost:${PORT}/api
    
    ========================================
    `);
});

/**
 * Graceful shutdown
 */
process.on("SIGINT", () => {
  console.log("\n\n📵 Shutting down gracefully...");

  server.close(() => {
    console.log("✅ Server closed");
  });

  db.close((err) => {
    if (err) {
      console.error("Error closing database:", err);
      process.exit(1);
    }
    console.log("✅ Database closed");
    process.exit(0);
  });
});

module.exports = app;
```

---

### ขั้นตอนที่ 3️⃣: Development Environment Setup

#### docs/DEVELOPMENT.md

````markdown
# Development Environment Setup

## System Requirements

- Node.js 14+
- npm 6+ or yarn 1.22+
- SQLite3
- Git 2.30+
- Text editor (VS Code, Sublime, etc.)

## Installation Steps

### 1. Clone Repository

```bash
git clone https://github.com/yourname/todo-app.git
cd todo-app
```
````

### 2. Install Dependencies

```bash
npm install
```

### 3. Setup Database

```bash

# Initialize database schema

npm run db:init

# (Optional) Seed with sample data

npm run db:seed
```

### 4. Create Environment File

```bash
cp .env.example .env
```

Edit .env if needed:

```
NODE_ENV=development
PORT=3000
DB_PATH=database.db
```

### 5. Start Development Server

```bash
npm run dev
```

Open browser: http://localhost:3000

## Verification

- [ ] Server running at http://localhost:3000
- [ ] Health check: http://localhost:3000/api/health → {"status":"UP"}
- [ ] Database file exists: \`database.db\`
- [ ] Can create/read/update/delete todos
- [ ] No console errors

## Useful Commands

### Development

```bash
npm run dev # Start with auto-reload
npm start # Start production build
npm test # Run tests
npm run test:watch # Watch mode
npm run lint # Check code quality
npm run format # Format code
```

### Database

```bash
npm run db:init # Create schema
npm run db:seed # Add sample data
sqlite3 database.db # Open SQLite CLI
```

## Troubleshooting

### Port 3000 Already in Use

```bash

# Find process using port

lsof -i :3000

# Kill process

kill -9 <PID>

# Or use different port

PORT=3001 npm run dev
```

### Database Errors

```bash

# Reset database

rm database.db
npm run db:init
npm run db:seed
```

### Dependencies Issues

```bash
rm package-lock.json
rm -rf node_modules
npm install
```

## IDE Setup

### VS Code

1. Install extensions:
   - ES7+ React/Redux/React-Native snippets
   - Prettier - Code formatter
   - ESLint
   - SQLite

2. Settings (`.vscode/settings.json`):
   ```json
   {
     "editor.formatOnSave": true,
     "editor.defaultFormatter": "esbenp.prettier-vscode",
     "[javascript]": {
       "editor.defaultFormatter": "esbenp.prettier-vscode"
     }
   }
   ```

### IntelliJ IDEA / WebStorm

1. Enable ESLint:
   - Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint
   - Check "Automatic ESLint configuration"

2. Setup Prettier:
   - Settings → Languages & Frameworks → JavaScript → Prettier
   - Node interpreter: Select your Node
   - Prettier package: Select from node_modules

## Testing

```bash

# Run all tests

npm test

# Watch mode

npm run test:watch

# Coverage report

npm run test:coverage
```

## Debugging

### Browser DevTools

1. Press F12 in browser
2. Go to Console tab
3. Use breakpoints in Sources tab

### VS Code Debugger

Add `.vscode/launch.json`:

```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "program": "\${workspaceFolder}/src/server.js"
    }
  ]
}
```

Then press F5 to start debugging.

## API Testing

### Using curl

```bash

# Get all todos

curl http://localhost:3000/api/todos

# Create todo

curl -X POST http://localhost:3000/api/todos \\
-H "Content-Type: application/json" \\
-d '{"title":"Test","description":"Test description"}'

# Update todo

curl -X PUT http://localhost:3000/api/todos/1 \\
-H "Content-Type: application/json" \\
-d '{"is_completed":true}'

# Delete todo

curl -X DELETE http://localhost:3000/api/todos/1
```

### Using REST Client (VS Code)

Create `test.http`:

```http

### Get all todos

GET http://localhost:3000/api/todos

### Create todo

POST http://localhost:3000/api/todos
Content-Type: application/json

{
"title": "Learn SQLite",
"description": "Complete database setup"
}
```

## Performance Tips

1. **Lazy load data**: Pagination for large lists
2. **Debounce search**: Delay API calls while typing
3. **Cache responses**: Store API responses
4. **Optimize database**: Add indexes, analyze queries
5. **Bundle optimization**: Webpack minification

## Security Checklist

- [ ] No sensitive data in .env (use .env.example)
- [ ] Input validation on server
- [ ] SQL injection prevention (parameterized queries)
- [ ] XSS prevention (sanitize output)
- [ ] CORS properly configured
- [ ] HTTPS in production
- [ ] Rate limiting on API

## Git Workflow

```bash
# Start feature
git checkout -b feature/add-categories

# Make changes
git add .
git commit -m "feat: add category support"

# Push
git push origin feature/add-categories

# Create PR on GitHub
# Request 2+ reviewers
# Merge after approval
```

---

## 🎯 ภารกิจ 7.3: Code Review Training

### Code_Review_Checklist.md

```markdown
# Code Review Checklist - Todo App

## Before Starting Review

- [ ] Understand the feature/fix
- [ ] Read related issues
- [ ] Understand acceptance criteria
- [ ] Know the code style guide

## Functionality (Must Have)

- [ ] Code does what it's supposed to do
- [ ] All acceptance criteria met
- [ ] Edge cases handled
  - Empty title/description
  - Maximum length exceeded
  - Database errors
  - Network errors
  - No todos exist
- [ ] Error messages are clear

## Code Quality (Must Have)

- [ ] Follows Coding Standards
- [ ] Variable/function names are clear
- [ ] No duplicate code
- [ ] Functions are focused (single responsibility)
- [ ] Methods not too long (< 30 lines ideal)
- [ ] Comments explain "why", not "what"
- [ ] No console.log() in production code
- [ ] No hardcoded values (use constants)
- [ ] No TODO comments left behind

## Testing (Must Have)

- [ ] Unit tests added for logic
- [ ] Integration tests for API
- [ ] Happy path tested
- [ ] Error scenarios tested
- [ ] Test names are clear
- [ ] No skipped tests
- [ ] Code coverage reasonable (>80%)

## Performance (Should Have)

- [ ] No N+1 database queries
- [ ] Debouncing on search/input
- [ ] No memory leaks
- [ ] Efficient data structures
- [ ] Database queries optimized

## Security (Must Have)

- [ ] No hardcoded secrets
- [ ] Input validation present
- [ ] SQL injection prevented (parameterized queries)
- [ ] XSS prevention (sanitized output)
- [ ] Authentication/authorization correct

## Accessibility (Should Have)

- [ ] ARIA labels present
- [ ] Keyboard navigation works
- [ ] Color contrast sufficient
- [ ] Form labels associated
- [ ] Focus states visible

## Documentation (Should Have)

- [ ] Code comments for complex logic
- [ ] JSDoc comments for functions
- [ ] README updated if needed
- [ ] API documentation updated

## UI/UX (For Frontend)

- [ ] Responsive design (mobile, tablet, desktop)
- [ ] Touch-friendly (48px buttons)
- [ ] Smooth transitions
- [ ] Loading states shown
- [ ] Error states shown
- [ ] Empty states handled

## Checklist for Database Changes

- [ ] Schema changes backward compatible
- [ ] Migrations written
- [ ] Indexes added where needed
- [ ] No N+1 queries

## Common Issues to Look For

- ❌ Mutation of function parameters
- ❌ Not closing database connections
- ❌ Race conditions in async code
- ❌ Forgetting try-catch blocks
- ❌ Not validating API inputs
- ❌ Breaking previous API contracts
- ❌ Not updating tests with code changes

## Review Comments Template

### For Issues (kind):
```

I'm concerned about [issue].

Consider [suggestion] because [reason].

This could prevent [potential problem].

```

Example:
```

I'm concerned about the database query in addTodo().

Consider adding .trim() to the title because whitespace-only strings
are technically non-empty but unusable as todo titles.

This could prevent creating todos like " " or " ".

```

### For Questions:
```

Can you clarify why [code]? I was expecting [alternative].

Have you considered [other approach]?

```

### For Praise:
```

Great work on [specific thing]! I like how you [specific reason].

This approach is [why it's good].

```

## Review Attitude

✅ DO:
- Be respectful and constructive
- Ask questions instead of demanding
- Assume good intent
- Praise good code
- Focus on important issues first
- Approve when good enough
- Be timely in reviews

❌ DON'T:
- Be condescending
- Make it personal
- Nitpick everything
- Request unnecessary changes
- Block PR over style disagreement
- Take days to review
```

---

## ✅ Deliverables Summary

```
wk07-th-doing-step-by-step-TODO.md contains:

### ภารกิจ 7.1 :
✅ Coding_Standards.md - HTML, CSS, JS standards with examples
✅ .gitignore - Complete gitignore for Node/frontend projects
✅ README.md - Full project documentation
✅ PR template - Structured pull request template
✅ GitHub Actions CI - Automated testing pipeline
✅ Code Skeleton - Complete project structure
  - package.json - Dependencies and scripts
  - index.html - Full HTML with form, list, filters
  - main.css - 700+ lines of styling (responsive, accessible)
  - app.js - Complete JavaScript app (400+ lines)

### ภารกิจ 7.2 :
✅ Sprint_1_Backlog.md
  - 5 user stories (30 points total)
  - Detailed acceptance criteria
  - Task breakdown for each story
  - Sprint schedule and team roles
  - Risk register

✅ Database setup
  - schema.sql - SQLite database design
  - seeds.sql - Sample data
  - server.js - Express API (600+ lines)

✅ DEVELOPMENT.md
  - Step-by-step setup instructions
  - Troubleshooting guide
  - IDE configuration
  - Testing & debugging guides
  - API testing examples

### ภารกิจ 7.3 (โบนัส):
✅ Code_Review_Checklist.md - Comprehensive review guidelines
```

---

**สาระสำคัญ:**

ไฟล์นี้แสดงตัวอย่างของการทำ Week 7: D2 โดยใช้ Todo App
เป็นกรณีศึกษา

**Files to Copy/Adapt:**

1. Copy Coding_Standards.md structure
2. Adapt file organization to your tech stack
3. Use Sprint_1_Backlog.md as template
4. Follow Database design patterns
5. Use server code as reference
6. Apply CSS & JS practices to your project

Good luck!
