From 6da5ee5ab85ed6999945f9b9b6871f325e1472b4 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Sat, 22 Feb 2025 17:50:34 +0900 Subject: [PATCH 01/21] initial commit From 35ddbd7667d733a5638c249d399d9d8f5c50e0f5 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Sat, 22 Feb 2025 18:09:51 +0900 Subject: [PATCH 02/21] =?UTF-8?q?swr=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/package-lock.json | 32 +++++++++++++++++++++++++++++ frontend_training/package.json | 1 + 2 files changed, 33 insertions(+) diff --git a/frontend_training/package-lock.json b/frontend_training/package-lock.json index 86f0bc6..4452e5e 100644 --- a/frontend_training/package-lock.json +++ b/frontend_training/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", + "swr": "^2.3.2", "uuid": "^11.1.0" }, "devDependencies": { @@ -1644,6 +1645,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/esbuild": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", @@ -2667,6 +2677,19 @@ "node": ">=8" } }, + "node_modules/swr": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.2.tgz", + "integrity": "sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2753,6 +2776,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/uuid": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", diff --git a/frontend_training/package.json b/frontend_training/package.json index 4f96718..2d1fa52 100644 --- a/frontend_training/package.json +++ b/frontend_training/package.json @@ -12,6 +12,7 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", + "swr": "^2.3.2", "uuid": "^11.1.0" }, "devDependencies": { From e9ece1c531d31285106ab33894b2f018b7ede17c Mon Sep 17 00:00:00 2001 From: yutoAb Date: Sat, 22 Feb 2025 18:40:33 +0900 Subject: [PATCH 03/21] =?UTF-8?q?fetcher.ts=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/fetcher.ts | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 frontend_training/src/fetcher.ts diff --git a/frontend_training/src/fetcher.ts b/frontend_training/src/fetcher.ts new file mode 100644 index 0000000..ef64ae9 --- /dev/null +++ b/frontend_training/src/fetcher.ts @@ -0,0 +1,47 @@ +/** + * ok以外の場合にステータスコードをmessageとして保持するErrorをスローします。 + * + * @param res + */ +export const handleErrors = (res: Response) => { + if (res.ok) { + return res; + } + throw Error(res.status.toString()); + }; + + /** + * X-Requested-Withヘッダーを付加してfetchします。 + * + * @param url URL + */ + const fetchWithAjaxHeader = (url: string): Promise => { + const header = new Headers(); + header.set('X-Requested-With', 'XMLHttpRequest'); + return fetch(url, { headers: header }); + }; + + /** + * fetchの結果としてok以外が返された場合にErrorをスローします。 + * json以外の場合は、こちらを使用してください。 + * + * @param url + * @param errorHandler + */ + export const fetchWithErrorHandling = ( + url: string, + errorHandler: (res: Response) => Response = handleErrors, + ): Promise => + fetchWithAjaxHeader(url) + .catch((e: string) => { + throw Error(e); + }) + .then(errorHandler); + + /** + * fetchの結果としてok以外が返された場合にErrorをスローします。 + * + * @param url URL + */ + export const fetcher = (url: string) => + fetchWithErrorHandling(url).then((res) => res.json()); \ No newline at end of file From 79895baedbe51e7e13f63e126f47ddcc16bc471c Mon Sep 17 00:00:00 2001 From: yutoAb Date: Sun, 23 Feb 2025 17:17:59 +0900 Subject: [PATCH 04/21] =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=81=AB=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E9=A3=9B=E3=81=B0=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 4 ++++ frontend_training/src/hooks/useList.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 frontend_training/src/hooks/useList.ts diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index afcd568..2e10472 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import "./App.css"; import { v4 as uuid } from "uuid"; import { EditTodo } from "./EditTodo"; +import { useList } from "./hooks/useList"; export type Todo = { id: string; @@ -14,6 +15,9 @@ function App() { const [listTodo, setListTodo] = useState([]); const [todo, setTodo] = useState(""); + const list = useList(); + console.log(list); + const addTodo = () => { if (todo.trim() !== "") { setListTodo([ diff --git a/frontend_training/src/hooks/useList.ts b/frontend_training/src/hooks/useList.ts new file mode 100644 index 0000000..936245d --- /dev/null +++ b/frontend_training/src/hooks/useList.ts @@ -0,0 +1,16 @@ +import useSWR from "swr"; + +type jsonData = { + status: string; + data: []; +}; + +async function fetcher(key: string) { + return fetch(key).then((res) => res.json()); +} + +export function useList() { + const url = "http://localhost/todos"; + const { data } = useSWR(url, fetcher); + return data; +} From 45490c7fd4290fc5d5e5d2e0b5e5420f6ce2cc54 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Sun, 23 Feb 2025 17:19:11 +0900 Subject: [PATCH 05/21] =?UTF-8?q?=E7=8F=BE=E5=9C=A8=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84fetcher=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/fetcher.ts | 47 -------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 frontend_training/src/fetcher.ts diff --git a/frontend_training/src/fetcher.ts b/frontend_training/src/fetcher.ts deleted file mode 100644 index ef64ae9..0000000 --- a/frontend_training/src/fetcher.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * ok以外の場合にステータスコードをmessageとして保持するErrorをスローします。 - * - * @param res - */ -export const handleErrors = (res: Response) => { - if (res.ok) { - return res; - } - throw Error(res.status.toString()); - }; - - /** - * X-Requested-Withヘッダーを付加してfetchします。 - * - * @param url URL - */ - const fetchWithAjaxHeader = (url: string): Promise => { - const header = new Headers(); - header.set('X-Requested-With', 'XMLHttpRequest'); - return fetch(url, { headers: header }); - }; - - /** - * fetchの結果としてok以外が返された場合にErrorをスローします。 - * json以外の場合は、こちらを使用してください。 - * - * @param url - * @param errorHandler - */ - export const fetchWithErrorHandling = ( - url: string, - errorHandler: (res: Response) => Response = handleErrors, - ): Promise => - fetchWithAjaxHeader(url) - .catch((e: string) => { - throw Error(e); - }) - .then(errorHandler); - - /** - * fetchの結果としてok以外が返された場合にErrorをスローします。 - * - * @param url URL - */ - export const fetcher = (url: string) => - fetchWithErrorHandling(url).then((res) => res.json()); \ No newline at end of file From e74d336fea58c2b2e24c576891743d3a07fa4f40 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 11:52:11 +0900 Subject: [PATCH 06/21] =?UTF-8?q?CORS=20=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend_training/app/src/config.php | 2 ++ backend_training/app/src/index.php | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/backend_training/app/src/config.php b/backend_training/app/src/config.php index b36c397..7b8d88c 100644 --- a/backend_training/app/src/config.php +++ b/backend_training/app/src/config.php @@ -3,6 +3,8 @@ declare(strict_types=1); header('Content-Type: application/json'); +header('Access-Control-Allow-Origin: http://localhost:5173'); +header('Access-Control-Allow-Origin: http://127.0.0.1:5173'); // データベース接続情報 $DB_CONFIG = [ diff --git a/backend_training/app/src/index.php b/backend_training/app/src/index.php index b5e3d4d..9c75d9d 100644 --- a/backend_training/app/src/index.php +++ b/backend_training/app/src/index.php @@ -31,6 +31,9 @@ 'DELETE' => [ // TODO: 他のエンドポイントを追加 '#^/todos\?id=(\d+)$#' => 'handleDeleteTodo', + ], + 'OPTIONS' => [ + '#^.*$#' => 'handleOptions' ] ]; @@ -341,3 +344,16 @@ function handleDeleteTodo(PDO $pdo): void } exit; } + +/** + * OPTIONS リクエストを処理する + * CORSヘッダーを設定して 200 OK レスポンスを返す + * + * @return void + */ +function handleOptions(): void +{ + header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); + header('Access-Control-Allow-Headers: Content-Type, Authorization'); + exit; +} From 58f4cccfe487904c7392026193ad1f4d6878abec Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 11:58:38 +0900 Subject: [PATCH 07/21] =?UTF-8?q?'http://localhost:5173',=20'http://127.0.?= =?UTF-8?q?0.1:5173'=E3=81=AE=E4=B8=A1=E6=96=B9=E3=82=92=E8=A8=B1=E5=8F=AF?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend_training/app/src/config.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend_training/app/src/config.php b/backend_training/app/src/config.php index 7b8d88c..6cd9fdf 100644 --- a/backend_training/app/src/config.php +++ b/backend_training/app/src/config.php @@ -3,8 +3,15 @@ declare(strict_types=1); header('Content-Type: application/json'); -header('Access-Control-Allow-Origin: http://localhost:5173'); -header('Access-Control-Allow-Origin: http://127.0.0.1:5173'); + +$allowed_origins = [ + 'http://localhost:5173', + 'http://127.0.0.1:5173' +]; + +if (isset($_SERVER['HTTP_ORIGIN']) && in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins, true)) { + header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); +} // データベース接続情報 $DB_CONFIG = [ From b91065198d869df2b16d6acbe0cf41beb5bdc07e Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:15:57 +0900 Subject: [PATCH 08/21] =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=81=8B=E3=82=89=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=92=E5=8F=96=E5=BE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 18 ++++++++++-------- frontend_training/src/hooks/useList.ts | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 2e10472..416e43f 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -4,10 +4,12 @@ import { v4 as uuid } from "uuid"; import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; +type name = `pending` | `completed` | `active`; + export type Todo = { id: string; - text: string; - isComplete: boolean; + title: string; + name: name; isEdit: boolean; }; @@ -22,7 +24,7 @@ function App() { if (todo.trim() !== "") { setListTodo([ ...listTodo, - { id: uuid(), text: todo, isComplete: false, isEdit: false }, + { id: uuid(), title: todo, name: "pending", isEdit: false }, ]); setTodo(""); } @@ -31,7 +33,7 @@ function App() { const completeTodo = (id: string) => { setListTodo( listTodo.map((item) => - item.id === id ? { ...item, isComplete: true } : item + item.id === id ? { ...item, name: "completed" } : item ) ); }; @@ -44,10 +46,10 @@ function App() { ); }; - const updateTodo = (id: string, newText: string) => { + const updateTodo = (id: string, newTitle: string) => { setListTodo( listTodo.map((item) => - item.id === id ? { ...item, text: newText, isEdit: false } : item + item.id === id ? { ...item, title: newTitle, isEdit: false } : item ) ); }; @@ -73,11 +75,11 @@ function App() {
    {listTodo - .filter((item) => !item.isComplete) + .filter((item) => item.name='completed') .map((item) => (
    -
  • editTodo(item.id)}>{item.text}
  • +
  • editTodo(item.id)}>{item.title}
  • {item.isEdit && ( diff --git a/frontend_training/src/hooks/useList.ts b/frontend_training/src/hooks/useList.ts index 936245d..88cc849 100644 --- a/frontend_training/src/hooks/useList.ts +++ b/frontend_training/src/hooks/useList.ts @@ -1,8 +1,9 @@ import useSWR from "swr"; +import { Todo } from "../App"; type jsonData = { status: string; - data: []; + data: Todo[]; }; async function fetcher(key: string) { @@ -12,5 +13,5 @@ async function fetcher(key: string) { export function useList() { const url = "http://localhost/todos"; const { data } = useSWR(url, fetcher); - return data; + return data?.data; } From ba8151c490474053cc3b23770acc2f3eed9c1dfc Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:19:55 +0900 Subject: [PATCH 09/21] =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=81=8B=E3=82=89=E3=81=AE=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=82=92=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 63 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 416e43f..e231ccc 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import "./App.css"; -import { v4 as uuid } from "uuid"; +// import { v4 as uuid } from "uuid"; import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; @@ -14,52 +14,57 @@ export type Todo = { }; function App() { - const [listTodo, setListTodo] = useState([]); + // const [listTodo, setListTodo] = useState([]); const [todo, setTodo] = useState(""); - const list = useList(); - console.log(list); + const listTodo = useList(); + // console.log(list); const addTodo = () => { if (todo.trim() !== "") { - setListTodo([ - ...listTodo, - { id: uuid(), title: todo, name: "pending", isEdit: false }, - ]); + // setListTodo([ + // ...listTodo, + // { id: uuid(), title: todo, name: "pending", isEdit: false }, + // ]); setTodo(""); } }; const completeTodo = (id: string) => { - setListTodo( - listTodo.map((item) => - item.id === id ? { ...item, name: "completed" } : item - ) - ); + console.log(id); + // setListTodo( + // listTodo.map((item) => + // item.id === id ? { ...item, name: "completed" } : item + // ) + // ); }; const editTodo = (id: string) => { - setListTodo( - listTodo.map((item) => - item.id === id ? { ...item, isEdit: true } : item - ) - ); + console.log(id); + // setListTodo( + // listTodo.map((item) => + // item.id === id ? { ...item, isEdit: true } : item + // ) + // ); }; const updateTodo = (id: string, newTitle: string) => { - setListTodo( - listTodo.map((item) => - item.id === id ? { ...item, title: newTitle, isEdit: false } : item - ) - ); + console.log(id); + console.log(newTitle); + // setListTodo( + // listTodo.map((item) => + // item.id === id ? { ...item, title: newTitle, isEdit: false } : item + // ) + // ); }; const cancelEdit = (id: string) => { - setListTodo( - listTodo.map((item) => - item.id === id ? { ...item, isEdit: false } : item - ) - ); + console.log(id); + // setListTodo( + // listTodo.map((item) => + // item.id === id ? { ...item, isEdit: false } : item + // ) + // ); }; return ( @@ -75,7 +80,7 @@ function App() {
      {listTodo - .filter((item) => item.name='completed') + ?.filter((item) => (item.name = "completed")) .map((item) => (
      From 25187c9a8357f199b6211452f622b6da9594f051 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:29:52 +0900 Subject: [PATCH 10/21] =?UTF-8?q?API=5FURL=20=E5=B0=8E=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/hooks/useList.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend_training/src/hooks/useList.ts b/frontend_training/src/hooks/useList.ts index 88cc849..3507b38 100644 --- a/frontend_training/src/hooks/useList.ts +++ b/frontend_training/src/hooks/useList.ts @@ -1,5 +1,6 @@ import useSWR from "swr"; import { Todo } from "../App"; +import { API_URL } from "../App"; type jsonData = { status: string; @@ -11,7 +12,6 @@ async function fetcher(key: string) { } export function useList() { - const url = "http://localhost/todos"; - const { data } = useSWR(url, fetcher); + const { data } = useSWR(API_URL, fetcher); return data?.data; } From 43c8839a9ba5f55ae44c4951aadc608281cb7157 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:37:10 +0900 Subject: [PATCH 11/21] =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E5=B0=8E=E5=85=A5(=E8=87=AA=E5=8B=95=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=84=A1=E3=81=97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 18 ++++++++----- frontend_training/src/hooks/useCreateTodo.ts | 27 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 frontend_training/src/hooks/useCreateTodo.ts diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index e231ccc..1ab1271 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -3,9 +3,12 @@ import "./App.css"; // import { v4 as uuid } from "uuid"; import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; +import { useCreateTodo } from "./hooks/useCreateTodo"; type name = `pending` | `completed` | `active`; +export const API_URL = "http://localhost/todos"; + export type Todo = { id: string; title: string; @@ -18,15 +21,16 @@ function App() { const [todo, setTodo] = useState(""); const listTodo = useList(); - // console.log(list); + const { createTodo } = useCreateTodo(); - const addTodo = () => { + const addTodo = async () => { if (todo.trim() !== "") { - // setListTodo([ - // ...listTodo, - // { id: uuid(), title: todo, name: "pending", isEdit: false }, - // ]); - setTodo(""); + try { + await createTodo({ title: todo }); + setTodo(""); + } catch (error) { + console.error("Todo の作成に失敗しました", error); + } } }; diff --git a/frontend_training/src/hooks/useCreateTodo.ts b/frontend_training/src/hooks/useCreateTodo.ts new file mode 100644 index 0000000..f63ce93 --- /dev/null +++ b/frontend_training/src/hooks/useCreateTodo.ts @@ -0,0 +1,27 @@ +// import mutate from "swr"; +import { API_URL } from "../App"; +import { Todo } from "../App"; + +export function useCreateTodo() { + async function createTodo(newTodo: Omit) { + const response = await fetch(API_URL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(newTodo), + }); + + if (!response.ok) { + throw new Error("Failed to create a new todo"); + } + + const result = await response.json(); + + // mutate(API_URL); + + return result; + } + + return { createTodo }; +} From 7870de53a590fb4142c62638a5084432d85314dd Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:38:43 +0900 Subject: [PATCH 12/21] =?UTF-8?q?=E8=87=AA=E5=8B=95=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=95=E3=81=9B=E3=82=8B(mutate=20=E3=81=AE=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=88=E6=96=B9=E6=B3=95=E3=81=8C?= =?UTF-8?q?=E9=81=95=E3=81=A3=E3=81=9F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/hooks/useCreateTodo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend_training/src/hooks/useCreateTodo.ts b/frontend_training/src/hooks/useCreateTodo.ts index f63ce93..53ecc74 100644 --- a/frontend_training/src/hooks/useCreateTodo.ts +++ b/frontend_training/src/hooks/useCreateTodo.ts @@ -1,4 +1,4 @@ -// import mutate from "swr"; +import { mutate } from "swr"; import { API_URL } from "../App"; import { Todo } from "../App"; @@ -18,7 +18,7 @@ export function useCreateTodo() { const result = await response.json(); - // mutate(API_URL); + mutate(API_URL); return result; } From 206719443e4950e75a96104363111e9bc536ea4e Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 12:42:48 +0900 Subject: [PATCH 13/21] =?UTF-8?q?=E5=89=8A=E9=99=A4=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 15 ++++++++------- frontend_training/src/hooks/useDeleteTodo.ts | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 frontend_training/src/hooks/useDeleteTodo.ts diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 1ab1271..6b1d669 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -4,6 +4,7 @@ import "./App.css"; import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; import { useCreateTodo } from "./hooks/useCreateTodo"; +import { useDeleteTodo } from "./hooks/useDeleteTodo"; type name = `pending` | `completed` | `active`; @@ -22,6 +23,7 @@ function App() { const listTodo = useList(); const { createTodo } = useCreateTodo(); + const { deleteTodo } = useDeleteTodo(); const addTodo = async () => { if (todo.trim() !== "") { @@ -34,13 +36,12 @@ function App() { } }; - const completeTodo = (id: string) => { - console.log(id); - // setListTodo( - // listTodo.map((item) => - // item.id === id ? { ...item, name: "completed" } : item - // ) - // ); + const completeTodo = async (id: string) => { + try { + await deleteTodo(id); + } catch (error) { + console.error("Todo の削除に失敗しました", error); + } }; const editTodo = (id: string) => { diff --git a/frontend_training/src/hooks/useDeleteTodo.ts b/frontend_training/src/hooks/useDeleteTodo.ts new file mode 100644 index 0000000..9e3d48e --- /dev/null +++ b/frontend_training/src/hooks/useDeleteTodo.ts @@ -0,0 +1,18 @@ +import { mutate } from "swr"; +import { API_URL } from "../App"; + +export function useDeleteTodo() { + async function deleteTodo(id: string) { + const response = await fetch(`${API_URL}?id=${id}`, { + method: "DELETE", + }); + + if (!response.ok) { + throw new Error("Failed to delete todo"); + } + + mutate(API_URL); + } + + return { deleteTodo }; +} From 2772dd69939c9ed09423c0a8c4b1ea3df8a7092d Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:05:19 +0900 Subject: [PATCH 14/21] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E3=81=AE=E6=8C=99?= =?UTF-8?q?=E5=8B=95=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 48 +++++++++---------- frontend_training/src/EditTodo.tsx | 9 ++-- frontend_training/src/hooks/useList.ts | 8 +++- frontend_training/src/hooks/useUpdateTodo.ts | 26 ++++++++++ .../src/utils/convertNameToCompleted.ts | 14 ++++++ 5 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 frontend_training/src/hooks/useUpdateTodo.ts create mode 100644 frontend_training/src/utils/convertNameToCompleted.ts diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 6b1d669..bedd0f7 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -5,8 +5,10 @@ import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; import { useCreateTodo } from "./hooks/useCreateTodo"; import { useDeleteTodo } from "./hooks/useDeleteTodo"; +import { useUpdateTodo } from "./hooks/useUpdateTodo"; +import { convertNameToCompleted } from "./utils/convertNameToCompleted"; -type name = `pending` | `completed` | `active`; +export type name = `pending` | `completed` | `active`; export const API_URL = "http://localhost/todos"; @@ -24,6 +26,12 @@ function App() { const listTodo = useList(); const { createTodo } = useCreateTodo(); const { deleteTodo } = useDeleteTodo(); + const { updateTodo } = useUpdateTodo(); + + // 編集状態を管理する state + const [editingTodos, setEditingTodos] = useState<{ [key: string]: boolean }>( + {} + ); const addTodo = async () => { if (todo.trim() !== "") { @@ -45,31 +53,23 @@ function App() { }; const editTodo = (id: string) => { - console.log(id); - // setListTodo( - // listTodo.map((item) => - // item.id === id ? { ...item, isEdit: true } : item - // ) - // ); + setEditingTodos((prev) => ({ ...prev, [id]: true })); }; - const updateTodo = (id: string, newTitle: string) => { - console.log(id); - console.log(newTitle); - // setListTodo( - // listTodo.map((item) => - // item.id === id ? { ...item, title: newTitle, isEdit: false } : item - // ) - // ); + const updateTodoDetails = async (id: string, newTitle: string, name: name) => { + try { + await updateTodo(id, { + title: newTitle, + completed: convertNameToCompleted(name), + }); + setEditingTodos((prev) => ({ ...prev, [id]: false })); + } catch (error) { + console.error("Todo の更新に失敗しました", error); + } }; const cancelEdit = (id: string) => { - console.log(id); - // setListTodo( - // listTodo.map((item) => - // item.id === id ? { ...item, isEdit: false } : item - // ) - // ); + setEditingTodos((prev) => ({ ...prev, [id]: false })); }; return ( @@ -90,12 +90,12 @@ function App() {
    • editTodo(item.id)}>{item.title}
    • - +
      - {item.isEdit && ( + {editingTodos[item.id] && ( )} diff --git a/frontend_training/src/EditTodo.tsx b/frontend_training/src/EditTodo.tsx index 7c2416e..8e75939 100644 --- a/frontend_training/src/EditTodo.tsx +++ b/frontend_training/src/EditTodo.tsx @@ -1,17 +1,18 @@ import { useState } from "react"; import { Todo } from "./App"; +import { name } from "./App"; type EditTodoProps = { todo: Todo; - updateTodo: (id: string, newText: string) => void; + updateTodoDetails: (id: string, newText: string, name: name) => void; cancelEdit: (id: string) => void; }; export const EditTodo: React.FC = ({ todo, - updateTodo, + updateTodoDetails, cancelEdit, }: EditTodoProps) => { - const [editText, setEditText] = useState(todo.text); + const [editText, setEditText] = useState(todo.title); return (
      @@ -20,7 +21,7 @@ export const EditTodo: React.FC = ({ onChange={(e) => setEditText(e.target.value)} placeholder="TODOを編集" /> - +
      ); diff --git a/frontend_training/src/hooks/useList.ts b/frontend_training/src/hooks/useList.ts index 3507b38..2d7195a 100644 --- a/frontend_training/src/hooks/useList.ts +++ b/frontend_training/src/hooks/useList.ts @@ -13,5 +13,11 @@ async function fetcher(key: string) { export function useList() { const { data } = useSWR(API_URL, fetcher); - return data?.data; + + const processedData = data?.data.map((todo) => ({ + ...todo, + isEdit: false, + })); + + return processedData; } diff --git a/frontend_training/src/hooks/useUpdateTodo.ts b/frontend_training/src/hooks/useUpdateTodo.ts new file mode 100644 index 0000000..278e018 --- /dev/null +++ b/frontend_training/src/hooks/useUpdateTodo.ts @@ -0,0 +1,26 @@ +import { mutate } from "swr"; +import { API_URL } from "../App"; + +export function useUpdateTodo() { + async function updateTodo( + id: string, + updatedData: { title: string; completed: string } + ) { + const response = await fetch(`${API_URL}?id=${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updatedData), + }); + + if (!response.ok) { + throw new Error("Failed to update todo"); + } + + // SWR のキャッシュを更新し、最新のデータを反映 + mutate(API_URL); + } + + return { updateTodo }; +} diff --git a/frontend_training/src/utils/convertNameToCompleted.ts b/frontend_training/src/utils/convertNameToCompleted.ts new file mode 100644 index 0000000..6072964 --- /dev/null +++ b/frontend_training/src/utils/convertNameToCompleted.ts @@ -0,0 +1,14 @@ +import { name } from "../App"; + +export function convertNameToCompleted(name: name): string { + switch (name) { + case "pending": + return "1"; + case "completed": + return "2"; + case "active": + return "3"; + default: + throw new Error(`Invalid name: ${name}`); + } +} From 46f3d4ae38047bb078051941c1de7a3cfa17735a Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:05:42 +0900 Subject: [PATCH 15/21] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=82=B3?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index bedd0f7..bc1123b 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -20,7 +20,6 @@ export type Todo = { }; function App() { - // const [listTodo, setListTodo] = useState([]); const [todo, setTodo] = useState(""); const listTodo = useList(); From f2400e63879168ed49dea655d13b64c85f286846 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:06:22 +0900 Subject: [PATCH 16/21] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=82=B3?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index bc1123b..fcee0af 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; import "./App.css"; -// import { v4 as uuid } from "uuid"; import { EditTodo } from "./EditTodo"; import { useList } from "./hooks/useList"; import { useCreateTodo } from "./hooks/useCreateTodo"; From 5ebd8d6b21169acc6deb19c9fd42d99fac77a8e2 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:13:01 +0900 Subject: [PATCH 17/21] =?UTF-8?q?model=20=E3=82=92=E3=81=BE=E3=81=A8?= =?UTF-8?q?=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 18 ++++++------------ frontend_training/src/EditTodo.tsx | 3 +-- frontend_training/src/hooks/useCreateTodo.ts | 3 +-- frontend_training/src/hooks/useDeleteTodo.ts | 2 +- frontend_training/src/hooks/useList.ts | 3 +-- frontend_training/src/hooks/useUpdateTodo.ts | 2 +- frontend_training/src/models/Todo.ts | 10 ++++++++++ .../src/utils/convertNameToCompleted.ts | 2 +- 8 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 frontend_training/src/models/Todo.ts diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index fcee0af..154b0ca 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -6,17 +6,7 @@ import { useCreateTodo } from "./hooks/useCreateTodo"; import { useDeleteTodo } from "./hooks/useDeleteTodo"; import { useUpdateTodo } from "./hooks/useUpdateTodo"; import { convertNameToCompleted } from "./utils/convertNameToCompleted"; - -export type name = `pending` | `completed` | `active`; - -export const API_URL = "http://localhost/todos"; - -export type Todo = { - id: string; - title: string; - name: name; - isEdit: boolean; -}; +import { name } from "./models/Todo"; function App() { const [todo, setTodo] = useState(""); @@ -54,7 +44,11 @@ function App() { setEditingTodos((prev) => ({ ...prev, [id]: true })); }; - const updateTodoDetails = async (id: string, newTitle: string, name: name) => { + const updateTodoDetails = async ( + id: string, + newTitle: string, + name: name + ) => { try { await updateTodo(id, { title: newTitle, diff --git a/frontend_training/src/EditTodo.tsx b/frontend_training/src/EditTodo.tsx index 8e75939..d02db28 100644 --- a/frontend_training/src/EditTodo.tsx +++ b/frontend_training/src/EditTodo.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; -import { Todo } from "./App"; -import { name } from "./App"; +import { Todo, name } from "./models/Todo"; type EditTodoProps = { todo: Todo; diff --git a/frontend_training/src/hooks/useCreateTodo.ts b/frontend_training/src/hooks/useCreateTodo.ts index 53ecc74..b9a0b37 100644 --- a/frontend_training/src/hooks/useCreateTodo.ts +++ b/frontend_training/src/hooks/useCreateTodo.ts @@ -1,6 +1,5 @@ import { mutate } from "swr"; -import { API_URL } from "../App"; -import { Todo } from "../App"; +import { Todo, API_URL } from "../models/Todo"; export function useCreateTodo() { async function createTodo(newTodo: Omit) { diff --git a/frontend_training/src/hooks/useDeleteTodo.ts b/frontend_training/src/hooks/useDeleteTodo.ts index 9e3d48e..12c5edb 100644 --- a/frontend_training/src/hooks/useDeleteTodo.ts +++ b/frontend_training/src/hooks/useDeleteTodo.ts @@ -1,5 +1,5 @@ import { mutate } from "swr"; -import { API_URL } from "../App"; +import { API_URL } from "../models/Todo"; export function useDeleteTodo() { async function deleteTodo(id: string) { diff --git a/frontend_training/src/hooks/useList.ts b/frontend_training/src/hooks/useList.ts index 2d7195a..37bc124 100644 --- a/frontend_training/src/hooks/useList.ts +++ b/frontend_training/src/hooks/useList.ts @@ -1,6 +1,5 @@ import useSWR from "swr"; -import { Todo } from "../App"; -import { API_URL } from "../App"; +import { Todo, API_URL } from "../models/Todo"; type jsonData = { status: string; diff --git a/frontend_training/src/hooks/useUpdateTodo.ts b/frontend_training/src/hooks/useUpdateTodo.ts index 278e018..bc0e69a 100644 --- a/frontend_training/src/hooks/useUpdateTodo.ts +++ b/frontend_training/src/hooks/useUpdateTodo.ts @@ -1,5 +1,5 @@ import { mutate } from "swr"; -import { API_URL } from "../App"; +import { API_URL } from "../models/Todo"; export function useUpdateTodo() { async function updateTodo( diff --git a/frontend_training/src/models/Todo.ts b/frontend_training/src/models/Todo.ts new file mode 100644 index 0000000..6d3a631 --- /dev/null +++ b/frontend_training/src/models/Todo.ts @@ -0,0 +1,10 @@ +export type name = `pending` | `completed` | `active`; + +export const API_URL = "http://localhost/todos"; + +export type Todo = { + id: string; + title: string; + name: name; + isEdit: boolean; +}; \ No newline at end of file diff --git a/frontend_training/src/utils/convertNameToCompleted.ts b/frontend_training/src/utils/convertNameToCompleted.ts index 6072964..2f4aebf 100644 --- a/frontend_training/src/utils/convertNameToCompleted.ts +++ b/frontend_training/src/utils/convertNameToCompleted.ts @@ -1,4 +1,4 @@ -import { name } from "../App"; +import { name } from "../models/Todo"; export function convertNameToCompleted(name: name): string { switch (name) { From 4ed9dbe19432e1e55676ff178bad1f4ef3f2a5e2 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:18:05 +0900 Subject: [PATCH 18/21] =?UTF-8?q?name=E3=81=AE=E6=83=85=E5=A0=B1=E3=82=82?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 32 +++++++++---------- .../src/utils/convertNameToCompleted.ts | 13 ++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 154b0ca..a1e2016 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -7,6 +7,7 @@ import { useDeleteTodo } from "./hooks/useDeleteTodo"; import { useUpdateTodo } from "./hooks/useUpdateTodo"; import { convertNameToCompleted } from "./utils/convertNameToCompleted"; import { name } from "./models/Todo"; +import { convertNameToJa } from "./utils/convertNameToCompleted"; function App() { const [todo, setTodo] = useState(""); @@ -76,23 +77,22 @@ function App() {
        - {listTodo - ?.filter((item) => (item.name = "completed")) - .map((item) => ( -
        -
        -
      • editTodo(item.id)}>{item.title}
      • - -
        - {editingTodos[item.id] && ( - - )} + {listTodo?.map((item) => ( +
        +
        +
      • editTodo(item.id)}>{item.title}
      • +
        {convertNameToJa(item.name)}
        +
        - ))} + {editingTodos[item.id] && ( + + )} +
        + ))}
      ); diff --git a/frontend_training/src/utils/convertNameToCompleted.ts b/frontend_training/src/utils/convertNameToCompleted.ts index 2f4aebf..49b729d 100644 --- a/frontend_training/src/utils/convertNameToCompleted.ts +++ b/frontend_training/src/utils/convertNameToCompleted.ts @@ -12,3 +12,16 @@ export function convertNameToCompleted(name: name): string { throw new Error(`Invalid name: ${name}`); } } + +export function convertNameToJa(name: name): string { + switch (name) { + case "pending": + return "未完了"; + case "completed": + return "完了"; + case "active": + return "進行中"; + default: + throw new Error(`Invalid name: ${name}`); + } +} From b51bee7db781a735346a8fc15c746bd1766a3920 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:22:54 +0900 Subject: [PATCH 19/21] =?UTF-8?q?name=20=E3=81=AE=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 4 ++-- frontend_training/src/EditTodo.tsx | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index a1e2016..93aeb3d 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -48,12 +48,12 @@ function App() { const updateTodoDetails = async ( id: string, newTitle: string, - name: name + newName: name ) => { try { await updateTodo(id, { title: newTitle, - completed: convertNameToCompleted(name), + completed: convertNameToCompleted(newName), }); setEditingTodos((prev) => ({ ...prev, [id]: false })); } catch (error) { diff --git a/frontend_training/src/EditTodo.tsx b/frontend_training/src/EditTodo.tsx index d02db28..e42f0cd 100644 --- a/frontend_training/src/EditTodo.tsx +++ b/frontend_training/src/EditTodo.tsx @@ -3,7 +3,7 @@ import { Todo, name } from "./models/Todo"; type EditTodoProps = { todo: Todo; - updateTodoDetails: (id: string, newText: string, name: name) => void; + updateTodoDetails: (id: string, newText: string, newName: name) => void; cancelEdit: (id: string) => void; }; export const EditTodo: React.FC = ({ @@ -12,6 +12,7 @@ export const EditTodo: React.FC = ({ cancelEdit, }: EditTodoProps) => { const [editText, setEditText] = useState(todo.title); + const [editName, setEditName] = useState(todo.name); return (
      @@ -20,7 +21,17 @@ export const EditTodo: React.FC = ({ onChange={(e) => setEditText(e.target.value)} placeholder="TODOを編集" /> - + +
      ); From c8ef77157079073e023cf875d24483dbb1a7b0ed Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:25:42 +0900 Subject: [PATCH 20/21] =?UTF-8?q?=E7=B7=A8=E9=9B=86=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=AE=E9=83=A8=E5=88=86=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 93aeb3d..0bbe527 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -80,8 +80,10 @@ function App() { {listTodo?.map((item) => (
      -
    • editTodo(item.id)}>{item.title}
    • -
      {convertNameToJa(item.name)}
      +
      editTodo(item.id)}> +
    • {item.title}
    • +
      {convertNameToJa(item.name)}
      +
      {editingTodos[item.id] && ( From fe3db5ebe24d41afaac9938e7663ad2b4ea5b819 Mon Sep 17 00:00:00 2001 From: yutoAb Date: Tue, 25 Feb 2025 13:27:10 +0900 Subject: [PATCH 21/21] =?UTF-8?q?=E3=82=B3=E3=83=AD=E3=83=B3=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_training/src/App.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 0bbe527..c18c553 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -81,8 +81,7 @@ function App() {
      editTodo(item.id)}> -
    • {item.title}
    • -
      {convertNameToJa(item.name)}
      +
    • {item.title}
    • {convertNameToJa(item.name)}