<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import { usageAPI } from "@/api";
import EmptyState from "@/components/common/EmptyState.vue";
import PageHeader from "@/components/common/PageHeader.vue";
import type { AccessUserStatus, AdminBillingUserDetail, AdminBillingUserSummary, AdminUsageRow, AuthUser, BillingPlanDetail, ResourceEntitlementSummary, UsageLedgerRow } from "@/types";

const users = ref<AdminBillingUserSummary[]>([]);
const plans = ref<BillingPlanDetail[]>([]);
const detail = ref<AdminBillingUserDetail | null>(null);
const selectedUserId = ref<string | null>(null);
const loading = ref(true);
const detailLoading = ref(false);
const error = ref<string | null>(null);
const mutationError = ref<string | null>(null);
const pending = ref<string | null>(null);
const page = ref(1);
const pageSize = ref(25);
const total = ref(0);
const stateAuthority = ref("pk01-postgres");
const auditTab = ref<"usage" | "ledger">("usage");
const auditLoading = ref(false);
const auditError = ref<string | null>(null);
const auditUsageRows = ref<AdminUsageRow[]>([]);
const auditLedgerRows = ref<UsageLedgerRow[]>([]);
const auditTotal = ref(0);

const filters = reactive({ search: "", status: "", role: "" });
const auditFilters = reactive({ search: "", userId: "", serviceId: "", resourceType: "", apiKeyId: "", status: "", kind: "", source: "", idempotencyKey: "", from: "", to: "" });
const createForm = reactive({ email: "", username: "", displayName: "", password: "", role: "user", status: "active", initialCredits: 0 });
const editForm = reactive({ email: "", username: "", displayName: "", password: "", role: "user", status: "active", planId: "default" });
const creditForm = reactive({ deltaCredits: 0, reason: "admin_adjust", idempotencyKey: "" });

const selectedSummary = computed(() => detail.value?.summary ?? users.value.find((item) => item.user?.id === selectedUserId.value) ?? null);
const selectedUser = computed(() => selectedSummary.value?.user ?? null);
const selectedEntitlements = computed<ResourceEntitlementSummary[]>(() => selectedSummary.value?.entitlements ?? []);
const ledgerRows = computed(() => detail.value?.ledger?.rows ?? []);
const apiKeys = computed(() => detail.value?.apiKeys?.keys ?? []);
const reservations = computed(() => detail.value?.reservations?.rows ?? detail.value?.reservations?.active ?? selectedSummary.value?.reservations?.active ?? []);
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)));

onMounted(async () => {
  await loadPlans();
  await refresh();
  await refreshAudit();
});

async function loadPlans(): Promise<void> {
  const response = await usageAPI.adminPlans();
  if (response.ok) plans.value = response.data?.plans ?? [];
}

async function refresh(): Promise<void> {
  loading.value = true;
  const response = await usageAPI.adminUsers({ page: page.value, pageSize: pageSize.value, search: filters.search.trim(), status: filters.status, role: filters.role });
  if (!response.ok) {
    error.value = response.error ?? `HTTP ${response.status}`;
    users.value = [];
    total.value = 0;
  } else {
    const payload = response.data ?? {};
    users.value = payload.users ?? [];
    total.value = Number(payload.total ?? payload.count ?? users.value.length);
    stateAuthority.value = String(payload.state?.stateAuthority ?? "pk01-postgres");
    error.value = null;
    if (!selectedUserId.value && users.value[0]?.user?.id) await selectUser(users.value[0].user.id);
  }
  loading.value = false;
}

async function selectUser(userId: string | undefined): Promise<void> {
  if (!userId) return;
  selectedUserId.value = userId;
  detailLoading.value = true;
  mutationError.value = null;
  const response = await usageAPI.adminUser(userId);
  if (!response.ok) {
    mutationError.value = response.error ?? `HTTP ${response.status}`;
    detail.value = null;
  } else {
    detail.value = response.data?.detail ?? null;
    fillEditForm(detail.value?.summary?.user ?? users.value.find((item) => item.user?.id === userId)?.user ?? null);
  }
  detailLoading.value = false;
}

