diff --git a/back/src/wos/wos.controller.ts b/back/src/wos/wos.controller.ts index 5b94db8..b47bfcd 100644 --- a/back/src/wos/wos.controller.ts +++ b/back/src/wos/wos.controller.ts @@ -17,6 +17,11 @@ export class WosController { return this.wosService.redeemAll(body.fid); } + @Get('users') + async listUsers() { + return this.wosService.listUsers(); + } + @Get('history/:fid') async getHistory(@Param('fid') fid: string) { return this.wosService.getHistory(fid); diff --git a/back/src/wos/wos.service.ts b/back/src/wos/wos.service.ts index f50cb08..198324b 100644 --- a/back/src/wos/wos.service.ts +++ b/back/src/wos/wos.service.ts @@ -154,6 +154,10 @@ export class WosService { }); } + async listUsers() { + return this.wosUserRepo.find({ order: { created_at: 'DESC' } }); + } + async listCoupons() { return this.wosCouponRepo.find({ order: { created_at: 'DESC' } }); } diff --git a/front/src/stores/wos.store.ts b/front/src/stores/wos.store.ts index c47a571..1084a6d 100644 --- a/front/src/stores/wos.store.ts +++ b/front/src/stores/wos.store.ts @@ -30,6 +30,13 @@ export interface Coupon { created_at: string; } +export interface SavedUser { + fid: string; + nickname: string; + avatar_url: string; + created_at: string; +} + export const useWosStore = defineStore('wos', { state: () => ({ fid: '', @@ -37,6 +44,7 @@ export const useWosStore = defineStore('wos', { results: [] as RedeemResult[], history: [] as HistoryItem[], coupons: [] as Coupon[], + savedUsers: [] as SavedUser[], status: 'idle' as 'idle' | 'loading' | 'success' | 'error', errorMsg: '', }), @@ -78,6 +86,13 @@ export const useWosStore = defineStore('wos', { } catch {} }, + async loadSavedUsers() { + try { + const { data } = await axios.get('/api/users'); + this.savedUsers = data; + } catch {} + }, + async loadCoupons() { try { const { data } = await axios.get('/api/coupons'); diff --git a/front/src/views/HomeView.vue b/front/src/views/HomeView.vue index 3e52f47..c3a12d4 100644 --- a/front/src/views/HomeView.vue +++ b/front/src/views/HomeView.vue @@ -21,6 +21,22 @@

{{ store.errorMsg }}

+ +
+ 최근 유저 +
+ +
+
@@ -52,11 +68,25 @@ import CouponManager from '../components/CouponManager.vue'; const store = useWosStore(); const fidInput = ref(''); -onMounted(() => store.loadCoupons()); +onMounted(() => { + store.loadCoupons(); + store.loadSavedUsers(); +}); async function searchPlayer() { if (!fidInput.value.trim()) return; await store.searchPlayer(fidInput.value.trim()); + store.loadSavedUsers(); +} + +async function selectUser(fid: string) { + fidInput.value = fid; + await store.searchPlayer(fid); + store.loadSavedUsers(); +} + +function onImgErr(e: Event) { + (e.target as HTMLImageElement).style.display = 'none'; } @@ -107,4 +137,23 @@ header h1 { font-size: 1.9rem; color: #f1f5f9; margin: 0 0 8px; } .btn-primary:hover:not(:disabled) { background: #2563eb; } .error-msg { color: #f87171; font-size: 0.9rem; margin: 8px 0 0; } .empty-history { color: #475569; font-size: 0.9rem; margin: 0; text-align: center; } +.saved-users { margin-top: 14px; } +.saved-label { color: #64748b; font-size: 0.8rem; display: block; margin-bottom: 8px; } +.chips { display: flex; flex-wrap: wrap; gap: 8px; } +.chip { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 12px 5px 6px; + background: #0f172a; + border: 1px solid #334155; + border-radius: 20px; + color: #cbd5e1; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.15s; +} +.chip:hover { border-color: #60a5fa; color: #f1f5f9; } +.chip.active { border-color: #3b82f6; background: #1e3a5f; color: #93c5fd; } +.chip-avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; }