## 작업 결과
**색상 통일 (화이트 테마):**
- `CouponInput.vue` — 결과 행 배경 `#0f172a` → `#f8fafc`, 텍스트/배지 라이트로
- `CouponManager.vue` — 입력창/쿠폰 리스트 배경/텍스트 전체 라이트로
- `HistoryList.vue` — 이력 아이템 배경/텍스트 라이트로, 스크롤바도 라이트
**CI/CD 설정:**
- Gitea `git.wageulwageul.com/hyoseung/coupon` 레포 생성
- `.gitea/workflows/deploy.yml` — front/back 각각 빌드 후 rsync 배포
- 백엔드 `ubuntu` PM2 → `hyoseung` PM2로 마이그레이션 (CI/CD 권한 문제 해결)
- 첫 푸시에 CI/CD 자동 실행 완료 ✅
이제부터는 `autoCoupon/` 폴더에서 코드 수정 후 `git push origin main` 하면 자동 배포됩니다.
112 lines
3.6 KiB
Vue
112 lines
3.6 KiB
Vue
<template>
|
||
<div class="coupon-manager">
|
||
<div class="manager-header">
|
||
<h3>🎟️ 쿠폰 관리</h3>
|
||
<span class="active-count">{{ activeCoupons }}개</span>
|
||
</div>
|
||
|
||
<div class="add-form">
|
||
<input v-model="newCode" type="text" placeholder="쿠폰 코드" class="input code-input" />
|
||
<button class="btn btn-add" @click="add" :disabled="!newCode">추가</button>
|
||
</div>
|
||
|
||
<div class="coupon-list" v-if="store.coupons.length > 0">
|
||
<div
|
||
v-for="c in store.coupons"
|
||
:key="c.id"
|
||
class="coupon-item"
|
||
:class="{ expired: c.is_expired }"
|
||
>
|
||
<div class="coupon-info">
|
||
<span class="coupon-code">{{ c.code }}</span>
|
||
</div>
|
||
<div class="coupon-actions">
|
||
<span v-if="c.is_expired" class="badge badge-expired">만료</span>
|
||
<span v-else class="badge badge-active">활성</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="empty" v-else>등록된 쿠폰이 없습니다.</p>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue';
|
||
import { useWosStore } from '../stores/wos.store';
|
||
|
||
const store = useWosStore();
|
||
const newCode = ref('');
|
||
|
||
const activeCoupons = computed(() => store.coupons.filter((c) => !c.is_expired).length);
|
||
|
||
async function add() {
|
||
if (!newCode.value.trim()) return;
|
||
const code = newCode.value.trim();
|
||
await store.addCoupon(code, code);
|
||
newCode.value = '';
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.coupon-manager { display: flex; flex-direction: column; gap: 14px; }
|
||
.manager-header { display: flex; align-items: center; justify-content: space-between; }
|
||
.manager-header h3 { margin: 0; color: #1e293b; font-size: 1rem; }
|
||
.active-count { background: #eff6ff; color: #3b82f6; padding: 3px 10px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; }
|
||
.add-form { display: flex; gap: 8px; flex-wrap: wrap; }
|
||
.input {
|
||
padding: 8px 12px;
|
||
background: #f8fafc;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 8px;
|
||
color: #1e293b;
|
||
font-size: 0.9rem;
|
||
flex: 1;
|
||
min-width: 120px;
|
||
}
|
||
.input:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.1); }
|
||
.code-input { font-family: monospace; }
|
||
.btn-add {
|
||
padding: 8px 16px;
|
||
background: #22c55e;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
white-space: nowrap;
|
||
}
|
||
.btn-add:disabled { opacity: 0.4; cursor: not-allowed; }
|
||
.btn-add:hover:not(:disabled) { background: #16a34a; }
|
||
.coupon-list { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow-y: auto; }
|
||
.coupon-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 10px 14px;
|
||
background: #f8fafc;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 8px;
|
||
border-left: 3px solid #22c55e;
|
||
gap: 8px;
|
||
}
|
||
.coupon-item.expired { border-left-color: #a855f7; opacity: 0.6; }
|
||
.coupon-info { display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
||
.coupon-code { color: #3b82f6; font-family: monospace; font-size: 0.9rem; }
|
||
.coupon-actions { display: flex; gap: 6px; align-items: center; }
|
||
.btn-toggle {
|
||
padding: 3px 10px;
|
||
border: none;
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
font-size: 0.78rem;
|
||
font-weight: 600;
|
||
}
|
||
.btn-toggle.on { background: #dcfce7; color: #16a34a; }
|
||
.btn-toggle.off { background: #f1f5f9; color: #64748b; }
|
||
.badge { padding: 3px 10px; border-radius: 20px; font-size: 0.78rem; font-weight: 600; }
|
||
.badge-active { background: #dcfce7; color: #16a34a; }
|
||
.badge-expired { background: #f3e8ff; color: #9333ea; }
|
||
.empty { color: #94a3b8; font-size: 0.9rem; margin: 0; text-align: center; }
|
||
</style>
|