function fillEditForm(user: (AuthUser & { email?: string }) | null | undefined): void {
  editForm.email = user?.email ?? "";
  editForm.username = user?.username ?? "";
  editForm.displayName = user?.displayName ?? "";
  editForm.password = "";
  editForm.role = user?.role === "admin" ? "admin" : "user";
  editForm.status = String(user?.status ?? "active");
  editForm.planId = selectedSummary.value?.planId ?? "default";
}

async function createUser(): Promise<void> {
  pending.value = "create";
  mutationError.value = null;
  const response = await usageAPI.createAdminUser({ ...createForm, initialCredits: Number(createForm.initialCredits || 0) });
  pending.value = null;
  if (!response.ok) {
    mutationError.value = response.error ?? `HTTP ${response.status}`;
    return;
  }
  createForm.email = "";
  createForm.username = "";
  createForm.displayName = "";
  createForm.password = "";
  createForm.initialCredits = 0;
  await refresh();
  const userId = response.data?.user?.id;
  if (userId) await selectUser(userId);
}

async function saveUser(): Promise<void> {
  const userId = selectedUser.value?.id;
  if (!userId) return;
  pending.value = `save:${userId}`;
  mutationError.value = null;
  const body: Record<string, unknown> = {
    email: editForm.email.trim(),
    username: editForm.username.trim(),
    displayName: editForm.displayName.trim(),
    role: editForm.role,
    status: editForm.status,
    planId: editForm.planId || "default"
  };
  if (editForm.password) body.password = editForm.password;
  const response = await usageAPI.updateAdminUser(userId, body);
  pending.value = null;
  if (!response.ok) {
    mutationError.value = response.error ?? `HTTP ${response.status}`;
    return;
  }
  await refresh();
  await selectUser(userId);
}

async function setUserStatus(userId: string | undefined, status: AccessUserStatus): Promise<void> {
  if (!userId) return;
  pending.value = `status:${userId}`;
  mutationError.value = null;
  const response = await usageAPI.updateAdminUserStatus(userId, status);
  pending.value = null;
  if (!response.ok) {
    mutationError.value = response.error ?? `HTTP ${response.status}`;
    return;
  }
  await refresh();
  await selectUser(userId);
}

async function adjustCredits(): Promise<void> {
  const userId = selectedUser.value?.id;
  if (!userId) return;
  pending.value = `credits:${userId}`;
  mutationError.value = null;
  const body: Record<string, unknown> = { deltaCredits: Number(creditForm.deltaCredits), reason: creditForm.reason.trim() || "admin_adjust" };
  if (creditForm.idempotencyKey.trim()) body.idempotencyKey = creditForm.idempotencyKey.trim();
  const response = await usageAPI.adjustAdminCredits(userId, body);
  pending.value = null;
  if (!response.ok) {
    mutationError.value = response.error ?? `HTTP ${response.status}`;
    return;
  }
  creditForm.deltaCredits = 0;
  creditForm.idempotencyKey = "";
  await refresh();
  await selectUser(userId);
  await refreshAudit();
}

function auditParams(extra: Record<string, unknown> = {}): Record<string, unknown> {
  const params: Record<string, unknown> = { page: 1, pageSize: 25, ...extra };
  for (const [key, value] of Object.entries(auditFilters)) {
    const text = String(value ?? "").trim();
    if (text) params[key] = text;
  }
  if (auditTab.value === "usage") delete params.kind;
  if (auditTab.value === "usage") delete params.source;
  if (auditTab.value === "ledger") delete params.serviceId;
  if (auditTab.value === "ledger") delete params.resourceType;
  if (auditTab.value === "ledger") delete params.apiKeyId;
  return params;
}

async function refreshAudit(): Promise<void> {
  auditLoading.value = true;
  auditError.value = null;
  const response = auditTab.value === "usage" ? await usageAPI.adminUsage(auditParams()) : await usageAPI.adminLedger(auditParams());
  auditLoading.value = false;
  if (!response.ok) {
    auditError.value = response.error ?? `HTTP ${response.status}`;
    auditUsageRows.value = [];
    auditLedgerRows.value = [];
    auditTotal.value = 0;
    return;
  }
  auditTotal.value = Number(response.data?.total ?? response.data?.count ?? 0);
  if (auditTab.value === "usage") auditUsageRows.value = (response.data?.rows ?? []) as AdminUsageRow[];
  else auditLedgerRows.value = (response.data?.rows ?? []) as UsageLedgerRow[];
}

