쿠폰 자동 입력 사이트 작성
배포 완료됐습니다. 이제 FID 입력란 아래에 **최근 유저** 칩이 표시됩니다. 한 번 조회한 사람은 아바타 + 닉네임 칩으로 저장되어, 다음번엔 클릭 한 번으로 바로 쿠폰을 받을 수 있습니다.
This commit is contained in:
parent
1b43b874a1
commit
608a904768
@ -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);
|
||||
|
||||
@ -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' } });
|
||||
}
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -21,6 +21,22 @@
|
||||
</button>
|
||||
</div>
|
||||
<p class="error-msg" v-if="store.status === 'error'">{{ store.errorMsg }}</p>
|
||||
|
||||
<div class="saved-users" v-if="store.savedUsers.length > 0">
|
||||
<span class="saved-label">최근 유저</span>
|
||||
<div class="chips">
|
||||
<button
|
||||
v-for="u in store.savedUsers"
|
||||
:key="u.fid"
|
||||
class="chip"
|
||||
:class="{ active: store.fid === u.fid }"
|
||||
@click="selectUser(u.fid)"
|
||||
>
|
||||
<img :src="u.avatar_url" class="chip-avatar" @error="onImgErr" />
|
||||
<span>{{ u.nickname }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UserCard v-if="store.player" :player="store.player" :fid="store.fid" />
|
||||
@ -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';
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -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; }
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user