From 205126ec0a702caf868c0b54ae7febcc736d4ba9 Mon Sep 17 00:00:00 2001 From: tmakkrhr-ctrl Date: Fri, 27 Feb 2026 13:32:23 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E7=B7=A8=E9=9B=86=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/src/public/app.js | 99 ++++++++++++++++++++++++------ team_product/src/public/index.html | 4 +- team_product/src/public/style.css | 44 +++++++++++++ team_product/src/server.js | 39 ++++++++++-- 4 files changed, 160 insertions(+), 26 deletions(-) diff --git a/team_product/src/public/app.js b/team_product/src/public/app.js index 91e0dfb..528c567 100644 --- a/team_product/src/public/app.js +++ b/team_product/src/public/app.js @@ -4,7 +4,7 @@ * このコードはブラウザ上で動作します。 * ログはブラウザのF12(開発者ツール)に表示されます。 * - * 現在の機能: Create(追加)、Read(一覧表示) + * 現在の機能: Create(追加)、Read(一覧表示)、Update(編集) */ // ===================================================== @@ -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) { @@ -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) + } +} + // ===================================================== // 画面描画関数 // ===================================================== @@ -115,22 +153,43 @@ function renderItems(items) { items.forEach(item => { const li = document.createElement('li') li.className = 'item' - li.innerHTML = ` - ${escapeHtml(item.title)} - ` + + 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 -} - // ===================================================== // イベントリスナー // ===================================================== diff --git a/team_product/src/public/index.html b/team_product/src/public/index.html index 5b52dc6..358c487 100644 --- a/team_product/src/public/index.html +++ b/team_product/src/public/index.html @@ -19,7 +19,7 @@

Base App

@@ -32,7 +32,7 @@

アイテム一覧

- アイテムがありません。上のフォームから追加してください。 + データがありません。上のフォームから追加してください。

