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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 79 additions & 20 deletions team_product/src/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* このコードはブラウザ上で動作します。
* ログはブラウザのF12(開発者ツール)に表示されます。
*
* 現在の機能: Create(追加)、Read(一覧表示)
* 現在の機能: Create(追加)、Read(一覧表示)、Update(編集)
*/

// =====================================================
Expand Down Expand Up @@ -52,27 +52,27 @@ async function loadItems() {
* アイテムを追加
*
* IPO:
* - Input: テキストボックスのタイトル
* - Input: テキストボックスのname
* - Process: サーバーにPOSTリクエスト → DB保存
* - Output: 一覧を再読み込み
*/
async function addItem() {
const title = titleInput.value.trim()
const name = titleInput.value.trim()

// 入力チェック(Client側のバリデーション)
if (!title) {
alert('タイトルを入力してください')
if (!name) {
alert('nameを入力してください')
return
}

console.log('[CLIENT] アイテムを追加:', title)
console.log('[CLIENT] アイテムを追加:', name)

try {
// サーバーにPOSTリクエスト
const response = await fetch('/api/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title })
body: JSON.stringify({ title: name })
})

if (!response.ok) {
Expand All @@ -94,9 +94,47 @@ async function addItem() {
}

// =====================================================
// ここに新しい機能(Delete, Updateなど)を追加していこう!
// Update機能
// =====================================================

/**
* アイテム名(name)を更新
*
* IPO:
* - Input: 編集ボタン押下後に入力されたname
* - Process: PUTリクエストでServerへ編集要求 → DB更新
* - Output: 更新後一覧を再描画
*/
async function updateItem(id, nameInput) {
const name = nameInput.value.trim()

if (!name) {
alert('nameを入力してください')
return
}

console.log('[CLIENT] アイテムを更新:', id, name)

try {
const response = await fetch(`/api/items/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name })
})

if (!response.ok) {
const error = await response.json()
throw new Error(error.error)
}

await loadItems()
console.log('[CLIENT] 更新完了')
} catch (error) {
console.error('[CLIENT] エラー:', error)
alert('更新に失敗しました: ' + error.message)
}
}

// =====================================================
// 画面描画関数
// =====================================================
Expand All @@ -115,22 +153,43 @@ function renderItems(items) {
items.forEach(item => {
const li = document.createElement('li')
li.className = 'item'
li.innerHTML = `
<span class="item-title">${escapeHtml(item.title)}</span>
`

const nameText = document.createElement('span')
nameText.className = 'item-title'
nameText.textContent = item.title

const actions = document.createElement('div')
actions.className = 'item-actions'

const editButton = document.createElement('button')
editButton.className = 'item-button edit-button'
editButton.textContent = '編集'
editButton.addEventListener('click', () => {
const nameInput = document.createElement('input')
nameInput.type = 'text'
nameInput.className = 'edit-input'
nameInput.value = item.title

const doneButton = document.createElement('button')
doneButton.className = 'item-button done-button'
doneButton.textContent = '完了'
doneButton.addEventListener('click', () => {
updateItem(item.id, nameInput)
})

actions.innerHTML = ''
li.replaceChild(nameInput, nameText)
actions.appendChild(doneButton)
nameInput.focus()
})

actions.appendChild(editButton)
li.appendChild(nameText)
li.appendChild(actions)
itemList.appendChild(li)
})
}

/**
* HTMLエスケープ(XSS対策)
*/
function escapeHtml(text) {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}

// =====================================================
// イベントリスナー
// =====================================================
Expand Down
4 changes: 2 additions & 2 deletions team_product/src/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ <h1>Base App</h1>
<input
type="text"
id="titleInput"
placeholder="新しいアイテムを入力..."
placeholder="新しいnameを入力..."
autocomplete="off"
>
<button id="addButton">追加</button>
Expand All @@ -32,7 +32,7 @@ <h2>アイテム一覧</h2>
<!-- ここにアイテムが動的に表示される -->
</ul>
<p id="emptyMessage" class="empty-message">
アイテムがありません。上のフォームから追加してください。
データがありません。上のフォームから追加してください。
</p>
</section>
</div>
Expand Down
44 changes: 44 additions & 0 deletions team_product/src/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,50 @@ header .subtitle {
.item-title {
font-size: 1rem;
color: #2c3e50;
flex: 1;
}

.item-actions {
display: flex;
gap: 8px;
}

.item-button {
padding: 8px 12px;
border: none;
border-radius: 6px;
color: white;
cursor: pointer;
font-size: 0.85rem;
}

.edit-button {
background-color: #7f8c8d;
}

.edit-button:hover {
background-color: #6c7a7d;
}

.done-button {
background-color: #27ae60;
}

.done-button:hover {
background-color: #219150;
}

.edit-input {
flex: 1;
padding: 8px 10px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1rem;
}

.edit-input:focus {
outline: none;
border-color: #3498db;
}

/* 空メッセージ */
Expand Down
39 changes: 35 additions & 4 deletions team_product/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* このファイルはサーバー側の処理を担当します。
* ログはVSCodeのターミナルに表示されます。
*
* 現在の機能: Create(追加)、Read(一覧表示)
* 現在の機能: Create(追加)、Read(一覧表示)、Update(編集)
*/

const express = require('express')
Expand Down Expand Up @@ -78,9 +78,40 @@ app.post('/api/items', async (req, res) => {
}
})

// =====================================================
// ここに新しいAPI(Delete, Updateなど)を追加していこう!
// =====================================================
/**
* PUT /api/items/:id - アイテム編集
*
* IPO:
* - Input: クライアントからidとnameを受け取る
* - Process: 対象idのtitleをDB上で更新
* - Output: 更新したアイテムをJSONで返す
*/
app.put('/api/items/:id', async (req, res) => {
try {
const id = Number(req.params.id)
const { name, title } = req.body
const nextName = (name ?? title ?? '').trim()

if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: '不正なidです' })
}

if (!nextName) {
return res.status(400).json({ error: 'nameを入力してください' })
}

const item = await prisma.item.update({
where: { id },
data: { title: nextName }
})

console.log('[SERVER] アイテムを更新:', item)
res.json(item)
} catch (error) {
console.error('[SERVER] エラー:', error)
res.status(500).json({ error: 'アイテム更新に失敗しました' })
}
})

// =====================================================
// サーバー起動
Expand Down