From 157d5e72d84f09d1ffeaab47e0101c730a3b0cf1 Mon Sep 17 00:00:00 2001 From: Tsuboi-coder Date: Thu, 19 Feb 2026 23:18:01 +0900 Subject: [PATCH] feat: Integrate OpenAPI-generated API client, refactor profile data model to use English keys, and update social media fields (X/Instagram) across frontend and backend. --- backend/main.go | 2 +- frontend/Makefile | 1 + frontend/app/profile/page.tsx | 428 +++++++++++++++++++--------------- 3 files changed, 245 insertions(+), 186 deletions(-) diff --git a/backend/main.go b/backend/main.go index f0bcabd..f8aa68d 100644 --- a/backend/main.go +++ b/backend/main.go @@ -68,7 +68,7 @@ func setupAPIServer(client *firestore.Client) *gin.Engine { h := handler.NewHandler(client) router := gin.Default() router.Use(func(c *gin.Context) { - c.Header("Access-Control-Allow-Origin", "http://localhost:3000") + c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type") }) diff --git a/frontend/Makefile b/frontend/Makefile index 3377298..95a9639 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -24,6 +24,7 @@ GENERATOR_OPTS := \ all: generate-client +# openapi.yamlから生成されたソースコードが格納される場所 generate-client: $(GENERATOR) generate $(GENERATOR_OPTS) diff --git a/frontend/app/profile/page.tsx b/frontend/app/profile/page.tsx index e586154..ce909f6 100644 --- a/frontend/app/profile/page.tsx +++ b/frontend/app/profile/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useState } from "react" +import { useState, useEffect } from "react" import { Card, CardContent, CardHeader } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -8,107 +8,146 @@ import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Switch } from "@/components/ui/switch" import ReactMarkdown from "react-markdown" +import { DefaultApi, Configuration } from "@/src/lib/api" + +const apiClient = new DefaultApi(new Configuration({ basePath: "http://localhost:8080" })) -// プロファイルの型定義 interface Profile { - 学籍番号: string - ニックネーム: string - 姓: string - 名: string - 学部: string - 自己紹介: string - LINE: string - Discord: string - GitHub: string - 公開設定: { - 学籍番号: boolean - ニックネーム: boolean - 姓: boolean - 名: boolean - 学部: boolean - 自己紹介: boolean - LINE: boolean - Discord: boolean - GitHub: boolean + studentId: string + nickname: string + lastName: string + firstName: string + faculty: string + selfIntroduction: string + visibility: { + name: boolean + selfIntroduction: boolean + x: boolean + instagram: boolean } } +const defaultProfile: Profile = { + studentId: "", + nickname: "", + lastName: "", + firstName: "", + faculty: "", + selfIntroduction: "", + visibility: { + name: false, + selfIntroduction: false, + x: false, + instagram: false, + }, +} + +// 表示ラベルのマップ +const fieldLabels: Record, string> = { + studentId: "学籍番号", + nickname: "ニックネーム", + lastName: "姓", + firstName: "名", + faculty: "学部", + selfIntroduction: "自己紹介", +} + +const faculties = ["理工学部", "都市科学部", "経済学部", "経営学部", "教育学部"] + export default function ProfilePage() { const [isEditing, setIsEditing] = useState(true) - const [profile, setProfile] = useState({ - 学籍番号: "2024001", - ニックネーム: "たろう", - 姓: "山田", - 名: "太郎", - 学部: "理工学部", - 自己紹介: "**プログラミング**が好きです。特にWeb開発に興味があります。", - LINE: "@yamada_line", - Discord: "yamada#1234", - GitHub: "github.com/yamada", - 公開設定: { - 学籍番号: true, - ニックネーム: true, - 姓: true, - 名: true, - 学部: true, - 自己紹介: true, - LINE: false, - Discord: false, - GitHub: true, - }, - }) + const [profile, setProfile] = useState(defaultProfile) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) - const faculties = ["理工学部", "都市科学部", "経済学部", "経営学部", "教育学部"] + useEffect(() => { + const fetchProfile = async () => { + try { + setIsLoading(true) + setError(null) + const response = await apiClient.apiProfileBasicInfoGet() + const data = response.data + setProfile({ + studentId: data.student_id, + nickname: data.nickname, + lastName: data.last_name, + firstName: data.first_name, + faculty: data.faculty, + selfIntroduction: data.self_introduction, + visibility: { + name: data.visibility.name, + selfIntroduction: data.visibility.self_introduction, + x: data.visibility.x, + instagram: data.visibility.instagram, + }, + }) + } catch (err) { + console.error("プロフィールの取得に失敗しました: ", err) + setError("プロフィールの取得に失敗しました。バックエンドが起動しているか確認してください。") + } finally { + setIsLoading(false) + } + } - const handleSave = () => { - // TODO: APIを呼び出してプロフィールを保存 - setIsEditing(false) - } + fetchProfile() + }, []) const handlePublish = async () => { const payload = { - student_id: profile.学籍番号, - faculty: profile.学部, - last_name: profile.姓, - first_name: profile.名, - nickname: profile.ニックネーム, - self_introduction: profile.自己紹介, + student_id: profile.studentId, + faculty: profile.faculty, + last_name: profile.lastName, + first_name: profile.firstName, + nickname: profile.nickname, + self_introduction: profile.selfIntroduction, visibility: { - name: profile.公開設定.姓 && profile.公開設定.名, - self_introduction: profile.公開設定.自己紹介, - x: profile.公開設定.LINE, - instagram: profile.公開設定.Discord, + name: profile.visibility.name, + self_introduction: profile.visibility.selfIntroduction, + x: profile.visibility.x, + instagram: profile.visibility.instagram, }, } try { - const response = await fetch("http://localhost:8080/api/profile/basic-info", { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }) - - if (response.ok) { + const response = await apiClient.apiProfileBasicInfoPut(payload) + if (response.status === 200) { alert("プロフィールが公開されました!") } else { alert("公開に失敗しました。もう一度お試しください。") } - } catch (error) { - console.error("エラーが発生しました: ", error) + } catch (err) { + console.error("エラーが発生しました: ", err) alert("エラーが発生しました。もう一度お試しください。") } } + if (isLoading) { + return ( +
+

読み込み中...

+
+ ) + } + + if (error) { + return ( +
+

{error}

+
+ ) + } + + const editFields = Object.keys(fieldLabels) as (keyof Omit)[] + + // フィールドに紐づく visibility キー(存在する項目のみ) + const visibilityKeyMap: Partial, keyof Profile["visibility"]>> = { + lastName: "name", + selfIntroduction: "selfIntroduction", + } + return (
- {/*
-

Lumos Profile System

-
-

Lumos Profile System

*/} - {/* スライドトグル */}
@@ -143,137 +182,156 @@ export default function ProfilePage() { {isEditing ? (
- {Object.keys(profile) - .filter((key) => key !== "公開設定") - .map((key) => ( -
-
- - {key === "自己紹介" && isEditing && ( -
- - - - - -
- )} -
- - {key === "自己紹介" ? ( -