Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
eb15df3
POST /api/me/likes を実装
eoate Jun 27, 2026
6417e4b
toUserの存在確認を追加
eoate Jun 27, 2026
4b37feb
feat: 認証を入れる
YuHima03 Jun 27, 2026
7eb4586
自身へのLikeを防止
eoate Jun 27, 2026
e026db7
cokerfile追加
Jun 27, 2026
7ec84b6
Merge pull request #35 from traP-jp/frontdocker
GenMira Jun 27, 2026
4649449
追加
Jun 27, 2026
b4b62f4
Merge pull request #36 from traP-jp/update/frontdockerfile
GenMira Jun 27, 2026
25cf0c4
コピー先変更
Jun 27, 2026
0c6574b
Merge pull request #37 from traP-jp/update/frontdockerfile-2
GenMira Jun 27, 2026
c11ef23
feat: logger, recover middleware
YuHima03 Jun 27, 2026
51a76f8
livesのidとnameを新しいものに変更
arina2147 Jun 27, 2026
b64fc04
変更
Jun 27, 2026
0ade98f
Merge pull request #39 from traP-jp/update/frontdockerfile-3
GenMira Jun 27, 2026
eacae56
Potential fix for pull request finding
YuHima03 Jun 27, 2026
5372fb7
Update OpenAPI auth header
YuHima03 Jun 27, 2026
465b595
Merge pull request #40 from traP-jp/likes.vueの型の変更
arina2147 Jun 27, 2026
b882829
Merge pull request #38 from traP-jp/feat/auth
YuHima03 Jun 27, 2026
2cf3d4b
lives.vueがapenapi .ymlからデータを入手出来るように変更
arina2147 Jun 27, 2026
7aaa11a
Merge pull request #32 from traP-jp/feat/postmelikes
eoate Jun 27, 2026
4126b08
fix
YuHima03 Jun 27, 2026
9f36803
Merge pull request #42 from traP-jp/fix/likeuser
YuHima03 Jun 27, 2026
49f580a
lives.vue で本物のデータを取得
arina2147 Jun 27, 2026
ed13915
Merge pull request #43 from traP-jp/lives.vueで実際の場所からデータを入手&バミーの削除
arina2147 Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Qpid-UI/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .

RUN npm ci && npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
137 changes: 59 additions & 78 deletions Qpid-UI/src/components/Likes.vue
Original file line number Diff line number Diff line change
@@ -1,72 +1,43 @@
<script setup lang="ts">
import { ref, computed } from 'vue'

//注意:色とかはまだ何も決めていません。全部仮です。
import { ref, computed, onMounted } from 'vue'

// タブの切り替え状態を管理 ('liked' = Likeした人, 'likedBy' = Likeされた人)
const activeTab = ref<'liked' | 'likedBy'>('liked')

// Likeした人一覧のダミーデータ
const dummyLikedUsers = [
{
id: 1,
name: 'n3',
bio: '明日晴れますよ(断定)\nうにお願いします。(注文)'
},
{
id: 2,
name: 'Jiro',
bio: 'バックエンドエンジニアです。\nGo言語が好きです。'
},
{
id: 3,
name: 'Saburo',
bio: 'デザイナー兼フロントエンド。\nUI/UXにこだわりがあります!'
},
{
id: 4,
name: 'Shiro',
bio: 'AIに興味があります。\nよろしくお願いします。'
},
{
id: 5,
name: 'Goro',
bio: 'これは自己紹介文のサンプルです、パイソンはいいぞよりも長くこのままだとタブ一覧からはみ出すかもしれないので、先ほどのCSSでしっかり「...」になるかテストするための長い文章です。'
},
{
id: 6,
name: 'Rokuro',
bio: 'プログラミング初心者です!\n楽しく開発したいです。'
}
]