function setAuditTab(tab: "usage" | "ledger"): void {
  auditTab.value = tab;
  void refreshAudit();
}

function downloadAudit(): void {
  const url = auditTab.value === "usage" ? usageAPI.adminUsageExportUrl(auditParams({ limit: 1000 })) : usageAPI.adminLedgerExportUrl(auditParams({ limit: 1000 }));
  window.location.assign(url);
}

async function nextPage(delta: number): Promise<void> {
  page.value = Math.min(totalPages.value, Math.max(1, page.value + delta));
  await refresh();
}

function nextStatus(status: unknown): AccessUserStatus {
  return String(status ?? "") === "disabled" ? "active" : "disabled";
}

function statusActionLabel(status: unknown): string {
  return String(status ?? "") === "disabled" ? "启用" : "禁用";
}

function formatNumber(value: unknown): string {
  const number = Number(value ?? 0);
  if (!Number.isFinite(number)) return "0";
  return new Intl.NumberFormat("zh-CN").format(number);
}

function formatSignedCredits(value: unknown): string {
  const number = Number(value ?? 0);
  const sign = number > 0 ? "+" : "";
  return `${sign}${formatNumber(number)}`;
}

function formatDate(value: unknown): string {
  const text = String(value ?? "").trim();
  if (!text) return "-";
  const date = new Date(text);
  if (Number.isNaN(date.getTime())) return text;
  return date.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" });
}

function entitlementLimit(current: unknown, limit: unknown, unlimited: unknown): string {
  return `${formatNumber(current)} / ${unlimited ? "unlimited" : formatNumber(limit)}`;
}

function metadataPreview(value: unknown): string {
  if (!value || typeof value !== "object") return "{}";
  return JSON.stringify(value).slice(0, 180);
}
</script>