diff --git a/team_product/src/public/style.css b/team_product/src/public/style.css index 203a4e8..a6bab48 100644 --- a/team_product/src/public/style.css +++ b/team_product/src/public/style.css @@ -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; } /* 空メッセージ */ diff --git a/team_product/src/server.js b/team_product/src/server.js index f9d2a84..e4b4222 100644 --- a/team_product/src/server.js +++ b/team_product/src/server.js @@ -4,7 +4,7 @@ * このファイルはサーバー側の処理を担当します。 * ログはVSCodeのターミナルに表示されます。 * - * 現在の機能: Create(追加)、Read(一覧表示) + * 現在の機能: Create(追加)、Read(一覧表示)、Update(編集) */ const express = require('express') @@ -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: 'アイテム更新に失敗しました' }) + } +}) // ===================================================== // サーバー起動 From f04c376ecf43709ad7fd0216326053664949b22a Mon Sep 17 00:00:00 2001 From: tmakkrhr-ctrl Date: Fri, 27 Feb 2026 14:12:41 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E3=81=AB=E3=82=B5?= =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=BC=E3=81=8C=E5=8B=95=E3=81=8F=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/database/app.db | Bin 0 -> 20480 bytes team_product/package-lock.json | 934 ++++++++++++++++++ team_product/package.json | 4 +- .../20260227050735_init/migration.sql | 7 + .../prisma/migrations/migration_lock.toml | 3 + team_product/src/package-lock.json | 417 ++++++++ team_product/src/package.json | 16 + 7 files changed, 1379 insertions(+), 2 deletions(-) create mode 100644 team_product/database/app.db create mode 100644 team_product/package-lock.json create mode 100644 team_product/prisma/migrations/20260227050735_init/migration.sql create mode 100644 team_product/prisma/migrations/migration_lock.toml create mode 100644 team_product/src/package-lock.json create mode 100644 team_product/src/package.json diff --git a/team_product/database/app.db b/team_product/database/app.db new file mode 100644 index 0000000000000000000000000000000000000000..1b974475a16061f22b670d94d8850c3a6407133a GIT binary patch literal 20480 zcmeI)Z%@-e90%~0Z6FKFd_opuV$S&h2w=K)W9*p|DlU$Jql=;snrrU{4c$80PJLuV z661@|r@jGw0lo_3L*51W%2B(9a78X!M-aVSc%&23cv;>mr&N9o;g@>znU`$Z**qIcY+o*Th5M8 zL&s|eqkG0Kk9@SkC5H%PDD{STkz;tcyWHLjy2A5P`jbr4X*ST8ZF6+u%-ra{n7Gw$M7LW?WI5fD#DW9?2tWV=5P$##AOHafKmY;|fWSWzI7rFK z#z88fiErzZ1ICq;}EDFKLV| z6>L>gwLB{^t5huIttMl6RpHAjEvmLw(zwM6oLV%u%;is?-+uR0MO{)SJ0uX=z1Rwwb2tWV=5P$##AaKnDW@3%MdcAP^{r^ZxA6>Hn z;&dSZ0SG_<0uX=z1Rwwb2tWV=5Qqfs$q6ZUN33QNiO1Y^y#?~x^IdjVeEEO(>zi1= tzrAt#{C_N^kAVgO2tWV=5P$##AOHafKmY;|fWUtyFd@gLC!>G2_zip6Mf3mw literal 0 HcmV?d00001 diff --git a/team_product/package-lock.json b/team_product/package-lock.json new file mode 100644 index 0000000..3aed839 --- /dev/null +++ b/team_product/package-lock.json @@ -0,0 +1,934 @@ +{ + "name": "base-app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "base-app", + "version": "1.0.0", + "dependencies": { + "@prisma/client": "^5.22.0", + "express": "^4.22.1" + }, + "devDependencies": { + "prisma": "^5.7.0" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/team_product/package.json b/team_product/package.json index 54f5f81..ee44a3f 100644 --- a/team_product/package.json +++ b/team_product/package.json @@ -10,8 +10,8 @@ "db:reset": "npx prisma migrate reset" }, "dependencies": { - "express": "^4.18.2", - "@prisma/client": "^5.7.0" + "@prisma/client": "^5.22.0", + "express": "^4.22.1" }, "devDependencies": { "prisma": "^5.7.0" diff --git a/team_product/prisma/migrations/20260227050735_init/migration.sql b/team_product/prisma/migrations/20260227050735_init/migration.sql new file mode 100644 index 0000000..ed47e7b --- /dev/null +++ b/team_product/prisma/migrations/20260227050735_init/migration.sql @@ -0,0 +1,7 @@ +-- CreateTable +CREATE TABLE "Item" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); diff --git a/team_product/prisma/migrations/migration_lock.toml b/team_product/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5e5c47 --- /dev/null +++ b/team_product/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/team_product/src/package-lock.json b/team_product/src/package-lock.json new file mode 100644 index 0000000..3316a5f --- /dev/null +++ b/team_product/src/package-lock.json @@ -0,0 +1,417 @@ +{ + "name": "src", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "src", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "prisma": "^6.19.2" + } + }, + "node_modules/@prisma/config": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/prisma": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", + "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.2", + "@prisma/engines": "6.19.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/team_product/src/package.json b/team_product/src/package.json new file mode 100644 index 0000000..648d1b9 --- /dev/null +++ b/team_product/src/package.json @@ -0,0 +1,16 @@ +{ + "name": "src", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "prisma": "^6.19.2" + } +} From 42110bff0c6c29372c99ccdfb0ef55327c1419dc Mon Sep 17 00:00:00 2001 From: Yuki Date: Fri, 27 Feb 2026 14:26:21 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E5=AE=8C=E4=BA=86=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/database/app.db | Bin 20480 -> 24576 bytes .../migration.sql | 15 ++++++ team_product/prisma/schema.prisma | 1 + team_product/src/public/app.js | 47 +++++++++++++++++- team_product/src/public/style.css | 19 +++++++ team_product/src/server.js | 31 ++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 team_product/prisma/migrations/20260227052347_add_completed/migration.sql diff --git a/team_product/database/app.db b/team_product/database/app.db index 1b974475a16061f22b670d94d8850c3a6407133a..20afdd60f0323fb58674de69e82a74bca6fb24ae 100644 GIT binary patch delta 558 zcmZozz}Rqrae}lUF9QPu8xXSqF(VMOPSi0L=Vj0<>)_@2!N9^jgMs%N|7MARTac4llA5BV z;N-0`$uQA?an9V{$={6(fQB0xnM)a%8X23I$0w$w#3#c%1y=H9 za)JC54krHf4E*c)Z)_G+Sj?}Z!5qp+j2oHMnL{Vn$gA=I!yF9wAMl?78nKUGOoWM* zL70^_DJcnPO9x{-gq-}qUY5TjLLU@ijQrmi_`mVL*evL9hhK$qVfF|$Zw-9AwV-V& PMGWae}lU8v_FaD-go~(?lI(Q8os>vJPJU9}Fx!%NTgC@o(n&!Lw|$pnwt2 z a.completed - b.completed) + // 各アイテムを描画 - items.forEach(item => { + sorted.forEach(item => { const li = document.createElement('li') - li.className = 'item' + li.className = item.completed ? 'item item-completed' : 'item' + + const checkbox = document.createElement('input') + checkbox.type = 'checkbox' + checkbox.className = 'item-checkbox' + checkbox.checked = item.completed + checkbox.addEventListener('change', () => completeItem(item.id)) const nameText = document.createElement('span') nameText.className = 'item-title' @@ -184,6 +226,7 @@ function renderItems(items) { }) actions.appendChild(editButton) + li.appendChild(checkbox) li.appendChild(nameText) li.appendChild(actions) itemList.appendChild(li) diff --git a/team_product/src/public/style.css b/team_product/src/public/style.css index a6bab48..6e6152d 100644 --- a/team_product/src/public/style.css +++ b/team_product/src/public/style.css @@ -156,6 +156,25 @@ header .subtitle { border-color: #3498db; } +/* 完了アイテム */ +.item-completed { + background-color: #f0f0f0; + opacity: 0.7; +} + +.item-completed .item-title { + color: #95a5a6; + text-decoration: line-through; +} + +.item-checkbox { + width: 18px; + height: 18px; + margin-right: 10px; + cursor: pointer; + accent-color: #27ae60; +} + /* 空メッセージ */ .empty-message { text-align: center; diff --git a/team_product/src/server.js b/team_product/src/server.js index e4b4222..c924138 100644 --- a/team_product/src/server.js +++ b/team_product/src/server.js @@ -113,6 +113,37 @@ app.put('/api/items/:id', async (req, res) => { } }) +/** + * PATCH /api/items/:id/complete - アイテム完了 + * + * IPO: + * - Input: URLパラメータからアイテムIDを受け取る + * - Process: 対象idのcompletedをDB上でトグル(true/false) + * - Output: 更新したアイテムをJSONで返す + */ +app.patch('/api/items/:id/complete', async (req, res) => { + try { + const id = Number(req.params.id) + + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: '不正なidです' }) + } + + // 現在のcompletedを取得してトグル + const current = await prisma.item.findUnique({ where: { id } }) + const item = await prisma.item.update({ + where: { id }, + data: { completed: !current.completed } + }) + + console.log('[SERVER] アイテムを完了切替:', item) + res.json(item) + } catch (error) { + console.error('[SERVER] エラー:', error) + res.status(500).json({ error: 'アイテム完了に失敗しました' }) + } +}) + // ===================================================== // サーバー起動 // ===================================================== From 8b7c305be0229579ddc1efc08879b2fffe0fda71 Mon Sep 17 00:00:00 2001 From: Yuki Date: Fri, 27 Feb 2026 14:32:29 +0900 Subject: [PATCH 4/7] =?UTF-8?q?Revert=20"=E5=AE=8C=E4=BA=86=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 42110bff0c6c29372c99ccdfb0ef55327c1419dc. --- team_product/database/app.db | Bin 24576 -> 20480 bytes .../migration.sql | 15 ------ team_product/prisma/schema.prisma | 1 - team_product/src/public/app.js | 47 +----------------- team_product/src/public/style.css | 19 ------- team_product/src/server.js | 31 ------------ 6 files changed, 2 insertions(+), 111 deletions(-) delete mode 100644 team_product/prisma/migrations/20260227052347_add_completed/migration.sql diff --git a/team_product/database/app.db b/team_product/database/app.db index 20afdd60f0323fb58674de69e82a74bca6fb24ae..1b974475a16061f22b670d94d8850c3a6407133a 100644 GIT binary patch delta 164 zcmZoTz}T>Wae}lU8v_FaD-go~(?lI(Q8os>vJPJU9}Fx!%NTgC@o(n&!Lw|$pnwt2 z)_@2!N9^jgMs%N|7MARTac4llA5BV z;N-0`$uQA?an9V{$={6(fQB0xnM)a%8X23I$0w$w#3#c%1y=H9 za)JC54krHf4E*c)Z)_G+Sj?}Z!5qp+j2oHMnL{Vn$gA=I!yF9wAMl?78nKUGOoWM* zL70^_DJcnPO9x{-gq-}qUY5TjLLU@ijQrmi_`mVL*evL9hhK$qVfF|$Zw-9AwV-V& PMG a.completed - b.completed) - // 各アイテムを描画 - sorted.forEach(item => { + items.forEach(item => { const li = document.createElement('li') - li.className = item.completed ? 'item item-completed' : 'item' - - const checkbox = document.createElement('input') - checkbox.type = 'checkbox' - checkbox.className = 'item-checkbox' - checkbox.checked = item.completed - checkbox.addEventListener('change', () => completeItem(item.id)) + li.className = 'item' const nameText = document.createElement('span') nameText.className = 'item-title' @@ -226,7 +184,6 @@ function renderItems(items) { }) actions.appendChild(editButton) - li.appendChild(checkbox) li.appendChild(nameText) li.appendChild(actions) itemList.appendChild(li) diff --git a/team_product/src/public/style.css b/team_product/src/public/style.css index 6e6152d..a6bab48 100644 --- a/team_product/src/public/style.css +++ b/team_product/src/public/style.css @@ -156,25 +156,6 @@ header .subtitle { border-color: #3498db; } -/* 完了アイテム */ -.item-completed { - background-color: #f0f0f0; - opacity: 0.7; -} - -.item-completed .item-title { - color: #95a5a6; - text-decoration: line-through; -} - -.item-checkbox { - width: 18px; - height: 18px; - margin-right: 10px; - cursor: pointer; - accent-color: #27ae60; -} - /* 空メッセージ */ .empty-message { text-align: center; diff --git a/team_product/src/server.js b/team_product/src/server.js index c924138..e4b4222 100644 --- a/team_product/src/server.js +++ b/team_product/src/server.js @@ -113,37 +113,6 @@ app.put('/api/items/:id', async (req, res) => { } }) -/** - * PATCH /api/items/:id/complete - アイテム完了 - * - * IPO: - * - Input: URLパラメータからアイテムIDを受け取る - * - Process: 対象idのcompletedをDB上でトグル(true/false) - * - Output: 更新したアイテムをJSONで返す - */ -app.patch('/api/items/:id/complete', async (req, res) => { - try { - const id = Number(req.params.id) - - if (!Number.isInteger(id) || id <= 0) { - return res.status(400).json({ error: '不正なidです' }) - } - - // 現在のcompletedを取得してトグル - const current = await prisma.item.findUnique({ where: { id } }) - const item = await prisma.item.update({ - where: { id }, - data: { completed: !current.completed } - }) - - console.log('[SERVER] アイテムを完了切替:', item) - res.json(item) - } catch (error) { - console.error('[SERVER] エラー:', error) - res.status(500).json({ error: 'アイテム完了に失敗しました' }) - } -}) - // ===================================================== // サーバー起動 // ===================================================== From d23c3aa671d1ea258446fee042428a61db670990 Mon Sep 17 00:00:00 2001 From: Yuki Date: Fri, 27 Feb 2026 14:38:38 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E5=AE=8C=E4=BA=86=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/database/app.db | Bin 24576 -> 24576 bytes .../migration.sql | 15 ++ team_product/src/public/app.js | 210 +++++++++--------- team_product/src/public/index.html | 76 +++---- team_product/src/server.js | 112 +++++----- 5 files changed, 214 insertions(+), 199 deletions(-) create mode 100644 team_product/prisma/migrations/20260227053501_add_completed/migration.sql diff --git a/team_product/database/app.db b/team_product/database/app.db index 20afdd60f0323fb58674de69e82a74bca6fb24ae..f1e79327306f2915886f9a992fe4b92b966f9e34 100644 GIT binary patch delta 91 zcmZoTz}Rqrae_3X=tLQ3M$wH4^Zogmc^McOnE5v{@ZaY@!M|~{puu8(JyB*h24PNC m*2Kg_#yN9maW4gtTI!-q5aFbxB%nYCV?0P$fBxkA@rnRoB^k2- delta 66 zcmZoTz}Rqrae_1>??f4AM&6AH^Zj|5co`TNnD|dH@ZaY@v02bz55Kr56B~mtCo5}G VQWE2wxgCu0K(fC%bMpOoMF3Vb6AJ(U diff --git a/team_product/prisma/migrations/20260227053501_add_completed/migration.sql b/team_product/prisma/migrations/20260227053501_add_completed/migration.sql new file mode 100644 index 0000000..d1fab86 --- /dev/null +++ b/team_product/prisma/migrations/20260227053501_add_completed/migration.sql @@ -0,0 +1,15 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Item" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "completed" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Item" ("createdAt", "id", "title", "updatedAt") SELECT "createdAt", "id", "title", "updatedAt" FROM "Item"; +DROP TABLE "Item"; +ALTER TABLE "new_Item" RENAME TO "Item"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/team_product/src/public/app.js b/team_product/src/public/app.js index a08affd..008fffb 100644 --- a/team_product/src/public/app.js +++ b/team_product/src/public/app.js @@ -10,13 +10,13 @@ // ===================================================== // DOM要素の取得 // ===================================================== -const titleInput = document.getElementById('titleInput') -const addButton = document.getElementById('addButton') -const itemList = document.getElementById('itemList') -const emptyMessage = document.getElementById('emptyMessage') +const titleInput = document.getElementById("titleInput"); +const addButton = document.getElementById("addButton"); +const itemList = document.getElementById("itemList"); +const emptyMessage = document.getElementById("emptyMessage"); // ページ読み込み時にログ出力 -console.log('[CLIENT] ページが読み込まれました') +console.log("[CLIENT] ページが読み込まれました"); // ===================================================== // API呼び出し関数 @@ -31,20 +31,20 @@ console.log('[CLIENT] ページが読み込まれました') * - Output: 画面にアイテムを表示 */ async function loadItems() { - console.log('[CLIENT] アイテム一覧を取得中...') + console.log("[CLIENT] アイテム一覧を取得中..."); try { // サーバーにリクエスト(この時点でServer層に処理が移る) - const response = await fetch('/api/items') - const items = await response.json() + const response = await fetch("/api/items"); + const items = await response.json(); - console.log('[CLIENT] 取得完了:', items.length, '件') + console.log("[CLIENT] 取得完了:", items.length, "件"); // 画面を更新 - renderItems(items) + renderItems(items); } catch (error) { - console.error('[CLIENT] エラー:', error) - alert('アイテムの取得に失敗しました') + console.error("[CLIENT] エラー:", error); + alert("アイテムの取得に失敗しました"); } } @@ -57,39 +57,39 @@ async function loadItems() { * - Output: 一覧を再読み込み */ async function addItem() { - const name = titleInput.value.trim() + const name = titleInput.value.trim(); // 入力チェック(Client側のバリデーション) if (!name) { - alert('nameを入力してください') - return + alert("nameを入力してください"); + return; } - console.log('[CLIENT] アイテムを追加:', name) + console.log("[CLIENT] アイテムを追加:", name); try { // サーバーにPOSTリクエスト - const response = await fetch('/api/items', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ title: name }) - }) + const response = await fetch("/api/items", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title: name }), + }); if (!response.ok) { - const error = await response.json() - throw new Error(error.error) + const error = await response.json(); + throw new Error(error.error); } // 入力欄をクリア - titleInput.value = '' + titleInput.value = ""; // 一覧を再読み込み - await loadItems() + await loadItems(); - console.log('[CLIENT] 追加完了') + console.log("[CLIENT] 追加完了"); } catch (error) { - console.error('[CLIENT] エラー:', error) - alert('追加に失敗しました: ' + error.message) + console.error("[CLIENT] エラー:", error); + alert("追加に失敗しました: " + error.message); } } @@ -106,23 +106,23 @@ async function addItem() { * - Output: 一覧を再読み込み(完了itemは下に表示) */ async function completeItem(id) { - console.log('[CLIENT] アイテムを完了切替: ID =', id) + console.log("[CLIENT] アイテムを完了切替: ID =", id); try { const response = await fetch(`/api/items/${id}/complete`, { - method: 'PATCH' - }) + method: "PATCH", + }); if (!response.ok) { - const error = await response.json() - throw new Error(error.error) + const error = await response.json(); + throw new Error(error.error); } - await loadItems() - console.log('[CLIENT] 完了切替完了') + await loadItems(); + console.log("[CLIENT] 完了切替完了"); } catch (error) { - console.error('[CLIENT] エラー:', error) - alert('完了切替に失敗しました: ' + error.message) + console.error("[CLIENT] エラー:", error); + alert("完了切替に失敗しました: " + error.message); } } @@ -139,32 +139,32 @@ async function completeItem(id) { * - Output: 更新後一覧を再描画 */ async function updateItem(id, nameInput) { - const name = nameInput.value.trim() + const name = nameInput.value.trim(); if (!name) { - alert('nameを入力してください') - return + alert("nameを入力してください"); + return; } - console.log('[CLIENT] アイテムを更新:', id, name) + console.log("[CLIENT] アイテムを更新:", id, name); try { const response = await fetch(`/api/items/${id}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name }) - }) + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name }), + }); if (!response.ok) { - const error = await response.json() - throw new Error(error.error) + const error = await response.json(); + throw new Error(error.error); } - await loadItems() - console.log('[CLIENT] 更新完了') + await loadItems(); + console.log("[CLIENT] 更新完了"); } catch (error) { - console.error('[CLIENT] エラー:', error) - alert('更新に失敗しました: ' + error.message) + console.error("[CLIENT] エラー:", error); + alert("更新に失敗しました: " + error.message); } } @@ -177,60 +177,60 @@ async function updateItem(id, nameInput) { */ function renderItems(items) { // リストをクリア - itemList.innerHTML = '' + itemList.innerHTML = ""; // 空メッセージの表示/非表示 - emptyMessage.style.display = items.length === 0 ? 'block' : 'none' + emptyMessage.style.display = items.length === 0 ? "block" : "none"; // 未完了を上、完了を下に並び替え - const sorted = [...items].sort((a, b) => a.completed - b.completed) + const sorted = [...items].sort((a, b) => a.completed - b.completed); // 各アイテムを描画 - sorted.forEach(item => { - const li = document.createElement('li') - li.className = item.completed ? 'item item-completed' : 'item' - - const checkbox = document.createElement('input') - checkbox.type = 'checkbox' - checkbox.className = 'item-checkbox' - checkbox.checked = item.completed - checkbox.addEventListener('change', () => completeItem(item.id)) - - 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(checkbox) - li.appendChild(nameText) - li.appendChild(actions) - itemList.appendChild(li) - }) + sorted.forEach((item) => { + const li = document.createElement("li"); + li.className = item.completed ? "item item-completed" : "item"; + + const checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.className = "item-checkbox"; + checkbox.checked = item.completed; + checkbox.addEventListener("change", () => completeItem(item.id)); + + 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(checkbox); + li.appendChild(nameText); + li.appendChild(actions); + itemList.appendChild(li); + }); } // ===================================================== @@ -238,18 +238,18 @@ function renderItems(items) { // ===================================================== // 追加ボタンクリック -addButton.addEventListener('click', addItem) +addButton.addEventListener("click", addItem); // Enterキーで追加 -titleInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - addItem() +titleInput.addEventListener("keypress", (e) => { + if (e.key === "Enter") { + addItem(); } -}) +}); // ===================================================== // 初期化 // ===================================================== // ページ読み込み時にアイテム一覧を取得 -loadItems() +loadItems(); diff --git a/team_product/src/public/index.html b/team_product/src/public/index.html index 358c487..3724c20 100644 --- a/team_product/src/public/index.html +++ b/team_product/src/public/index.html @@ -1,43 +1,43 @@ - + - - - - Base App - - - -
- -
-

