1406 lines
48 KiB
Vue
1406 lines
48 KiB
Vue
<template>
|
||
<div class="sms-panel-page h-full min-h-0 w-full min-w-0">
|
||
<div class="sms-panel-page__scroll h-full min-h-0 overflow-y-auto overflow-x-hidden px-2 pb-8 md:px-4">
|
||
<header class="mb-6 md:mb-8">
|
||
<h1 class="sms-panel-heading text-xl font-bold md:text-2xl">پنل پیامک</h1>
|
||
<p class="sms-panel-subtitle mt-1 text-sm">مدیریت کیف پول، تعرفه و گزارش ارسال پیامک</p>
|
||
</header>
|
||
|
||
<div v-if="pageLoading" class="flex justify-center py-16">
|
||
<a-spin size="large" />
|
||
</div>
|
||
|
||
<template v-else-if="overview">
|
||
<!-- آمار کلی -->
|
||
<section
|
||
class="sms-stat-cards mb-6 grid grid-cols-1 gap-2 sm:grid-cols-2 sm:gap-4 xl:grid-cols-4 md:mb-8 md:gap-5"
|
||
>
|
||
<div
|
||
class="sms-stat-card relative overflow-hidden rounded-xl border p-3 shadow-sm transition-shadow hover:shadow-md md:rounded-2xl md:p-6"
|
||
:class="balanceCardClass"
|
||
>
|
||
<div class="sms-stat-inline flex gap-2.5 md:block">
|
||
<div
|
||
class="sms-stat-icon flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
||
:class="balanceIconClass"
|
||
>
|
||
<Icon icon="vuesax-linear:wallet-3" size="18px" class="md:!text-[22px]" />
|
||
</div>
|
||
<div class="sms-stat-inline-body sms-balance-stat-body flex min-w-0 flex-1 justify-between gap-2 md:block">
|
||
<div class="sms-balance-stat-title flex min-w-0 items-center gap-1 leading-none md:hidden">
|
||
<span class="sms-stat-label shrink-0 text-xs font-medium leading-none md:hidden">موجودی کیف پول</span>
|
||
<a-tooltip
|
||
v-if="overview.wallet.balance <= 0"
|
||
trigger="click"
|
||
placement="top"
|
||
overlay-class-name="sms-balance-info-tooltip"
|
||
>
|
||
<template #title>{{ balanceAlertHint }}</template>
|
||
<button
|
||
type="button"
|
||
class="sms-balance-info-btn inline-flex shrink-0 items-center justify-center text-[var(--admin-text-secondary)] transition-colors hover:text-[var(--admin-text-muted)]"
|
||
aria-label="توضیح هشدار موجودی"
|
||
@click.stop
|
||
>
|
||
<svg class="h-4 w-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||
<circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.1" />
|
||
<circle cx="8" cy="5.2" r="0.55" fill="currentColor" />
|
||
<path d="M8 7.2v3.3" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" />
|
||
</svg>
|
||
</button>
|
||
</a-tooltip>
|
||
<a-tag
|
||
v-if="overview.wallet.is_blocked"
|
||
color="error"
|
||
class="!m-0 shrink-0 !text-[10px] leading-none"
|
||
>
|
||
ارسال مسدود
|
||
</a-tag>
|
||
</div>
|
||
<p
|
||
class="sms-stat-value shrink-0 whitespace-nowrap text-base font-bold leading-none tracking-tight md:hidden"
|
||
:class="balanceTextClass"
|
||
>
|
||
{{ formatNumber(overview.wallet.balance) }}
|
||
<span class="text-xs font-normal opacity-70">ریال</span>
|
||
</p>
|
||
|
||
<div class="hidden w-full md:block">
|
||
<div class="mb-2 flex items-center justify-between gap-2">
|
||
<div class="sms-balance-stat-title-desktop flex items-center gap-1">
|
||
<span class="sms-stat-label text-sm font-medium leading-none">موجودی کیف پول</span>
|
||
<a-tooltip
|
||
v-if="overview.wallet.balance <= 0"
|
||
trigger="click"
|
||
placement="top"
|
||
overlay-class-name="sms-balance-info-tooltip"
|
||
>
|
||
<template #title>{{ balanceAlertHint }}</template>
|
||
<button
|
||
type="button"
|
||
class="sms-balance-info-btn inline-flex shrink-0 items-center justify-center text-[var(--admin-text-secondary)] transition-colors hover:text-[var(--admin-text-muted)]"
|
||
aria-label="توضیح هشدار موجودی"
|
||
@click.stop
|
||
>
|
||
<svg class="h-4 w-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||
<circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.1" />
|
||
<circle cx="8" cy="5.2" r="0.55" fill="currentColor" />
|
||
<path d="M8 7.2v3.3" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" />
|
||
</svg>
|
||
</button>
|
||
</a-tooltip>
|
||
</div>
|
||
<a-tag
|
||
v-if="overview.wallet.is_blocked"
|
||
color="error"
|
||
class="!m-0 !text-xs"
|
||
>
|
||
ارسال مسدود
|
||
</a-tag>
|
||
</div>
|
||
<p
|
||
class="sms-stat-value whitespace-nowrap text-3xl font-bold leading-normal tracking-tight"
|
||
:class="balanceTextClass"
|
||
>
|
||
{{ formatNumber(overview.wallet.balance) }}
|
||
<span class="text-sm font-normal opacity-70">ریال</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sms-stat-card sms-stat-card--sky rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
||
>
|
||
<div class="sms-stat-inline flex gap-2.5 md:block">
|
||
<div
|
||
class="sms-stat-icon sms-stat-icon--sky flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
||
>
|
||
<Icon icon="vuesax-linear:tag" Size="18px" class="md:!text-[22px]" />
|
||
</div>
|
||
<div class="sms-stat-inline-body flex min-w-0 flex-1 justify-between gap-2 md:block">
|
||
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
||
قیمت هر پارت
|
||
</p>
|
||
<p class="sms-stat-value sms-stat-value--sky shrink-0 whitespace-nowrap text-base font-bold leading-none md:text-3xl md:leading-normal">
|
||
{{ formatNumber(overview.wallet.price_per_part) }}
|
||
<span class="sms-stat-unit text-xs font-normal md:text-sm">ریال</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sms-stat-card sms-stat-card--emerald rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
||
>
|
||
<div class="sms-stat-inline flex gap-2.5 md:block">
|
||
<div
|
||
class="sms-stat-icon sms-stat-icon--emerald flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
||
>
|
||
<Icon icon="vuesax-linear:sms" Size="18px" class="md:!text-[22px]" />
|
||
</div>
|
||
<div class="sms-stat-inline-body sms-stat-period-body flex min-w-0 flex-1 items-center md:block">
|
||
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
||
امروز
|
||
</p>
|
||
<p class="sms-stat-period-mid min-w-0 flex-1 text-center text-sm leading-none md:hidden">
|
||
<span class="sms-stat-value sms-stat-value--emerald font-bold">
|
||
{{ formatNumber(overview.stats.today.messages) }}
|
||
<span class="sms-stat-unit text-xs font-medium">پیامک</span>
|
||
</span>
|
||
<span class="sms-stat-meta">
|
||
· {{ formatNumber(overview.stats.today.parts) }} پارت
|
||
</span>
|
||
</p>
|
||
<p class="sms-stat-meta shrink-0 whitespace-nowrap text-sm leading-none md:hidden">
|
||
{{ formatNumber(overview.stats.today.amount) }} ریال
|
||
</p>
|
||
<p class="sms-stat-value sms-stat-value--emerald hidden text-xl font-bold md:block">
|
||
{{ formatNumber(overview.stats.today.messages) }}
|
||
<span class="sms-stat-unit text-sm font-medium">پیامک</span>
|
||
</p>
|
||
<p class="sms-stat-meta hidden text-sm md:mt-2 md:block">
|
||
{{ formatNumber(overview.stats.today.parts) }} پارت ·
|
||
{{ formatNumber(overview.stats.today.amount) }} ریال
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sms-stat-card sms-stat-card--violet rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
||
>
|
||
<div class="sms-stat-inline flex gap-2.5 md:block">
|
||
<div
|
||
class="sms-stat-icon sms-stat-icon--violet flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
||
>
|
||
<Icon icon="vuesax-linear:chart" Size="18px" class="md:!text-[22px]" />
|
||
</div>
|
||
<div class="sms-stat-inline-body sms-stat-period-body flex min-w-0 flex-1 items-center md:block">
|
||
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
||
ماه جاری
|
||
</p>
|
||
<p class="sms-stat-period-mid min-w-0 flex-1 text-center text-sm leading-none md:hidden">
|
||
<span class="sms-stat-value sms-stat-value--violet font-bold">
|
||
{{ formatNumber(overview.stats.month.messages) }}
|
||
<span class="sms-stat-unit text-xs font-medium">پیامک</span>
|
||
</span>
|
||
<span class="sms-stat-meta">
|
||
· {{ formatNumber(overview.stats.month.parts) }} پارت
|
||
</span>
|
||
</p>
|
||
<p class="sms-stat-meta shrink-0 whitespace-nowrap text-sm leading-none md:hidden">
|
||
{{ formatNumber(overview.stats.month.amount) }} ریال
|
||
</p>
|
||
<p class="sms-stat-value sms-stat-value--violet hidden text-xl font-bold md:block">
|
||
{{ formatNumber(overview.stats.month.messages) }}
|
||
<span class="sms-stat-unit text-sm font-medium">پیامک</span>
|
||
</p>
|
||
<p class="sms-stat-meta hidden text-sm md:mt-2 md:block">
|
||
{{ formatNumber(overview.stats.month.parts) }} پارت ·
|
||
{{ formatNumber(overview.stats.month.amount) }} ریال
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- مدیریت و گزارش -->
|
||
<section class="sms-panel-mgmt mb-6 flex flex-col gap-4 md:mb-8 md:gap-6">
|
||
<div class="sms-panel-surface rounded-2xl p-4 shadow-sm md:p-6">
|
||
<div class="sms-panel-section-head mb-4 flex items-center gap-2 pb-3 md:mb-5 md:pb-4">
|
||
<Icon icon="vuesax-linear:setting-2" Size="20px" class="sms-panel-section-icon" />
|
||
<h2 class="sms-panel-heading text-base font-semibold">مدیریت کیف پول</h2>
|
||
</div>
|
||
|
||
<div class="flex flex-col gap-4 md:gap-6">
|
||
<div class="flex flex-col gap-3 md:gap-4 xl:flex-row xl:items-stretch">
|
||
<div
|
||
class="sms-panel-subcard sms-panel-subcard--sky flex-1 rounded-xl p-3 shadow-sm md:p-5"
|
||
>
|
||
<div class="mb-3 flex items-center gap-2 md:mb-4">
|
||
<span class="sms-stat-icon sms-stat-icon--sky flex h-8 w-8 items-center justify-center rounded-lg">
|
||
<Icon icon="vuesax-linear:tag" Size="18px" />
|
||
</span>
|
||
<div>
|
||
<p class="sms-subcard-title text-sm font-semibold">تعرفه پیامک</p>
|
||
<p class="sms-panel-subtitle hidden text-xs md:block">
|
||
قیمت هر پارت برای ارسالهای بعدی
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="grid grid-cols-[1fr_auto] items-end gap-2 md:gap-3">
|
||
<a-form-item label="قیمت هر پارت (ریال)" class="sms-compact-field !mb-0">
|
||
<a-input
|
||
v-model:value="pricePerPartField"
|
||
inputmode="numeric"
|
||
placeholder="قیمت پارت"
|
||
size="large"
|
||
class="!w-full"
|
||
/>
|
||
</a-form-item>
|
||
<BaseButton
|
||
type="primary"
|
||
size="large"
|
||
class="!w-auto shrink-0 whitespace-nowrap"
|
||
:loading="actionLoading"
|
||
@click="submitPrice"
|
||
>
|
||
ذخیره قیمت
|
||
</BaseButton>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sms-panel-subcard sms-panel-subcard--emerald flex-1 rounded-xl p-3 shadow-sm md:p-5"
|
||
>
|
||
<div class="mb-3 flex items-center gap-2 md:mb-4">
|
||
<span
|
||
class="sms-stat-icon sms-stat-icon--emerald flex h-8 w-8 items-center justify-center rounded-lg"
|
||
>
|
||
<Icon icon="vuesax-linear:wallet-add" Size="18px" />
|
||
</span>
|
||
<div>
|
||
<p class="sms-subcard-title text-sm font-semibold">شارژ کیف پول</p>
|
||
<p class="sms-panel-subtitle hidden text-xs md:block">
|
||
افزایش موجودی برای ارسال پیامک
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="grid grid-cols-[1fr_auto] items-end gap-2 md:grid-cols-2 md:gap-3 xl:grid-cols-[1fr_1.2fr_auto]"
|
||
>
|
||
<a-form-item label="مبلغ شارژ (ریال)" class="sms-compact-field order-1 !mb-0">
|
||
<a-input
|
||
v-model:value="chargeAmountField"
|
||
inputmode="numeric"
|
||
placeholder="مبلغ شارژ"
|
||
size="large"
|
||
class="!w-full"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item
|
||
label="توضیح (اختیاری)"
|
||
class="sms-compact-field order-3 col-span-2 !mb-0 md:order-2 md:col-span-1"
|
||
>
|
||
<BasicInput v-model:value="chargeNote" place-holder="توضیح شارژ" size="large" />
|
||
</a-form-item>
|
||
<BaseButton
|
||
type="primary"
|
||
size="large"
|
||
class="order-2 !w-auto shrink-0 whitespace-nowrap md:order-3 md:col-span-2 xl:col-span-1"
|
||
:loading="actionLoading"
|
||
@click="openChargeConfirm"
|
||
>
|
||
شارژ کیف پول
|
||
</BaseButton>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="sms-panel-block rounded-xl border p-4 md:p-5"
|
||
:class="isBlocked ? 'sms-panel-block--blocked' : 'sms-panel-block--active'"
|
||
>
|
||
<div class="inline-flex max-w-full flex-col gap-2 self-start">
|
||
<div class="sms-block-row flex w-full items-center justify-between gap-4">
|
||
<span class="sms-panel-heading font-semibold leading-none">
|
||
مسدودسازی ارسال پیامک
|
||
</span>
|
||
<a-switch
|
||
v-model:checked="isBlocked"
|
||
class="sms-block-switch !m-0 shrink-0"
|
||
:class="isBlocked ? 'sms-block-switch--blocked' : 'sms-block-switch--active'"
|
||
:loading="blockLoading"
|
||
checked-children="مسدود"
|
||
un-checked-children="فعال"
|
||
@change="onBlockChange"
|
||
/>
|
||
</div>
|
||
<p class="sms-panel-subtitle text-sm leading-relaxed">
|
||
در حالت مسدود، هیچ پیامکی از این نسخه ارسال نمیشود.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sms-panel-surface sms-report-card rounded-2xl p-4 shadow-sm md:p-6">
|
||
<div class="sms-panel-section-head mb-3 flex items-center gap-2 pb-3 md:mb-4 md:pb-4">
|
||
<Icon icon="vuesax-linear:calendar" Size="20px" class="sms-panel-section-icon" />
|
||
<h2 class="sms-panel-heading text-base font-semibold">گزارش</h2>
|
||
</div>
|
||
|
||
<a-tabs v-model:activeKey="activeReportTab" class="sms-panel-tabs sms-report-tabs">
|
||
<a-tab-pane key="sms" tab="گزارش بازهای پیامک">
|
||
<div class="flex flex-col gap-3 md:gap-4">
|
||
<div
|
||
class="sms-report-filter-row grid grid-cols-[1fr_1fr_auto] items-center gap-2 md:flex md:flex-wrap md:items-end md:gap-4"
|
||
>
|
||
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="reportFrom"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="از تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="reportTo"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="تا تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<BaseButton
|
||
class="sms-report-action-btn !w-auto shrink-0 whitespace-nowrap px-3 md:!w-auto"
|
||
:loading="reportLoading"
|
||
@click="loadReport"
|
||
>
|
||
دریافت گزارش
|
||
</BaseButton>
|
||
</div>
|
||
|
||
<div
|
||
v-if="report"
|
||
class="sms-panel-muted-box space-y-2 rounded-xl p-3 md:space-y-3 md:p-4"
|
||
>
|
||
<div class="grid grid-cols-3 gap-2 md:gap-3">
|
||
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
||
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد پیامک</span>
|
||
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
||
{{ formatNumber(report.summary.messages) }}
|
||
</p>
|
||
</div>
|
||
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
||
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد پارت</span>
|
||
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
||
{{ formatNumber(report.summary.parts) }}
|
||
</p>
|
||
</div>
|
||
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
||
<span class="sms-panel-subtitle text-[10px] md:text-xs">مبلغ کل</span>
|
||
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
||
{{ formatNumber(report.summary.amount) }}
|
||
<span class="sms-panel-subtitle text-[10px] font-normal md:text-sm">ریال</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div v-if="report.by_provider?.length" class="sms-panel-divider border-t pt-2 md:pt-3">
|
||
<p class="sms-stat-label mb-1 text-[10px] font-semibold md:mb-2 md:text-xs">
|
||
بر اساس ارائهدهنده
|
||
</p>
|
||
<ul class="space-y-1 md:space-y-2">
|
||
<li
|
||
v-for="row in report.by_provider"
|
||
:key="row.provider"
|
||
class="sms-panel-kpi flex items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-[11px] md:px-3 md:py-2 md:text-xs"
|
||
>
|
||
<span class="font-medium">{{ row.provider }}</span>
|
||
<span class="sms-panel-subtitle shrink-0">
|
||
{{ formatNumber(row.messages) }} پیام · {{ formatNumber(row.amount) }} ریال
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="charge" tab="گزارش شارژ کیف پول">
|
||
<div class="flex flex-col gap-3 md:gap-4">
|
||
<div
|
||
class="sms-report-filter-row grid grid-cols-[1fr_1fr_auto] items-center gap-2 md:flex md:flex-wrap md:items-end md:gap-4"
|
||
>
|
||
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="chargeReportFrom"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="از تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="chargeReportTo"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="تا تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<BaseButton
|
||
class="sms-report-action-btn !w-auto shrink-0 whitespace-nowrap px-3 md:!w-auto"
|
||
:loading="chargeReportLoading"
|
||
@click="loadChargeReport"
|
||
>
|
||
دریافت گزارش
|
||
</BaseButton>
|
||
</div>
|
||
|
||
<p class="sms-panel-subtitle text-xs md:text-sm">
|
||
در صورت خالی بودن تاریخ، همه شارژها نمایش داده میشوند.
|
||
</p>
|
||
|
||
<div
|
||
v-if="chargeReport"
|
||
class="sms-panel-muted-box space-y-2 rounded-xl p-3 md:space-y-3 md:p-4"
|
||
>
|
||
<div class="grid grid-cols-2 gap-2 md:gap-3">
|
||
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
||
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد شارژ</span>
|
||
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
||
{{ formatNumber(chargeReport.summary.charges) }}
|
||
</p>
|
||
</div>
|
||
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
||
<span class="sms-panel-subtitle text-[10px] md:text-xs">مبلغ کل شارژ</span>
|
||
<p class="sms-stat-value sms-stat-value--emerald text-sm font-bold md:text-lg">
|
||
{{ formatNumber(chargeReport.summary.amount) }}
|
||
<span class="sms-panel-subtitle text-[10px] font-normal md:text-sm">ریال</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="chargeReport.items.length" class="sms-panel-divider border-t pt-2 md:pt-3">
|
||
<p class="sms-stat-label mb-1 text-[10px] font-semibold md:mb-2 md:text-xs">
|
||
لیست شارژها
|
||
</p>
|
||
<ul class="max-h-64 space-y-1 overflow-y-auto md:max-h-80 md:space-y-2">
|
||
<li
|
||
v-for="item in chargeReport.items"
|
||
:key="item.id"
|
||
class="sms-panel-kpi rounded-lg px-2 py-2 text-[11px] md:px-3 md:py-2.5 md:text-xs"
|
||
>
|
||
<div class="flex items-start justify-between gap-2">
|
||
<div class="min-w-0">
|
||
<p class="sms-stat-value sms-stat-value--emerald font-semibold">
|
||
+{{ formatNumber(item.amount) }} ریال
|
||
</p>
|
||
<p v-if="item.note" class="sms-panel-subtitle mt-0.5">{{ item.note }}</p>
|
||
</div>
|
||
<div class="sms-panel-subtitle shrink-0 text-left">
|
||
<p>{{ formatReportDate(item.created_at) }}</p>
|
||
<p class="mt-0.5">
|
||
موجودی:
|
||
{{ formatNumber(item.balance_after) }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<p v-else class="sms-panel-subtitle text-center text-xs md:text-sm">
|
||
شارژی در این بازه یافت نشد.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- جداول -->
|
||
<section class="sms-panel-surface rounded-2xl p-4 shadow-sm md:p-6">
|
||
<a-tabs
|
||
v-model:activeKey="activeTab"
|
||
class="sms-panel-tabs sms-panel-table-tabs"
|
||
:animated="false"
|
||
destroy-inactive-tab-pane
|
||
>
|
||
<a-tab-pane key="logs" tab="لاگ ارسال">
|
||
<div
|
||
class="sms-panel-muted-box sms-table-date-filter mb-4 grid grid-cols-2 items-end gap-2 rounded-xl p-3 md:mb-5 md:flex md:flex-wrap md:gap-4 md:p-4"
|
||
>
|
||
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="tableFrom"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="انتخاب تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="tableTo"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="انتخاب تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
</div>
|
||
<SmsPanelLogsTable ref="logsTableRef" :from-date="tableFrom" :to-date="tableTo" />
|
||
</a-tab-pane>
|
||
<a-tab-pane key="ledger" tab="دفترچه کیف پول">
|
||
<div
|
||
class="sms-panel-muted-box sms-table-date-filter mb-4 grid grid-cols-2 items-end gap-2 rounded-xl p-3 md:mb-5 md:flex md:flex-wrap md:gap-4 md:p-4"
|
||
>
|
||
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="tableFrom"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="انتخاب تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
||
<BaseDatePicker
|
||
v-model:value="tableTo"
|
||
format="YYYY-MM-DD"
|
||
class="w-full"
|
||
placeholder="انتخاب تاریخ"
|
||
/>
|
||
</a-form-item>
|
||
</div>
|
||
<SmsPanelLedgerTable ref="ledgerTableRef" :from-date="tableFrom" :to-date="tableTo" />
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</section>
|
||
</template>
|
||
</div>
|
||
|
||
<BasicModal
|
||
:visible="chargeModalVisible"
|
||
title="تأیید شارژ کیف پول"
|
||
width="380px"
|
||
ok-text="شارژ"
|
||
:loading="actionLoading"
|
||
@ok="submitCharge"
|
||
@close="chargeModalVisible = false"
|
||
>
|
||
<p class="sms-panel-subtitle p-3 text-sm">
|
||
مبلغ
|
||
<strong class="sms-panel-heading">{{ formatNumber(Number(chargeAmount) || 0) }}</strong>
|
||
ریال به کیف پول پیامک اضافه شود؟
|
||
</p>
|
||
</BasicModal>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import BasicInput from '@/components/BasicInput.vue'
|
||
import BasicModal from '@/components/BasicModal.vue'
|
||
import BaseDatePicker from '@core/components/BaseDatePicker'
|
||
import BaseButton from '@core/components/BaseButton'
|
||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||
import SmsPanelLogsTable from './components/SmsPanelLogsTable.vue'
|
||
import SmsPanelLedgerTable from './components/SmsPanelLedgerTable.vue'
|
||
import {
|
||
chargeSmsWallet,
|
||
getSmsPanelChargeReport,
|
||
getSmsPanelOverview,
|
||
getSmsPanelReport,
|
||
updateSmsBlock,
|
||
updateSmsPrice,
|
||
type SmsPanelChargeReport,
|
||
type SmsPanelOverview,
|
||
type SmsPanelReport
|
||
} from '@/api/smsPanel'
|
||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||
import { message } from 'ant-design-vue'
|
||
|
||
const pageLoading = ref(true)
|
||
const actionLoading = ref(false)
|
||
const blockLoading = ref(false)
|
||
const isBlocked = ref(false)
|
||
const reportLoading = ref(false)
|
||
const chargeReportLoading = ref(false)
|
||
const overview = ref<SmsPanelOverview | null>(null)
|
||
const report = ref<SmsPanelReport | null>(null)
|
||
const chargeReport = ref<SmsPanelChargeReport | null>(null)
|
||
|
||
const chargeAmount = ref<number | null>(null)
|
||
const chargeNote = ref('')
|
||
const pricePerPart = ref<number | null>(null)
|
||
const chargeModalVisible = ref(false)
|
||
|
||
const reportFrom = ref('')
|
||
const reportTo = ref('')
|
||
const chargeReportFrom = ref('')
|
||
const chargeReportTo = ref('')
|
||
const tableFrom = ref('')
|
||
const tableTo = ref('')
|
||
|
||
const activeReportTab = ref('sms')
|
||
const activeTab = ref('logs')
|
||
const logsTableRef = ref<InstanceType<typeof SmsPanelLogsTable> | null>(null)
|
||
const ledgerTableRef = ref<InstanceType<typeof SmsPanelLedgerTable> | null>(null)
|
||
|
||
const balanceCardClass = computed(() => {
|
||
if (!overview.value) return 'sms-stat-card--neutral'
|
||
if (overview.value.wallet.balance < 0) return 'sms-stat-card--danger'
|
||
if (overview.value.wallet.balance <= 0) return 'sms-stat-card--warn'
|
||
return 'sms-stat-card--ok'
|
||
})
|
||
|
||
const balanceIconClass = computed(() => {
|
||
if (!overview.value) return 'sms-stat-icon--neutral'
|
||
if (overview.value.wallet.balance < 0) return 'sms-stat-icon--danger'
|
||
if (overview.value.wallet.balance <= 0) return 'sms-stat-icon--warn'
|
||
return 'sms-stat-icon--ok'
|
||
})
|
||
|
||
const balanceTextClass = computed(() => {
|
||
if (!overview.value) return 'sms-stat-value--neutral'
|
||
if (overview.value.wallet.balance < 0) return 'sms-stat-value--danger'
|
||
if (overview.value.wallet.balance <= 0) return 'sms-stat-value--warn'
|
||
return 'sms-stat-value--ok'
|
||
})
|
||
|
||
const balanceAlertHint = computed(() => {
|
||
const sent = overview.value?.wallet.alert_days_sent ?? 0
|
||
return `هشدار روزانه حداکثر ۳ روز (${sent}/۳)`
|
||
})
|
||
|
||
function formatNumber(value: number) {
|
||
return Number(value || 0).toLocaleString('fa-IR')
|
||
}
|
||
|
||
function formatReportDate(value: string | null | undefined) {
|
||
if (!value) return '—'
|
||
return toJalaliNoTime(value)
|
||
}
|
||
|
||
function formatAmountDisplay(value: number | null) {
|
||
if (value == null || !Number.isFinite(value)) return ''
|
||
return String(Math.trunc(value)).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||
}
|
||
|
||
function parseAmountDisplay(value: string) {
|
||
const digits = value.replace(/[^\d]/g, '')
|
||
return digits === '' ? null : Number(digits)
|
||
}
|
||
|
||
function useAmountField(source: Ref<number | null>) {
|
||
return computed({
|
||
get: () => formatAmountDisplay(source.value),
|
||
set: (val: string) => {
|
||
source.value = parseAmountDisplay(val)
|
||
}
|
||
})
|
||
}
|
||
|
||
const chargeAmountField = useAmountField(chargeAmount)
|
||
const pricePerPartField = useAmountField(pricePerPart)
|
||
|
||
async function loadOverview() {
|
||
pageLoading.value = true
|
||
try {
|
||
overview.value = await getSmsPanelOverview()
|
||
pricePerPart.value = overview.value.wallet.price_per_part || null
|
||
isBlocked.value = overview.value.wallet.is_blocked
|
||
} catch {
|
||
message.error('خطا در بارگذاری پنل پیامک')
|
||
} finally {
|
||
pageLoading.value = false
|
||
}
|
||
}
|
||
|
||
function openChargeConfirm() {
|
||
const amount = Number(chargeAmount.value ?? 0)
|
||
if (!amount || amount <= 0) {
|
||
message.warning('مبلغ شارژ را وارد کنید')
|
||
return
|
||
}
|
||
chargeModalVisible.value = true
|
||
}
|
||
|
||
async function submitCharge() {
|
||
const amount = Number(chargeAmount.value ?? 0)
|
||
if (!amount || amount <= 0) return
|
||
|
||
actionLoading.value = true
|
||
try {
|
||
const res = await chargeSmsWallet({
|
||
amount,
|
||
note: chargeNote.value.trim() || undefined
|
||
})
|
||
message.success(res.message || 'شارژ انجام شد')
|
||
chargeModalVisible.value = false
|
||
chargeAmount.value = null
|
||
chargeNote.value = ''
|
||
await loadOverview()
|
||
ledgerTableRef.value?.fetch()
|
||
if (chargeReport.value) {
|
||
await loadChargeReport()
|
||
}
|
||
} catch (e: any) {
|
||
message.error(e?.response?.data?.message || 'خطا در شارژ')
|
||
} finally {
|
||
actionLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function submitPrice() {
|
||
const price = Number(pricePerPart.value ?? 0)
|
||
if (Number.isNaN(price) || price < 0) {
|
||
message.warning('قیمت معتبر وارد کنید')
|
||
return
|
||
}
|
||
|
||
actionLoading.value = true
|
||
try {
|
||
const res = await updateSmsPrice(price)
|
||
message.success(res.message || 'قیمت ذخیره شد')
|
||
await loadOverview()
|
||
} catch (e: any) {
|
||
message.error(e?.response?.data?.message || 'خطا در ذخیره قیمت')
|
||
} finally {
|
||
actionLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function onBlockChange(checked: boolean) {
|
||
blockLoading.value = true
|
||
try {
|
||
const res = await updateSmsBlock(checked)
|
||
message.success(res.message)
|
||
isBlocked.value = res.is_blocked
|
||
await loadOverview()
|
||
} catch (e: any) {
|
||
isBlocked.value = overview.value?.wallet.is_blocked ?? false
|
||
message.error(e?.response?.data?.message || 'خطا در تغییر وضعیت')
|
||
} finally {
|
||
blockLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function loadReport() {
|
||
reportLoading.value = true
|
||
try {
|
||
report.value = await getSmsPanelReport({
|
||
from_date: reportFrom.value || undefined,
|
||
to_date: reportTo.value || undefined
|
||
})
|
||
} catch {
|
||
message.error('خطا در دریافت گزارش')
|
||
} finally {
|
||
reportLoading.value = false
|
||
}
|
||
}
|
||
|
||
async function loadChargeReport() {
|
||
chargeReportLoading.value = true
|
||
try {
|
||
chargeReport.value = await getSmsPanelChargeReport({
|
||
from_date: chargeReportFrom.value || undefined,
|
||
to_date: chargeReportTo.value || undefined
|
||
})
|
||
} catch {
|
||
message.error('خطا در دریافت گزارش شارژ')
|
||
} finally {
|
||
chargeReportLoading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadOverview()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.sms-panel-page__scroll {
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.sms-panel-heading {
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-panel-subtitle {
|
||
color: var(--admin-text-secondary);
|
||
}
|
||
|
||
.sms-panel-section-icon {
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-panel-section-head {
|
||
border-bottom: 1px solid var(--admin-border);
|
||
}
|
||
|
||
.sms-panel-surface {
|
||
border: 1px solid var(--admin-border);
|
||
background: var(--admin-bg-elevated);
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-panel-muted-box {
|
||
border: 1px solid var(--admin-border);
|
||
background: var(--admin-bg-muted);
|
||
}
|
||
|
||
.sms-panel-kpi {
|
||
background: var(--admin-bg-elevated);
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-panel-divider {
|
||
border-color: var(--admin-border);
|
||
}
|
||
|
||
.sms-stat-label {
|
||
color: var(--admin-text-secondary);
|
||
}
|
||
|
||
.sms-stat-meta {
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-stat-unit {
|
||
color: var(--admin-text-secondary);
|
||
}
|
||
|
||
.sms-stat-card--neutral {
|
||
border-color: var(--admin-border);
|
||
background: var(--admin-bg-elevated);
|
||
}
|
||
|
||
.sms-stat-card--ok {
|
||
border-color: rgba(16, 185, 129, 0.28);
|
||
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.1), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-card--warn {
|
||
border-color: rgba(245, 158, 11, 0.32);
|
||
background: linear-gradient(to bottom right, rgba(245, 158, 11, 0.12), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-card--danger {
|
||
border-color: rgba(239, 68, 68, 0.32);
|
||
background: linear-gradient(to bottom right, rgba(239, 68, 68, 0.12), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-card--sky {
|
||
border-color: rgba(14, 165, 233, 0.28);
|
||
background: linear-gradient(to bottom right, rgba(14, 165, 233, 0.1), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-card--emerald {
|
||
border-color: rgba(16, 185, 129, 0.28);
|
||
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.1), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-card--violet {
|
||
border-color: rgba(139, 92, 246, 0.28);
|
||
background: linear-gradient(to bottom right, rgba(139, 92, 246, 0.1), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-stat-icon {
|
||
background: var(--admin-bg-muted);
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-stat-icon--neutral {
|
||
background: var(--admin-bg-muted);
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-stat-icon--ok,
|
||
.sms-stat-icon--emerald {
|
||
background: rgba(16, 185, 129, 0.14);
|
||
color: #059669;
|
||
}
|
||
|
||
.sms-stat-icon--warn {
|
||
background: rgba(245, 158, 11, 0.16);
|
||
color: #d97706;
|
||
}
|
||
|
||
.sms-stat-icon--danger {
|
||
background: rgba(239, 68, 68, 0.14);
|
||
color: #dc2626;
|
||
}
|
||
|
||
.sms-stat-icon--sky {
|
||
background: rgba(14, 165, 233, 0.14);
|
||
color: #0284c7;
|
||
}
|
||
|
||
.sms-stat-icon--violet {
|
||
background: rgba(139, 92, 246, 0.14);
|
||
color: #7c3aed;
|
||
}
|
||
|
||
.sms-stat-value--neutral {
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-stat-value--ok {
|
||
color: #047857;
|
||
}
|
||
|
||
.sms-stat-value--warn {
|
||
color: #b45309;
|
||
}
|
||
|
||
.sms-stat-value--danger {
|
||
color: #b91c1c;
|
||
}
|
||
|
||
.sms-stat-value--sky {
|
||
color: #0c4a6e;
|
||
}
|
||
|
||
.sms-stat-value--emerald {
|
||
color: #047857;
|
||
}
|
||
|
||
.sms-stat-value--violet {
|
||
color: #5b21b6;
|
||
}
|
||
|
||
.sms-panel-subcard {
|
||
border: 1px solid var(--admin-border);
|
||
background: var(--admin-bg-elevated);
|
||
}
|
||
|
||
.sms-panel-subcard--sky {
|
||
border-color: rgba(14, 165, 233, 0.24);
|
||
background: linear-gradient(to bottom right, rgba(14, 165, 233, 0.08), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-panel-subcard--emerald {
|
||
border-color: rgba(16, 185, 129, 0.24);
|
||
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.08), var(--admin-bg-elevated));
|
||
}
|
||
|
||
.sms-subcard-title {
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-panel-block--active {
|
||
border-color: rgba(16, 185, 129, 0.28);
|
||
background: rgba(16, 185, 129, 0.08);
|
||
}
|
||
|
||
.sms-panel-block--blocked {
|
||
border-color: rgba(239, 68, 68, 0.28);
|
||
background: rgba(239, 68, 68, 0.1);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-btn-primary:not(:disabled)) {
|
||
background-color: var(--admin-gold-light);
|
||
border-color: var(--admin-gold-light);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-btn-primary:not(:disabled):hover),
|
||
.sms-panel-page :deep(.ant-btn-primary:not(:disabled):focus) {
|
||
background-color: var(--admin-gold-hover);
|
||
border-color: var(--admin-gold-hover);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-tabs-ink-bar) {
|
||
background: var(--admin-gold-light);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
|
||
color: var(--admin-gold-light);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-tabs-tab .ant-tabs-tab-btn) {
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-input-lg),
|
||
.sms-panel-page :deep(.ant-picker) {
|
||
border-radius: 0.5rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.ant-form-item-label > label) {
|
||
font-weight: 500;
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-balance-info-btn {
|
||
width: 1.125rem;
|
||
height: 1.125rem;
|
||
margin: 0;
|
||
padding: 0;
|
||
border: none;
|
||
background: transparent;
|
||
line-height: 0;
|
||
vertical-align: middle;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.sms-balance-stat-title-desktop {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
line-height: 1;
|
||
}
|
||
|
||
.sms-balance-stat-title-desktop :deep(.ant-tooltip) {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 0;
|
||
}
|
||
|
||
.sms-panel-tabs :deep(.ant-tabs-nav) {
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
.sms-panel-tabs :deep(.ant-tabs-tab) {
|
||
padding: 0.6rem 1rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.sms-panel-table-tabs :deep(.ant-tabs-content-holder),
|
||
.sms-panel-table-tabs :deep(.ant-tabs-content),
|
||
.sms-panel-table-tabs :deep(.ant-tabs-tabpane),
|
||
.sms-panel-table-tabs :deep(.sms-panel-table),
|
||
.sms-panel-table-tabs :deep(.hr-tamin-base-table),
|
||
.sms-panel-table-tabs :deep(.scrollable-container) {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
min-width: 0;
|
||
}
|
||
|
||
.sms-report-tabs :deep(.ant-tabs-nav) {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.sms-report-tabs :deep(.ant-tabs-nav) {
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-report-card .sms-compact-field .ant-picker) {
|
||
height: 2rem;
|
||
padding-top: 0;
|
||
padding-bottom: 0;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-report-card .sms-report-action-btn.ant-btn) {
|
||
height: 2rem;
|
||
padding-top: 0;
|
||
padding-bottom: 0;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-report-card .sms-report-filter-row) {
|
||
align-items: center;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.sms-panel-page :deep(.sms-report-card .sms-report-filter-row) {
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-report-card .sms-compact-field.ant-form-item) {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-switch--active:not(.ant-switch-checked)) {
|
||
background: #22c55e;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-switch--active:not(.ant-switch-checked):hover:not(.ant-switch-disabled)) {
|
||
background: #16a34a;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-switch--blocked.ant-switch-checked) {
|
||
background: #ef4444;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-switch--blocked.ant-switch-checked:hover:not(.ant-switch-disabled)) {
|
||
background: #dc2626;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-row .ant-switch) {
|
||
margin-top: 0;
|
||
margin-bottom: 0;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-block-row .ant-switch-inner) {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
@media (max-width: 767px) {
|
||
.sms-stat-inline {
|
||
align-items: center;
|
||
}
|
||
|
||
.sms-stat-inline-body {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 2.25rem;
|
||
min-height: 2.25rem;
|
||
}
|
||
|
||
.sms-stat-inline-body > p {
|
||
margin: 0;
|
||
}
|
||
|
||
.sms-balance-stat-body > p {
|
||
margin: 0;
|
||
}
|
||
|
||
.sms-balance-stat-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
line-height: 1;
|
||
min-height: 2.25rem;
|
||
margin: 0;
|
||
}
|
||
|
||
.sms-balance-stat-title :deep(.ant-tooltip) {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
line-height: 0;
|
||
}
|
||
|
||
.sms-balance-stat-body :deep(.ant-tooltip) {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
line-height: 0;
|
||
}
|
||
|
||
.sms-stat-period-body {
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.sms-stat-period-mid {
|
||
padding-inline: 0.25rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-compact-field .ant-form-item-label) {
|
||
display: none;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-compact-field .ant-input-lg),
|
||
.sms-panel-page :deep(.sms-compact-field .hr-tamin-input .ant-input-lg) {
|
||
padding-top: 0.375rem;
|
||
padding-bottom: 0.375rem;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2) {
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full) {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 0.25rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.flex-col) {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.justify-between.items-center.gap-2) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.justify-between.items-center.gap-2 > .flex.md\:hidden) {
|
||
display: none;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.flex-col > .block.md\:hidden) {
|
||
display: none;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .hidden.md\:block) {
|
||
display: none !important;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .ant-affix) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table .ant-affix > div) {
|
||
width: 100% !important;
|
||
}
|
||
|
||
.sms-panel-page :deep(.sms-panel-table-tabs .ant-tabs-content) {
|
||
margin-right: 0 !important;
|
||
margin-left: 0 !important;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card:not(:has(.base-table-card-header))) {
|
||
padding: 0.75rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .space-y-3) {
|
||
gap: 0.625rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card) {
|
||
padding: 0;
|
||
border-radius: 0.875rem;
|
||
border-color: var(--admin-border);
|
||
box-shadow: 0 1px 3px var(--admin-shadow);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card:hover) {
|
||
box-shadow: 0 2px 8px var(--admin-shadow);
|
||
transform: none;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-header) {
|
||
margin: 0;
|
||
padding: 0.625rem 0.875rem;
|
||
border: none;
|
||
border-bottom: 1px solid var(--admin-border);
|
||
border-radius: 0;
|
||
background: var(--admin-bg-muted);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-row-badge) {
|
||
height: 1.625rem;
|
||
width: 1.625rem;
|
||
background: var(--admin-gold-light);
|
||
color: var(--admin-menu-selected-text);
|
||
font-size: 0.6875rem;
|
||
font-weight: 700;
|
||
box-shadow: 0 1px 2px rgb(212 175 55 / 0.35);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-header-value) {
|
||
font-weight: 600;
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2) {
|
||
margin: 0;
|
||
padding: 0.75rem 0.875rem;
|
||
gap: 0.5rem 0.875rem;
|
||
background: var(--admin-bg-elevated);
|
||
border-bottom: 1px solid var(--admin-border);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 > div) {
|
||
min-height: 1.125rem;
|
||
padding: 0.25rem 0;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 .base-table-card-grid-label) {
|
||
font-weight: 500;
|
||
font-size: 0.6875rem;
|
||
color: var(--admin-text-secondary);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 .base-table-card-grid-value) {
|
||
font-weight: 600;
|
||
font-size: 0.75rem;
|
||
color: var(--admin-text);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked) {
|
||
margin: 0;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-box) {
|
||
margin: 0;
|
||
padding: 0.75rem 0.875rem 0.875rem;
|
||
border: none;
|
||
border-radius: 0;
|
||
background: var(--admin-bg-muted);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-label) {
|
||
margin: 0 0 0.5rem;
|
||
padding-bottom: 0.375rem;
|
||
border-bottom: 1px dashed var(--admin-border);
|
||
text-align: center;
|
||
font-size: 0.6875rem;
|
||
font-weight: 600;
|
||
color: var(--admin-text-secondary);
|
||
letter-spacing: 0.01em;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-value) {
|
||
margin-top: 0 !important;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.55;
|
||
color: var(--admin-text-muted);
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .group\/row.mb-2) {
|
||
margin-bottom: 0.125rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .group\/row .flex) {
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .w-1\/3) {
|
||
width: 30%;
|
||
font-size: 0.65rem;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .text-sm) {
|
||
font-size: 0.75rem;
|
||
line-height: 1.3;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style lang="less">
|
||
html.is-dark {
|
||
.sms-panel-page {
|
||
.sms-stat-value--ok,
|
||
.sms-stat-value--emerald {
|
||
color: #6ee7b7;
|
||
}
|
||
|
||
.sms-stat-value--warn {
|
||
color: #fcd34d;
|
||
}
|
||
|
||
.sms-stat-value--danger {
|
||
color: #fda4af;
|
||
}
|
||
|
||
.sms-stat-value--sky {
|
||
color: #7dd3fc;
|
||
}
|
||
|
||
.sms-stat-value--violet {
|
||
color: #c4b5fd;
|
||
}
|
||
|
||
.sms-stat-icon--ok,
|
||
.sms-stat-icon--emerald {
|
||
color: #6ee7b7;
|
||
}
|
||
|
||
.sms-stat-icon--warn {
|
||
color: #fcd34d;
|
||
}
|
||
|
||
.sms-stat-icon--danger {
|
||
color: #fda4af;
|
||
}
|
||
|
||
.sms-stat-icon--sky {
|
||
color: #7dd3fc;
|
||
}
|
||
|
||
.sms-stat-icon--violet {
|
||
color: #c4b5fd;
|
||
}
|
||
}
|
||
}
|
||
</style>
|