<template>
  <section class="route-stack">
    <PageHeader eyebrow="Admin" title="用户与账务" description="Sub2API-style 用户管理工作面；用户、余额、API key、ledger 和 reservation 均来自 PK01 user-billing authority。" />
    <EmptyState v-if="loading" title="正在加载用户" description="正在通过 Cloud API admin 代理读取 user-billing 用户列表。" />
    <EmptyState v-else-if="error" title="管理员用户接口不可用" :description="error" />
    <template v-else>
      <p v-if="mutationError" class="form-error admin-action-error">{{ mutationError }}</p>

      <section class="data-panel admin-users-toolbar">
        <header class="panel-header">
          <h2>用户查询</h2>
          <small>{{ stateAuthority }} / {{ formatNumber(total) }} users</small>
        </header>
        <div class="admin-users-filters">
          <input v-model="filters.search" class="input" placeholder="搜索用户名、邮箱或 user id" @keyup.enter="refresh" />
          <select v-model="filters.status" class="input" @change="refresh"><option value="">全部状态</option><option value="active">active</option><option value="disabled">disabled</option><option value="pending">pending</option></select>
          <select v-model="filters.role" class="input" @change="refresh"><option value="">全部角色</option><option value="user">user</option><option value="admin">admin</option></select>
          <button class="btn btn-secondary" type="button" @click="refresh">刷新</button>
        </div>
      </section>

      <section class="data-panel admin-audit-panel">
        <header class="panel-header">
          <h2>运营审计</h2>
          <small>{{ auditLoading ? "loading" : `${formatNumber(auditTotal)} rows` }}</small>
        </header>
        <div class="admin-users-filters audit-filters">
          <button class="table-action" type="button" :data-selected="auditTab === 'usage'" @click="setAuditTab('usage')">Usage</button>
          <button class="table-action" type="button" :data-selected="auditTab === 'ledger'" @click="setAuditTab('ledger')">Ledger</button>
          <input v-model="auditFilters.search" class="input" placeholder="搜索用户、记录或原因" @keyup.enter="refreshAudit" />
          <input v-model="auditFilters.userId" class="input" placeholder="user id" @keyup.enter="refreshAudit" />
          <input v-if="auditTab === 'usage'" v-model="auditFilters.serviceId" class="input" placeholder="service" @keyup.enter="refreshAudit" />
          <input v-if="auditTab === 'usage'" v-model="auditFilters.resourceType" class="input" placeholder="resource" @keyup.enter="refreshAudit" />
          <input v-if="auditTab === 'usage'" v-model="auditFilters.apiKeyId" class="input" placeholder="api key id" @keyup.enter="refreshAudit" />
          <input v-if="auditTab === 'ledger'" v-model="auditFilters.kind" class="input" placeholder="kind" @keyup.enter="refreshAudit" />
          <input v-if="auditTab === 'ledger'" v-model="auditFilters.source" class="input" placeholder="source" @keyup.enter="refreshAudit" />
          <input v-model="auditFilters.status" class="input" placeholder="status" @keyup.enter="refreshAudit" />
          <input v-model="auditFilters.from" class="input" type="datetime-local" @change="refreshAudit" />
          <input v-model="auditFilters.to" class="input" type="datetime-local" @change="refreshAudit" />
          <button class="btn btn-secondary" type="button" @click="refreshAudit">过滤</button>
          <button class="btn btn-secondary" type="button" @click="downloadAudit">CSV</button>
        </div>
        <p v-if="auditError" class="form-error admin-action-error">{{ auditError }}</p>
        <table v-if="auditTab === 'usage' && auditUsageRows.length" class="data-table usage-table audit-table">
          <thead><tr><th>Time</th><th>User</th><th>Service</th><th>Resource</th><th>Credits</th><th>Status</th><th>Metadata</th></tr></thead>
          <tbody>
            <tr v-for="row in auditUsageRows" :key="row.id">
              <td>{{ formatDate(row.createdAt) }}<small><code>{{ row.id }}</code></small></td>
              <td>{{ row.username || row.email || row.userId }}<small>{{ row.userId }}</small></td>
              <td><code>{{ row.serviceId }}</code><small>{{ row.apiKeyId || "session/internal" }}</small></td>
              <td><code>{{ row.resourceType }}</code><small>{{ formatNumber(row.quantity) }} {{ row.unit || "unit" }}</small></td>
              <td>{{ formatNumber(row.credits) }}</td>
              <td><span class="status-pill" :data-status="row.status">{{ row.status }}</span></td>
              <td><small>{{ metadataPreview(row.metadata) }}</small></td>
            </tr>
          </tbody>
        </table>
        <table v-else-if="auditTab === 'ledger' && auditLedgerRows.length" class="data-table usage-table audit-table">
          <thead><tr><th>Time</th><th>User</th><th>Action</th><th>Delta</th><th>Before/After</th><th>Operator</th><th>Idempotency</th></tr></thead>
          <tbody>
            <tr v-for="row in auditLedgerRows" :key="row.id">
              <td>{{ formatDate(row.createdAt) }}<small><code>{{ row.id }}</code></small></td>
              <td>{{ row.username || row.email || row.userId }}<small>{{ row.userId }}</small></td>
              <td>{{ row.kind }}<small>{{ row.source }} / {{ row.status }} / {{ row.reason }}</small></td>
              <td>{{ formatSignedCredits(row.deltaCredits) }}</td>
              <td>{{ formatNumber(row.balanceBefore) }} -> {{ formatNumber(row.balanceAfter) }}</td>
              <td>{{ row.operatorUsername || row.operatorUserId || "-" }}</td>
              <td><code>{{ row.idempotencyKey || "-" }}</code></td>
            </tr>
          </tbody>
        </table>
        <p v-else class="muted-box">当前筛选下没有 {{ auditTab }} 记录。</p>
      </section>

      <div class="admin-users-layout">
        <section class="data-panel admin-users-list-panel">
          <header class="panel-header">
            <h2>用户列表</h2>
            <small>page {{ page }} / {{ totalPages }}</small>
          </header>
          <table v-if="users.length" class="data-table usage-table admin-billing-table">
            <thead><tr><th>User</th><th>Status</th><th>Credits</th><th>Usage</th><th>Keys</th><th>Action</th></tr></thead>
            <tbody>
              <tr v-for="item in users" :key="item.user?.id ?? item.user?.username" :data-selected="selectedUserId === item.user?.id">
                <td>
                  <button class="link-button" type="button" @click="selectUser(item.user?.id)"><strong>{{ item.user?.displayName || item.user?.username || item.user?.id }}</strong></button>
                  <small>{{ item.user?.email || item.user?.username }} · {{ item.planId ?? "default" }}</small>
                </td>
                <td><span class="status-pill" :data-status="item.user?.status">{{ item.user?.role }} / {{ item.user?.status }}</span></td>
                <td><strong>{{ formatNumber(item.credits?.available) }}</strong><small>{{ formatNumber(item.credits?.balance) }} balance · {{ formatNumber(item.credits?.reserved) }} reserved</small></td>
                <td><strong>{{ formatNumber(item.usage?.totalCredits) }}</strong><small>{{ formatNumber(item.usage?.recordCount) }} records</small></td>
                <td><strong>{{ formatNumber(item.apiKeys?.activeCount) }}</strong><small>{{ formatNumber(item.apiKeys?.totalCount) }} total</small></td>
                <td><button class="table-action" type="button" :disabled="pending === `status:${item.user?.id}`" @click="setUserStatus(item.user?.id, nextStatus(item.user?.status))">{{ statusActionLabel(item.user?.status) }}</button></td>
              </tr>
            </tbody>
          </table>
          <p v-else class="muted-box">没有匹配用户。</p>
          <footer class="pagination-row">
            <button class="table-action" type="button" :disabled="page <= 1" @click="nextPage(-1)">上一页</button>
            <button class="table-action" type="button" :disabled="page >= totalPages" @click="nextPage(1)">下一页</button>
          </footer>
        </section>

        <section class="data-panel admin-user-create-panel">
          <header class="panel-header"><h2>创建用户</h2><small>写入 user-billing</small></header>
          <div class="form-grid two-col">
            <label>邮箱<input v-model="createForm.email" class="input" type="email" /></label>
            <label>用户名<input v-model="createForm.username" class="input" /></label>
            <label>显示名<input v-model="createForm.displayName" class="input" /></label>
            <label>初始 credit<input v-model.number="createForm.initialCredits" class="input" type="number" /></label>
            <label>角色<select v-model="createForm.role" class="input"><option value="user">user</option><option value="admin">admin</option></select></label>
            <label>状态<select v-model="createForm.status" class="input"><option value="active">active</option><option value="pending">pending</option><option value="disabled">disabled</option></select></label>
            <label class="span-2">初始密码<input v-model="createForm.password" class="input" type="password" autocomplete="new-password" /></label>
          </div>
          <button class="btn btn-primary" type="button" :disabled="pending === 'create'" @click="createUser">{{ pending === "create" ? "创建中" : "创建用户" }}</button>
        </section>
      </div>

      <section class="data-panel admin-user-detail-panel">
        <header class="panel-header">
          <h2>{{ selectedUser?.displayName || selectedUser?.username || "用户详情" }}</h2>
          <span class="status-pill" :data-status="selectedUser?.status || (detailLoading ? 'pending' : 'disabled')">{{ detailLoading ? "loading" : selectedUser?.role || "未选择" }}</span>
        </header>
        <p v-if="!selectedUser" class="muted-box">选择一个用户查看详情。</p>
        <template v-else>
          <div class="overview-grid usage-metrics">
            <article class="metric-card"><span>Available</span><strong>{{ formatNumber(selectedSummary?.credits?.available) }}</strong></article>
            <article class="metric-card"><span>Plan</span><strong>{{ selectedSummary?.plan?.displayName || selectedSummary?.planId || "default" }}</strong></article>
            <article class="metric-card"><span>Usage credits</span><strong>{{ formatNumber(selectedSummary?.usage?.totalCredits) }}</strong></article>
            <article class="metric-card"><span>Active keys</span><strong>{{ formatNumber(selectedSummary?.apiKeys?.activeCount) }}</strong></article>
          </div>

          <div class="admin-detail-grid">
            <section class="detail-subpanel">
              <h3>资料与权限</h3>
              <div class="form-grid two-col">
                <label>邮箱<input v-model="editForm.email" class="input" /></label>
                <label>用户名<input v-model="editForm.username" class="input" /></label>
                <label>显示名<input v-model="editForm.displayName" class="input" /></label>
                <label>重置密码<input v-model="editForm.password" class="input" type="password" autocomplete="new-password" placeholder="留空则不变" /></label>
                <label>角色<select v-model="editForm.role" class="input"><option value="user">user</option><option value="admin">admin</option></select></label>
                <label>状态<select v-model="editForm.status" class="input"><option value="active">active</option><option value="pending">pending</option><option value="disabled">disabled</option></select></label>
                <label class="span-2">计费 plan<select v-model="editForm.planId" class="input"><option v-for="item in plans" :key="item.plan?.id" :value="item.plan?.id">{{ item.plan?.displayName || item.plan?.id }} / {{ item.plan?.status }}</option></select></label>
              </div>
              <button class="btn btn-primary" type="button" :disabled="pending === `save:${selectedUser?.id}`" @click="saveUser">{{ pending === `save:${selectedUser?.id}` ? "保存中" : "保存资料" }}</button>
            </section>

            <section class="detail-subpanel">
              <h3>Credit 调整</h3>
              <div class="form-grid">
                <label>Delta credits<input v-model.number="creditForm.deltaCredits" class="input" type="number" /></label>
                <label>原因<input v-model="creditForm.reason" class="input" /></label>
                <label>Idempotency key<input v-model="creditForm.idempotencyKey" class="input" placeholder="可选" /></label>
              </div>
              <button class="btn btn-secondary" type="button" :disabled="pending === `credits:${selectedUser?.id}`" @click="adjustCredits">{{ pending === `credits:${selectedUser?.id}` ? "提交中" : "调整余额" }}</button>
            </section>
          </div>

          <section class="detail-subpanel">
            <h3>资源授权</h3>
            <table v-if="selectedEntitlements.length" class="data-table usage-table">
              <thead><tr><th>Resource</th><th>Status</th><th>Monthly</th><th>Concurrent</th><th>RPM</th></tr></thead>
              <tbody>
                <tr v-for="item in selectedEntitlements" :key="item.resourceType">
                  <td><code>{{ item.resourceType }}</code></td>
                  <td><span class="status-pill" :data-status="item.enabled ? 'active' : 'disabled'">{{ item.enabled ? "enabled" : "disabled" }}</span></td>
                  <td>{{ entitlementLimit(item.monthlyUsageCredits, item.monthlyQuotaCredits, item.monthlyQuotaUnlimited) }}</td>
                  <td>{{ entitlementLimit(item.activeReservations, item.maxConcurrentReservations, item.concurrencyUnlimited) }}</td>
                  <td>{{ entitlementLimit(item.rpmUsed, item.rpmLimit, item.rpmUnlimited) }}</td>
                </tr>
              </tbody>
            </table>
            <p v-else class="muted-box">当前用户 plan 没有资源授权记录。</p>
          </section>

          <div class="usage-panels">
            <section class="detail-subpanel">
              <h3>API keys</h3>
              <table v-if="apiKeys.length" class="data-table usage-table"><tbody><tr v-for="key in apiKeys" :key="key.id"><td>{{ key.name || key.id }}<small>{{ key.id }}</small></td><td><code>{{ key.prefix }}</code></td><td><span class="status-pill" :data-status="key.revokedAt ? 'disabled' : 'active'">{{ key.revokedAt ? "revoked" : "active" }}</span></td></tr></tbody></table>
              <p v-else class="muted-box">暂无 API key。</p>
            </section>
            <section class="detail-subpanel">
              <h3>Ledger</h3>
              <table v-if="ledgerRows.length" class="data-table usage-table"><tbody><tr v-for="row in ledgerRows" :key="row.id"><td>{{ formatDate(row.createdAt) }}<small>{{ row.kind }} / {{ row.reason }} / {{ row.source }}</small></td><td>{{ formatSignedCredits(row.deltaCredits) }}</td><td>{{ formatNumber(row.balanceBefore) }} -> {{ formatNumber(row.balanceAfter) }}<small>{{ row.operatorUsername || row.operatorUserId || "-" }} · {{ row.idempotencyKey || "no-key" }}</small></td></tr></tbody></table>
              <p v-else class="muted-box">暂无 ledger。</p>
            </section>
          </div>

          <section class="detail-subpanel">
            <h3>Reservations</h3>
            <table v-if="reservations.length" class="data-table usage-table"><tbody><tr v-for="item in reservations" :key="item.reservationId"><td><code>{{ item.reservationId }}</code></td><td><code>{{ item.serviceId }}</code><small>{{ item.resourceType || "generic" }}</small></td><td>{{ formatNumber(item.estimatedCredits) }}</td><td>{{ item.status }}</td><td>{{ formatDate(item.expiresAt) }}</td></tr></tbody></table>
            <p v-else class="muted-box">没有 active reservation。</p>
          </section>
        </template>
      </section>
    </template>
  </section>
</template>
