- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin - Add @vmall/shared/theme.css tokens with html[data-accent] presets - Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage, VAccentSwatch, useAccent) as a Nuxt module - Convert all three apps to kit + utilities; delete ui.css/mall.css and every <style scoped>; consoles get accent presets, mall locked to red - Fix VCard boolean prop default (padding) and PDP/store stale useAsyncData keys on param navigation - Archive adopt-tailwind-design-system; new frontend-ui capability spec
63 lines
1.9 KiB
Vue
63 lines
1.9 KiB
Vue
<script setup lang="ts">
|
|
import { t as pick } from "@vmall/shared";
|
|
import type { Coupon } from "@vmall/shared";
|
|
|
|
definePageMeta({ middleware: "auth" });
|
|
|
|
const { locale, t } = useI18n();
|
|
const { $api } = useNuxtApp();
|
|
const coupons = ref<Coupon[]>([]);
|
|
const loading = ref(true);
|
|
const error = ref("");
|
|
|
|
const statusLabels = computed<Record<string, string>>(() => ({
|
|
claimed: t("user.couponClaimed"),
|
|
redeemed: t("user.couponRedeemed"),
|
|
expired: t("user.couponExpired"),
|
|
}));
|
|
|
|
async function load(): Promise<void> {
|
|
loading.value = true;
|
|
error.value = "";
|
|
try {
|
|
coupons.value = await $api.listMyCoupons();
|
|
} catch {
|
|
error.value = t("user.loadFailed");
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
onMounted(() => void load());
|
|
</script>
|
|
|
|
<template>
|
|
<VCard class="min-h-[560px]">
|
|
<h1 class="mb-4 text-xl font-bold text-text">{{ t("user.couponsTitle") }}</h1>
|
|
<p v-if="error" class="mb-4 border border-danger/30 bg-danger/10 px-3.5 py-2.5 text-danger">{{ error }}</p>
|
|
<div v-else-if="loading" class="py-10 text-muted">{{ t("common.loading") }}</div>
|
|
<UiEmptyState v-else-if="coupons.length === 0" :text="t('user.noCoupons')" />
|
|
<VTable v-else>
|
|
<thead>
|
|
<tr>
|
|
<th>{{ t("user.couponTitle") }}</th>
|
|
<th>{{ t("user.amount") }}</th>
|
|
<th>{{ t("user.threshold") }}</th>
|
|
<th>{{ t("user.expiresAt") }}</th>
|
|
<th>{{ t("user.couponStatus") }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="coupon in coupons" :key="coupon.id">
|
|
<td>{{ pick(coupon.title, locale) }}</td>
|
|
<td><PriceText :amount-minor="coupon.amount_minor" :currency="coupon.currency" /></td>
|
|
<td><PriceText :amount-minor="coupon.threshold_minor" :currency="coupon.currency" /></td>
|
|
<td>{{ coupon.ends_at.slice(0, 10) }}</td>
|
|
<td>{{ statusLabels[coupon.status] ?? coupon.status }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</VTable>
|
|
</VCard>
|
|
</template>
|
|
|