Base App

-

シンプルなリストアプリ

-
+ + + + Base App + + + +
+ +
+

Base App

+

シンプルなリストアプリ

+
- -
- - -
+ +
+ + +
- -
-

アイテム一覧

-
    - -
-

- データがありません。上のフォームから追加してください。 -

-
-
+ +
+

アイテム一覧

+
    + +
+

+ データがありません。上のフォームから追加してください。 +

+
+
- - - + + + diff --git a/team_product/src/server.js b/team_product/src/server.js index c924138..5719851 100644 --- a/team_product/src/server.js +++ b/team_product/src/server.js @@ -7,19 +7,19 @@ * 現在の機能: Create(追加)、Read(一覧表示)、Update(編集) */ -const express = require('express') -const { PrismaClient } = require('@prisma/client') -const path = require('path') +const express = require("express"); +const { PrismaClient } = require("@prisma/client"); +const path = require("path"); -const app = express() -const prisma = new PrismaClient() -const PORT = 3000 +const app = express(); +const prisma = new PrismaClient(); +const PORT = 3000; // ミドルウェア設定 // JSONデータを受け取れるようにする -app.use(express.json()) +app.use(express.json()); // 静的ファイル(HTML、CSS、JS)を提供 -app.use(express.static(path.join(__dirname, 'public'))) +app.use(express.static(path.join(__dirname, "public"))); // ===================================================== // API エンドポイント(ここがServer層の処理) @@ -33,20 +33,20 @@ app.use(express.static(path.join(__dirname, 'public'))) * - Process: DBからアイテムを全件取得 * - Output: アイテム一覧をJSONで返す */ -app.get('/api/items', async (req, res) => { +app.get("/api/items", async (req, res) => { try { // DBからアイテムを取得(新しい順) const items = await prisma.item.findMany({ - orderBy: { createdAt: 'desc' } - }) + orderBy: { createdAt: "desc" }, + }); - console.log('[SERVER] アイテム一覧を取得:', items.length, '件') - res.json(items) + console.log("[SERVER] アイテム一覧を取得:", items.length, "件"); + res.json(items); } catch (error) { - console.error('[SERVER] エラー:', error) - res.status(500).json({ error: 'アイテム取得に失敗しました' }) + console.error("[SERVER] エラー:", error); + res.status(500).json({ error: "アイテム取得に失敗しました" }); } -}) +}); /** * POST /api/items - アイテム作成 @@ -56,27 +56,27 @@ app.get('/api/items', async (req, res) => { * - Process: DBに新しいアイテムを保存 * - Output: 作成したアイテムをJSONで返す */ -app.post('/api/items', async (req, res) => { +app.post("/api/items", async (req, res) => { try { - const { title } = req.body + const { title } = req.body; // バリデーション(入力チェック) - if (!title || title.trim() === '') { - return res.status(400).json({ error: 'タイトルを入力してください' }) + if (!title || title.trim() === "") { + return res.status(400).json({ error: "タイトルを入力してください" }); } // DBにアイテムを保存(永続化!) const item = await prisma.item.create({ - data: { title: title.trim() } - }) + data: { title: title.trim() }, + }); - console.log('[SERVER] アイテムを作成:', item) - res.status(201).json(item) + console.log("[SERVER] アイテムを作成:", item); + res.status(201).json(item); } catch (error) { - console.error('[SERVER] エラー:', error) - res.status(500).json({ error: 'アイテム作成に失敗しました' }) + console.error("[SERVER] エラー:", error); + res.status(500).json({ error: "アイテム作成に失敗しました" }); } -}) +}); /** * PUT /api/items/:id - アイテム編集 @@ -86,32 +86,32 @@ app.post('/api/items', async (req, res) => { * - Process: 対象idのtitleをDB上で更新 * - Output: 更新したアイテムをJSONで返す */ -app.put('/api/items/:id', async (req, res) => { +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() + 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です' }) + return res.status(400).json({ error: "不正なidです" }); } if (!nextName) { - return res.status(400).json({ error: 'nameを入力してください' }) + return res.status(400).json({ error: "nameを入力してください" }); } const item = await prisma.item.update({ where: { id }, - data: { title: nextName } - }) + data: { title: nextName }, + }); - console.log('[SERVER] アイテムを更新:', item) - res.json(item) + console.log("[SERVER] アイテムを更新:", item); + res.json(item); } catch (error) { - console.error('[SERVER] エラー:', error) - res.status(500).json({ error: 'アイテム更新に失敗しました' }) + console.error("[SERVER] エラー:", error); + res.status(500).json({ error: "アイテム更新に失敗しました" }); } -}) +}); /** * PATCH /api/items/:id/complete - アイテム完了 @@ -121,35 +121,35 @@ app.put('/api/items/:id', async (req, res) => { * - Process: 対象idのcompletedをDB上でトグル(true/false) * - Output: 更新したアイテムをJSONで返す */ -app.patch('/api/items/:id/complete', async (req, res) => { +app.patch("/api/items/:id/complete", async (req, res) => { try { - const id = Number(req.params.id) + const id = Number(req.params.id); if (!Number.isInteger(id) || id <= 0) { - return res.status(400).json({ error: '不正なidです' }) + return res.status(400).json({ error: "不正なidです" }); } // 現在のcompletedを取得してトグル - const current = await prisma.item.findUnique({ where: { id } }) + const current = await prisma.item.findUnique({ where: { id } }); const item = await prisma.item.update({ where: { id }, - data: { completed: !current.completed } - }) + data: { completed: !current.completed }, + }); - console.log('[SERVER] アイテムを完了切替:', item) - res.json(item) + console.log("[SERVER] アイテムを完了切替:", item); + res.json(item); } catch (error) { - console.error('[SERVER] エラー:', error) - res.status(500).json({ error: 'アイテム完了に失敗しました' }) + console.error("[SERVER] エラー:", error); + res.status(500).json({ error: "アイテム完了に失敗しました" }); } -}) +}); // ===================================================== // サーバー起動 // ===================================================== app.listen(PORT, () => { - console.log('='.repeat(50)) - console.log('[SERVER] Base App 起動中') - console.log(`[SERVER] URL: http://localhost:${PORT}`) - console.log('='.repeat(50)) -}) + console.log("=".repeat(50)); + console.log("[SERVER] Base App 起動中"); + console.log(`[SERVER] URL: http://localhost:${PORT}`); + console.log("=".repeat(50)); +}); From 8982fa9992f5bad69ff2ee53d835a06c33221f7d Mon Sep 17 00:00:00 2001 From: Yuki Date: Fri, 27 Feb 2026 14:56:16 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E5=AE=8C=E4=BA=86=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/database/app.db | Bin 24576 -> 24576 bytes team_product/src/public/style.css | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/team_product/database/app.db b/team_product/database/app.db index f1e79327306f2915886f9a992fe4b92b966f9e34..7841a5f61e569da75d28dbc4a225a7a822f3dc24 100644 GIT binary patch delta 69 zcmZoTz}Rqrae_3X>O>i5M%9f8Vey(AtgMNNiHvjR&f;DQBv(DD6=h;$5C#e-B_#m` UIvC?Y!q1jXz8^2gymIM700&|iasU7T delta 69 zcmZoTz}Rqrae_3X=tLQ3M$wH4Vey)rtgMNNiHvjR&f;DQBDK^-nb;VFfx<~iNkD-P S#(0ph{`|@Jt;BJ=^#&=p1i diff --git a/team_product/src/public/style.css b/team_product/src/public/style.css index a6bab48..6e6152d 100644 --- a/team_product/src/public/style.css +++ b/team_product/src/public/style.css @@ -156,6 +156,25 @@ header .subtitle { border-color: #3498db; } +/* 完了アイテム */ +.item-completed { + background-color: #f0f0f0; + opacity: 0.7; +} + +.item-completed .item-title { + color: #95a5a6; + text-decoration: line-through; +} + +.item-checkbox { + width: 18px; + height: 18px; + margin-right: 10px; + cursor: pointer; + accent-color: #27ae60; +} + /* 空メッセージ */ .empty-message { text-align: center; From b223c8e09f0fcb14c96668f92c2ff7d0c4c35b4c Mon Sep 17 00:00:00 2001 From: tmakkrhr-ctrl Date: Fri, 27 Feb 2026 15:46:05 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E6=A4=9C=E7=B4=A2=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=81=A8=E5=89=8A=E9=99=A4=E6=A9=9F=E8=83=BD=E3=82=92=E4=BB=98?= =?UTF-8?q?=E3=81=91=E3=81=BE=E3=81=97=E3=81=9F=EF=BC=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- team_product/database/app.db | Bin 24576 -> 24576 bytes .../20260227061729_init/migration.sql | 20 ++ .../migration.sql | 15 + team_product/prisma/schema.prisma | 9 +- team_product/src/public/app.js | 268 +++++++++++++++++- team_product/src/public/index.html | 14 + team_product/src/public/style.css | 129 +++++++++ team_product/src/server.js | 72 ++++- 8 files changed, 513 insertions(+), 14 deletions(-) create mode 100644 team_product/prisma/migrations/20260227061729_init/migration.sql create mode 100644 team_product/prisma/migrations/20260227063115_sync_completed/migration.sql diff --git a/team_product/database/app.db b/team_product/database/app.db index 7841a5f61e569da75d28dbc4a225a7a822f3dc24..c4be2a025cf43a2c8b94781cedc3a740b4bcb1ea 100644 GIT binary patch delta 980 zcma)*%WKp?9LJL;`{=IexJd0m>edHTq$!!qOeQ%9*0K~qMWhvaDC;DdNTIETE{GSq z4=LiwvVwbSlT(89vAFW-sUX?BBOKdLuJ3o*5V) zxpXBna5~&BpVZbsu`d=Y9*eb(7L&2cN^)8~qH60BnD1%yjA`qY)X`N97W&l0L_v#; zNm8WSp#)=0sLr7g=otH!?)b>j140<%%=d_EmgDKs#m&jzreRu!X;Q;N5R&Y|y*V%I z-I<-A;W_Th&sH}l-xM^ZrHw*uMhRyg^mGFnmX48a>CCl#q3!sl=@=Hq&~km^27$j(v+SciyW5g|In$a5`-Tx152BWj4{x`bIiX12|#<+E}+ zJ#@2LPaFgyh!`--eE**oRBKNPnta#-8+(}Y0O}kerenge&H@|il)DacxnmMW%P-QS zL)F@YLurE|a!^BUED_!kFeQQaU<*uvrAlhxDp)+HOvs%~PYuCNMxlgYCoSq4R3^HI zJ1I>h-g>}pC3Ud@maoUp^vV&bHxkM5g>XVOyV>@d;AF5Tfj#hRpN*YODoTI%{;iKI zO0OLL1!{;YpeTWF;4LVMX5_&AK{YDTcqFp@?)mQPax-hm($q9GSxWI%u+YeGSOJq#K_D* z*V4p1Mc2^S&@w48)iT8-*=+JXS?93c$={6(fYL@r<_4xl#wOJH>1j5aVgHjU5B%Paw!Sa-mY3+O{8{`Cy}>-leN7BpDQAFII}%1Df>nbes> ziE;zb1~A}%z<&y8#Xf!~5hhj!VOG|pq$HptIvC?2q#PqFgE}j#M`}(^zK%k9eo;<} zB2cE`=N6EBgg!6O6h{7U4E*2tUu+h1xXG^x6yRZEmSxOKEsyssNzG;AU}6?!1hJSw zikbO0GVtH$Kf%8d>@$B+W;O<44p!F0#6+Npv$&T6$yHBkMVTPN$j({yY?(YG8-qGf LHFo!`T>20IabuYQ diff --git a/team_product/prisma/migrations/20260227061729_init/migration.sql b/team_product/prisma/migrations/20260227061729_init/migration.sql new file mode 100644 index 0000000..620b86a --- /dev/null +++ b/team_product/prisma/migrations/20260227061729_init/migration.sql @@ -0,0 +1,20 @@ +/* + Warnings: + + - You are about to drop the column `completed` on the `Item` table. All the data in the column will be lost. + +*/ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Item" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Item" ("createdAt", "id", "title", "updatedAt") SELECT "createdAt", "id", "title", "updatedAt" FROM "Item"; +DROP TABLE "Item"; +ALTER TABLE "new_Item" RENAME TO "Item"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/team_product/prisma/migrations/20260227063115_sync_completed/migration.sql b/team_product/prisma/migrations/20260227063115_sync_completed/migration.sql new file mode 100644 index 0000000..d1fab86 --- /dev/null +++ b/team_product/prisma/migrations/20260227063115_sync_completed/migration.sql @@ -0,0 +1,15 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Item" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "completed" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Item" ("createdAt", "id", "title", "updatedAt") SELECT "createdAt", "id", "title", "updatedAt" FROM "Item"; +DROP TABLE "Item"; +ALTER TABLE "new_Item" RENAME TO "Item"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/team_product/prisma/schema.prisma b/team_product/prisma/schema.prisma index ec4e6f2..de18bb2 100644 --- a/team_product/prisma/schema.prisma +++ b/team_product/prisma/schema.prisma @@ -13,8 +13,9 @@ datasource db { // アイテムモデル // シンプルなCRUDアプリの基本データ構造 model Item { - id Int @id @default(autoincrement()) // 自動採番のID - title String // アイテム名 - createdAt DateTime @default(now()) // 作成日時 - updatedAt DateTime @updatedAt // 更新日時 + id Int @id @default(autoincrement()) // 自動採番のID + title String // アイテム名 + completed Boolean @default(false) // 完了フラグ + createdAt DateTime @default(now()) // 作成日時 + updatedAt DateTime @updatedAt // 更新日時 } diff --git a/team_product/src/public/app.js b/team_product/src/public/app.js index 5de0204..c11e00e 100644 --- a/team_product/src/public/app.js +++ b/team_product/src/public/app.js @@ -4,7 +4,7 @@ * このコードはブラウザ上で動作します。 * ログはブラウザのF12(開発者ツール)に表示されます。 * - * 現在の機能: Create(追加)、Read(一覧表示)、Update(編集) + * 現在の機能: Create(追加)、Read(一覧表示)、Search(検索)、Update(編集)、Complete(完了)、Delete(削除) */ // ===================================================== @@ -12,8 +12,14 @@ // ===================================================== const titleInput = document.getElementById("titleInput"); const addButton = document.getElementById("addButton"); +const searchButton = document.getElementById("searchButton"); +const searchForm = document.getElementById("searchForm"); +const searchInput = document.getElementById("searchInput"); +const searchStartButton = document.getElementById("searchStartButton"); const itemList = document.getElementById("itemList"); const emptyMessage = document.getElementById("emptyMessage"); +let currentSearchKeyword = ""; +let activeSearchToken = 0; // ページ読み込み時にログ出力 console.log("[CLIENT] ページが読み込まれました"); @@ -41,13 +47,65 @@ async function loadItems() { console.log("[CLIENT] 取得完了:", items.length, "件"); // 画面を更新 - renderItems(items); + renderItems(items, ""); } catch (error) { console.error("[CLIENT] エラー:", error); alert("アイテムの取得に失敗しました"); } } +/** + * アイテムを検索して表示 + * + * IPO: + * - Input: 検索文字列keyword + * - Process: サーバーへ検索リクエスト → DBで部分一致検索 + * - Output: 一致したアイテムのみ表示 + */ +async function searchItems(keyword, token = activeSearchToken) { + console.log("[CLIENT] アイテムを検索:", keyword); + + try { + const response = await fetch( + `/api/items/search?keyword=${encodeURIComponent(keyword)}`, + ); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error); + } + + const items = await response.json(); + + if (token !== activeSearchToken || currentSearchKeyword !== keyword) { + return; + } + + renderItems(items, currentSearchKeyword); + console.log("[CLIENT] 検索完了:", items.length, "件"); + } catch (error) { + console.error("[CLIENT] エラー:", error); + alert("検索に失敗しました: " + error.message); + } +} + +/** + * 現在の表示条件で一覧を再取得 + * + * IPO: + * - Input: なし + * - Process: 検索中なら検索、通常時は全件取得 + * - Output: 最新一覧を表示 + */ +async function refreshItems() { + if (currentSearchKeyword) { + await searchItems(currentSearchKeyword, activeSearchToken); + return; + } + + await loadItems(); +} + /** * アイテムを追加 * @@ -84,7 +142,7 @@ async function addItem() { titleInput.value = ""; // 一覧を再読み込み - await loadItems(); + await refreshItems(); console.log("[CLIENT] 追加完了"); } catch (error) { @@ -118,7 +176,7 @@ async function completeItem(id) { throw new Error(error.error) } - await loadItems() + await refreshItems() console.log('[CLIENT] 完了切替完了') } catch (error) { console.error('[CLIENT] エラー:', error) @@ -160,7 +218,7 @@ async function updateItem(id, nameInput) { throw new Error(error.error); } - await loadItems(); + await refreshItems(); console.log("[CLIENT] 更新完了"); } catch (error) { console.error("[CLIENT] エラー:", error); @@ -168,6 +226,94 @@ async function updateItem(id, nameInput) { } } +/** + * 削除確認モーダルを表示 + * + * IPO: + * - Input: なし + * - Process: はい / いいえ の選択を待つ + * - Output: ユーザー選択をbooleanで返す + */ +function showDeleteConfirm() { + return new Promise((resolve) => { + const overlay = document.createElement("div"); + overlay.className = "confirm-overlay"; + + const dialog = document.createElement("div"); + dialog.className = "confirm-dialog"; + + const message = document.createElement("p"); + message.className = "confirm-message"; + message.textContent = "削除してもよろしいですか?"; + + const buttonArea = document.createElement("div"); + buttonArea.className = "confirm-actions"; + + const yesButton = document.createElement("button"); + yesButton.className = "item-button confirm-yes-button"; + yesButton.textContent = "はい"; + + const noButton = document.createElement("button"); + noButton.className = "item-button confirm-no-button"; + noButton.textContent = "いいえ"; + + const closeDialog = (result) => { + document.body.removeChild(overlay); + resolve(result); + }; + + yesButton.addEventListener("click", () => closeDialog(true)); + noButton.addEventListener("click", () => closeDialog(false)); + overlay.addEventListener("click", (event) => { + if (event.target === overlay) { + closeDialog(false); + } + }); + + buttonArea.appendChild(yesButton); + buttonArea.appendChild(noButton); + dialog.appendChild(message); + dialog.appendChild(buttonArea); + overlay.appendChild(dialog); + document.body.appendChild(overlay); + }); +} + +/** + * アイテムを削除 + * + * IPO: + * - Input: 削除対象のid + * - Process: 確認後にDELETEリクエストでServerへ削除要求 → DB削除 + * - Output: 更新後一覧を再描画 + */ +async function deleteItem(id) { + const shouldDelete = await showDeleteConfirm(); + if (!shouldDelete) { + console.log("[CLIENT] 削除をキャンセル:", id); + return; + } + + console.log("[CLIENT] アイテムを削除:", id); + + try { + const response = await fetch(`/api/items/${id}`, { + method: "DELETE", + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error); + } + + await refreshItems(); + console.log("[CLIENT] 削除完了"); + } catch (error) { + console.error("[CLIENT] エラー:", error); + alert("削除に失敗しました: " + error.message); + } +} + // ===================================================== // 画面描画関数 // ===================================================== @@ -175,7 +321,7 @@ async function updateItem(id, nameInput) { /** * アイテム一覧を画面に描画 */ -function renderItems(items) { +function renderItems(items, keyword = "") { // リストをクリア itemList.innerHTML = ""; @@ -196,9 +342,7 @@ function renderItems(items) { checkbox.checked = item.completed checkbox.addEventListener('change', () => completeItem(item.id)) - const nameText = document.createElement("span"); - nameText.className = "item-title"; - nameText.textContent = item.title; + const nameText = createHighlightedTitleElement(item.title, keyword); const actions = document.createElement("div"); actions.className = "item-actions"; @@ -225,7 +369,15 @@ function renderItems(items) { nameInput.focus(); }); + const deleteButton = document.createElement("button"); + deleteButton.className = "item-button delete-button"; + deleteButton.textContent = "削除"; + deleteButton.addEventListener("click", () => { + deleteItem(item.id); + }); + actions.appendChild(editButton) + actions.appendChild(deleteButton) li.appendChild(checkbox) li.appendChild(nameText) li.appendChild(actions) @@ -233,12 +385,104 @@ function renderItems(items) { }) } +/** + * タイトル中の部分一致箇所をマーカー表示 + * + * IPO: + * - Input: タイトル文字列と検索文字列 + * - Process: 一致箇所を抽出してマーカー要素を構築 + * - Output: 描画用のspan要素を返す + */ +function createHighlightedTitleElement(title, keyword) { + const titleElement = document.createElement("span"); + titleElement.className = "item-title"; + + if (!keyword) { + titleElement.textContent = title; + return titleElement; + } + + const lowerTitle = title.toLowerCase(); + const lowerKeyword = keyword.toLowerCase(); + let startIndex = 0; + + while (startIndex < title.length) { + const matchIndex = lowerTitle.indexOf(lowerKeyword, startIndex); + + if (matchIndex === -1) { + titleElement.appendChild(document.createTextNode(title.slice(startIndex))); + break; + } + + if (matchIndex > startIndex) { + titleElement.appendChild( + document.createTextNode(title.slice(startIndex, matchIndex)), + ); + } + + const marker = document.createElement("span"); + marker.className = "highlight-marker"; + marker.textContent = title.slice(matchIndex, matchIndex + keyword.length); + titleElement.appendChild(marker); + startIndex = matchIndex + keyword.length; + } + + return titleElement; +} + +/** + * 検索フォーム表示を切り替え + * + * IPO: + * - Input: 検索ボタン押下 + * - Process: フォームを開閉、閉じる時は一覧を通常表示へ戻す + * - Output: 画面表示を更新 + */ +async function toggleSearchForm() { + const isHidden = searchForm.classList.contains("hidden"); + + if (isHidden) { + searchForm.classList.remove("hidden"); + searchInput.focus(); + return; + } + + searchForm.classList.add("hidden"); + searchInput.value = ""; + currentSearchKeyword = ""; + activeSearchToken += 1; + await loadItems(); +} + +/** + * 検索開始 + * + * IPO: + * - Input: 検索ボックスの文字列 + * - Process: 検索APIを呼び出す + * - Output: 一致データのみ表示 + */ +async function startSearch() { + const keyword = searchInput.value.trim(); + + if (!keyword) { + alert("検索文字列を入力してください"); + return; + } + + currentSearchKeyword = keyword; + activeSearchToken += 1; + await searchItems(keyword, activeSearchToken); +} + // ===================================================== // イベントリスナー // ===================================================== // 追加ボタンクリック addButton.addEventListener("click", addItem); +searchButton.addEventListener("click", toggleSearchForm); +searchStartButton.addEventListener("click", startSearch); // Enterキーで追加 titleInput.addEventListener("keypress", (e) => { @@ -247,6 +491,12 @@ titleInput.addEventListener("keypress", (e) => { } }); +searchInput.addEventListener("keypress", (e) => { + if (e.key === "Enter") { + startSearch(); + } +}); + // ===================================================== // 初期化 // ===================================================== diff --git a/team_product/src/public/index.html b/team_product/src/public/index.html index 3724c20..8cbfa70 100644 --- a/team_product/src/public/index.html +++ b/team_product/src/public/index.html @@ -25,6 +25,20 @@