// Likeされた人一覧のダミーデータ(こちらも4人に設定)
const dummyLikedByUsers = [
{
id: 101,
name: 'Hanako',
bio: 'React派ですがVueも触ってみてます!\n仲良くしてください。'
},
{
id: 102,
name: 'Keiko',
bio: '趣味はカフェ巡りです。\n休日はもくもく会によく行きます。'
},
{
id: 103,
name: 'Mari',
bio: 'TypeScript最高!\n型がないと不安になります。'
},
{
id: 104,
name: 'Yumi',
bio: 'インフラエンジニア。\nAWSメインで触ってます。'
// バックエンドの UserSummary 型の定義
interface UserSummary {
username: string
displayName?: string
bio?: string
}

// 本物のデータを入れるための空っぽの箱
const likedUsers = ref<UserSummary[]>([])
const likedByUsers = ref<UserSummary[]>([])

// 画面が開いた瞬間に、ブラウザ標準の機能(fetch)でバックエンドからデータを取ってくる
onMounted(async () => {
try {
// 1. LIKEした人をバックエンドから取得
const resLikes = await fetch('http://localhost:8080/api/me/likes')
if (resLikes.ok) {
likedUsers.value = await resLikes.json()
}

// 2. 自分をLIKEした人をバックエンドから取得
const resLikedBy = await fetch('http://localhost:8080/api/me/liked-by')
if (resLikedBy.ok) {
likedByUsers.value = await resLikedBy.json()
}

} catch (error) {
console.error('APIの取得に失敗しました。サーバーが起動していないか、ログインしていない可能性があります:', error)
}
]
})

// 現在選択されているタブに応じて、表示する配列を切り替える
const displayUsers = computed(() => {
return activeTab.value === 'liked' ? dummyLikedUsers : dummyLikedByUsers
return activeTab.value === 'liked' ? likedUsers.value : likedByUsers.value
})
</script>

Expand All @@ -89,11 +60,15 @@ const displayUsers = computed(() => {
</button>
</div>

<div class="cards-grid">
<div v-for="user in displayUsers" :key="user.id" class="card">
<div v-if="displayUsers.length === 0" class="no-users">
まだユーザーがいません。
</div>

<div v-else class="cards-grid">
<div v-for="user in displayUsers" :key="user.username" class="card">
<div class="icon-placeholder"></div>
<p class="user-name">{{ user.name }}</p>
<p class="user-bio">{{ user.bio }}</p>
<p class="user-name">{{ user.displayName || user.username }}</p>
<p class="user-bio">{{ user.bio || '自己紹介文はまだありません。' }}</p>
</div>
</div>
</div>
Expand All @@ -102,12 +77,20 @@ const displayUsers = computed(() => {
<style scoped>
/* ページ全体を画面の横幅いっぱいまで広げる */
.likes-page {
padding: 40px 5%; /* 左右に少し余白を取りつつ、画面幅いっぱいに広げる */
max-width: 100%; /* ★ここを100%にすることで右端まで伸びます */
padding: 40px 5%;
max-width: 100%;
margin: 0 auto;
}

/* タブのスタイル(変更なし) */
/* データがないときのメッセージ */
.no-users {
font-size: 1.5rem;
color: #777;
text-align: center;
margin-top: 50px;
}

/* タブのスタイル */
.tab-container {
display: flex;
margin-bottom: 40px;
Expand Down Expand Up @@ -138,46 +121,44 @@ const displayUsers = computed(() => {
/* 4列で画面いっぱいに均等に広がるようにする */
.cards-grid {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 画面幅をきっちり4等分して広げる */
gap: 40px; /* カード同士の間隔 */
grid-template-columns: repeat(4, 1fr);
gap: 40px;
}

/* カードの比率を維持したまま拡大 */
.card {
background-color: #f0f0f0;
border: 1px solid #aaa;
border-radius: 20px;
padding: 10%; /* ★固定のpxではなく%にすることで、カード拡大に合わせて内側の余白も広がる */
padding: 10%;
display: flex;
flex-direction: column;
aspect-ratio: 2 / 3; /* ★ここで縦横の比率を固定(横2:縦3) */
aspect-ratio: 2 / 3;
}

/* アイコンの比率も維持 */
.icon-placeholder {
width: 50%; /* ★カード幅の50%の大きさに設定 */
aspect-ratio: 1 / 1; /* ★常に正方形を維持する */
width: 50%;
aspect-ratio: 1 / 1;
border: 1px solid #555;
background-color: #fff;
margin-bottom: 20px;
}

.user-name {
font-size: 1.8rem; /* カードが大きくなるのに合わせて文字も少し大きく */
font-size: 1.8rem;
margin: 0 0 15px 0;
color: #333;
}

.user-bio {
font-size: 1.3rem; /* こちらも少し大きく */
font-size: 1.3rem;
white-space: pre-wrap;
margin: 0;
color: #555;
line-height: 1.6;

/* 紹介文が省略のための設定 */
display: -webkit-box;
-webkit-line-clamp: 4; /* カードが縦に長くなった分、4行まで表示させる */
-webkit-line-clamp: 4;
line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
Expand Down
33 changes: 33 additions & 0 deletions Qpid/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package env

import (
"cmp"
"os"
)

type Env struct {
Environment string
}

const (
EnvironmentLocal = "local"
EnvironmentProduction = "production"
)

func (e *Env) IsLocal() bool {
return e.Environment == EnvironmentLocal
}

func (e *Env) IsProduction() bool {
return e.Environment == EnvironmentProduction
}

func GetEnv() Env {
environment := cmp.Or(os.Getenv("ENVIRONMENT"), EnvironmentLocal)
if environment != EnvironmentLocal && environment != EnvironmentProduction {
environment = EnvironmentLocal
}
return Env{
Environment: environment,
}
}
1 change: 1 addition & 0 deletions Qpid/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ require (
golang.org/x/net v0.56.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/time v0.15.0 // indirect
)
2 changes: 2 additions & 0 deletions Qpid/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15 changes: 0 additions & 15 deletions Qpid/handler/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,7 @@ package handler

import "github.com/labstack/echo/v4"

// GET /api/login
func (h *handler) startLogin(c echo.Context) error {
return notImplemented(c)
}

// POST /api/login/callback
func (h *handler) loginCallback(c echo.Context) error {
return notImplemented(c)
}

// POST /api/signup
func (h *handler) signup(c echo.Context) error {
return notImplemented(c)
}

// POST /api/logout
func (h *handler) logout(c echo.Context) error {
return notImplemented(c)
}
59 changes: 30 additions & 29 deletions Qpid/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,66 @@ import (

"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
"github.com/traP-jp/hackathon26spring_05/Qpid/domain"
echoMiddleware "github.com/labstack/echo/v4/middleware"
"github.com/traP-jp/hackathon26spring_05/Qpid/env"
"github.com/traP-jp/hackathon26spring_05/Qpid/handler/middleware"
"github.com/traP-jp/hackathon26spring_05/Qpid/infrastructure"
"github.com/traP-jp/hackathon26spring_05/Qpid/repository"
"github.com/traP-jp/hackathon26spring_05/Qpid/repository/mock"
)

type handler struct {
env env.Env
repository repository.Repository
sessions sessions.Store
// middlewares
loginUserRetriever domain.LoginUserRetriever
}

func Serve() {
e := echo.New()

// _, err := infrastructure.NewDB()
// if err != nil {
// e.Logger.Fatal(err)
// return
// }

_, err := infrastructure.NewDB()
if err != nil {
e.Logger.Fatal(err)
return
}
repo := mock.NewMockRepository()

h := &handler{
env: env.GetEnv(),
repository: repo,
sessions: sessions.NewCookieStore([]byte(
cmp.Or(os.Getenv("SESSION_SECRET"), "secret"),
)),
loginUserRetriever: middleware.GetLoginUserRetriever(),
}

h.mapRoutes(e)
e.Logger.Fatal(e.Start(":8080"))
}

func (h *handler) mapRoutes(e *echo.Echo) {
api := e.Group("/api")
api := e.Group("/api",
echoMiddleware.RequestLogger(),
echoMiddleware.Recover())
{
login := api.Group("/login")
{
login.GET("", h.startLogin)
login.POST("/callback", h.loginCallback)
}
api.POST("/signup", h.signup)
api.POST("/logout", h.logout)
me := api.Group("/me")
{
me.GET("", h.getMe)
me.PUT("", h.updateMe)
me.GET("/likes", h.listMyLikes)
me.POST("/likes", h.likeUser)
me.GET("/liked-by", h.listUsersWhoLikedMe)
me.POST("/nopes", h.nopeUser)
}
users := api.Group("/users")

// 認証が必要な API 群
authenticated := api.Group("", middleware.AuthenticationMiddleware(&h.env, h.repository))
{
users.GET("/:id", h.getUser)
me := authenticated.Group("/me")
{
me.GET("", h.getMe)
me.PUT("", h.updateMe)
me.GET("/likes", h.listMyLikes)
me.POST("/likes", h.likeUser)
me.GET("/liked-by", h.listUsersWhoLikedMe)
me.POST("/nopes", h.nopeUser)
}
users := authenticated.Group("/users")
{
users.GET("/:id", h.getUser)
}
authenticated.GET("/suggestions", h.listSuggestions)
}
api.GET("/suggestions", h.listSuggestions)
}
}
Loading
Loading