Base App

+ +
+ + +
+

アイテム一覧

diff --git a/team_product/src/public/style.css b/team_product/src/public/style.css index 6e6152d..6a677b0 100644 --- a/team_product/src/public/style.css +++ b/team_product/src/public/style.css @@ -78,6 +78,65 @@ header .subtitle { background-color: #2980b9; } +/* 検索 */ +.search-container { + margin-bottom: 20px; +} + +.search-toggle-button { + padding: 10px 20px; + font-size: 0.95rem; + background-color: #7f8c8d; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s; +} + +.search-toggle-button:hover { + background-color: #6c7a7d; +} + +.search-form { + display: flex; + gap: 10px; + margin-top: 12px; +} + +.search-form input { + flex: 1; + padding: 10px 12px; + font-size: 0.95rem; + border: 2px solid #ddd; + border-radius: 8px; + outline: none; + transition: border-color 0.2s; +} + +.search-form input:focus { + border-color: #f39c12; +} + +.search-form button { + padding: 10px 18px; + font-size: 0.9rem; + background-color: #f39c12; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s; +} + +.search-form button:hover { + background-color: #d68910; +} + +.hidden { + display: none; +} + /* アイテム一覧 */ .items-container h2 { font-size: 1.2rem; @@ -113,6 +172,12 @@ header .subtitle { flex: 1; } +.highlight-marker { + background-color: #fff1b8; + padding: 0 2px; + border-radius: 3px; +} + .item-actions { display: flex; gap: 8px; @@ -143,6 +208,14 @@ header .subtitle { background-color: #219150; } +.delete-button { + background-color: #e74c3c; +} + +.delete-button:hover { + background-color: #c0392b; +} + .edit-input { flex: 1; padding: 8px 10px; @@ -185,6 +258,54 @@ header .subtitle { display: none; } +/* 削除確認ダイアログ */ +.confirm-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.45); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + z-index: 1000; +} + +.confirm-dialog { + width: 100%; + max-width: 360px; + background-color: #fff; + border-radius: 10px; + padding: 20px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +.confirm-message { + color: #2c3e50; + margin-bottom: 18px; +} + +.confirm-actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.confirm-yes-button { + background-color: #e74c3c; +} + +.confirm-yes-button:hover { + background-color: #c0392b; +} + +.confirm-no-button { + background-color: #7f8c8d; +} + +.confirm-no-button:hover { + background-color: #6c7a7d; +} + /* レスポンシブ */ @media (max-width: 480px) { .add-form { @@ -194,4 +315,12 @@ header .subtitle { .add-form button { width: 100%; } + + .search-form { + flex-direction: column; + } + + .search-form button { + width: 100%; + } } diff --git a/team_product/src/server.js b/team_product/src/server.js index 4f5b57a..f59fed5 100644 --- a/team_product/src/server.js +++ b/team_product/src/server.js @@ -4,7 +4,7 @@ * このファイルはサーバー側の処理を担当します。 * ログはVSCodeのターミナルに表示されます。 * - * 現在の機能: Create(追加)、Read(一覧表示)、Update(編集) + * 現在の機能: Create(追加)、Read(一覧表示)、Search(検索)、Update(編集)、Complete(完了)、Delete(削除) */ const express = require("express"); @@ -48,6 +48,39 @@ app.get("/api/items", async (req, res) => { } }); +/** + * GET /api/items/search - アイテム検索 + * + * IPO: + * - Input: クライアントから検索文字列(keyword)を受け取る + * - Process: DBでtitleがkeywordを部分一致するデータを検索 + * - Output: 該当するアイテム一覧をJSONで返す + */ +app.get("/api/items/search", async (req, res) => { + try { + const keyword = String(req.query.keyword ?? "").trim(); + + if (!keyword) { + return res.status(400).json({ error: "検索文字列を入力してください" }); + } + + const items = await prisma.item.findMany({ + where: { + title: { + contains: keyword, + }, + }, + orderBy: { createdAt: "desc" }, + }); + + console.log("[SERVER] アイテムを検索:", keyword, items.length, "件"); + res.json(items); + } catch (error) { + console.error("[SERVER] エラー:", error); + res.status(500).json({ error: "検索に失敗しました" }); + } +}); + /** * POST /api/items - アイテム作成 * @@ -113,6 +146,39 @@ app.put("/api/items/:id", async (req, res) => { } }); +/** + * DELETE /api/items/:id - アイテム削除 + * + * IPO: + * - Input: クライアントからidを受け取る + * - Process: 対象idのデータをDB上から削除 + * - Output: 削除したアイテムをJSONで返す + */ +app.delete("/api/items/:id", async (req, res) => { + try { + const id = Number(req.params.id); + + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: "不正なidです" }); + } + + const item = await prisma.item.delete({ + where: { id }, + }); + + console.log("[SERVER] アイテムを削除:", item); + res.json(item); + } catch (error) { + console.error("[SERVER] エラー:", error); + + if (error.code === "P2025") { + return res.status(404).json({ error: "対象データが見つかりません" }); + } + + res.status(500).json({ error: "アイテム削除に失敗しました" }); + } +}); + /** * PATCH /api/items/:id/complete - アイテム完了 * @@ -131,6 +197,10 @@ app.patch('/api/items/:id/complete', async (req, res) => { // 現在のcompletedを取得してトグル const current = await prisma.item.findUnique({ where: { id } }) + if (!current) { + return res.status(404).json({ error: 'アイテムが見つかりません' }) + } + const item = await prisma.item.update({ where: { id }, data: { completed: !current.completed }