feat(ux/sms/engine): complete Persian UX writing overhaul, Wolog SMS integration and respin support
This commit is contained in:
@@ -32,7 +32,7 @@ const allTabs = [
|
||||
];
|
||||
|
||||
const allMoreItems = [
|
||||
{ key: 'm-spins', label: 'تاریخچه اسپین', icon: ReloadOutlined, path: '/m/spins', permission: 'spins:read' },
|
||||
{ key: 'm-spins', label: 'تاریخچه چرخشها', icon: ReloadOutlined, path: '/m/spins', permission: 'spins:read' },
|
||||
{ key: 'm-sms', label: 'لاگ پیامک', icon: MessageOutlined, path: '/m/sms', permission: 'sms:read' },
|
||||
{ key: 'm-reports', label: 'گزارشها', icon: LineChartOutlined, path: '/m/reports', permission: 'reports:read' },
|
||||
{ key: 'm-audit', label: 'لاگ بازرسی', icon: AuditOutlined, path: '/m/audit', permission: 'audit:read' },
|
||||
|
||||
@@ -47,7 +47,7 @@ const mainItems: NavItem[] = [
|
||||
{ key: 'campaigns', label: 'مدیریت کمپینها', icon: PieChartOutlined, path: '/campaigns', permission: 'campaigns:read' },
|
||||
{ key: 'users', label: 'مدیریت کاربران', icon: UserOutlined, path: '/users', permission: 'users:read' },
|
||||
{ key: 'winners', label: 'مدیریت برندگان', icon: TrophyOutlined, path: '/winners', permission: 'winners:read' },
|
||||
{ key: 'spins', label: 'تاریخچه اسپینها', icon: ReloadOutlined, path: '/spins', permission: 'spins:read' },
|
||||
{ key: 'spins', label: 'تاریخچه چرخشها', icon: ReloadOutlined, path: '/spins', permission: 'spins:read' },
|
||||
];
|
||||
|
||||
const utilityItems: NavItem[] = [
|
||||
|
||||
@@ -14,7 +14,7 @@ const titles: Record<string, string> = {
|
||||
'm-campaign-prizes': 'مدیریت جوایز',
|
||||
'm-users': 'کاربران',
|
||||
'm-user-detail': 'جزئیات کاربر',
|
||||
'm-spins': 'اسپینها',
|
||||
'm-spins': 'چرخشها',
|
||||
'm-winners': 'برندگان',
|
||||
'm-sms': 'لاگ پیامک',
|
||||
'm-reports': 'گزارشها',
|
||||
|
||||
@@ -129,7 +129,7 @@ const columns = [
|
||||
{ title: 'تاریخ و ساعت پایان', dataIndex: 'endAt', key: 'endAt' },
|
||||
{ title: 'نوع جوایز', key: 'prizeTypes' },
|
||||
{ title: 'تعداد جوایز', key: 'prizesCount', align: 'center' as const },
|
||||
{ title: 'تعداد اسپینها', key: 'spinsCount', align: 'center' as const },
|
||||
{ title: 'تعداد چرخشها', key: 'spinsCount', align: 'center' as const },
|
||||
{ title: 'عملیات', key: 'actions', width: 70, align: 'center' as const },
|
||||
];
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const designs = [
|
||||
title: 'کلاسیک کازینویی (Classic Casino)',
|
||||
badge: 'جشنوارهها و قرعهکشی',
|
||||
badgeColor: 'gold',
|
||||
desc: 'طرح سنتی و مجلل با حبابهای نور طلایی چشمکزن، اسلایسهای قرمز و مشکی و ستاره برنجی',
|
||||
desc: 'طرح سنتی و مجلل با حبابهای نور طلایی چشمکزن، بخشهای قرمز و مشکی و ستاره برنجی',
|
||||
bg: 'radial-gradient(circle, #2d0a0a 0%, #150505 100%)',
|
||||
accent: '#fbbf24',
|
||||
border: '#d97706',
|
||||
|
||||
@@ -155,7 +155,7 @@ onMounted(loadCampaigns);
|
||||
<span>{{ formatDate(c.startAt) }} → {{ formatDate(c.endAt) }}</span>
|
||||
<span>
|
||||
{{ formatFarsiNumber(c._count?.prizes || 0) }} جایزه ·
|
||||
{{ formatFarsiNumber(c._count?.spins || 0) }} اسپین
|
||||
{{ formatFarsiNumber(c._count?.spins || 0) }} چرخش
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ function initChart() {
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'اسپین',
|
||||
name: 'چرخش',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
@@ -132,7 +132,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<div class="m-kpi amber">
|
||||
<div class="m-kpi__head">
|
||||
<span>اسپینها</span>
|
||||
<span>چرخشها</span>
|
||||
<ReloadOutlined />
|
||||
</div>
|
||||
<div class="m-kpi__val">{{ formatFarsiNumber(dashboardData?.stats?.totalSpins || 0) }}</div>
|
||||
@@ -181,7 +181,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="m-card">
|
||||
<div class="m-card__title">روند اسپین روزانه</div>
|
||||
<div class="m-card__title">روند چرخش روزانه</div>
|
||||
<div ref="chartRef" class="m-chart" />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ onMounted(() => {
|
||||
<div class="m-kpi">
|
||||
<ThunderboltOutlined class="ico amber" />
|
||||
<div class="val">{{ formatFarsiNumber(report?.totalSpins || 0) }}</div>
|
||||
<div class="lbl">کل اسپین</div>
|
||||
<div class="lbl">کل چرخشها</div>
|
||||
</div>
|
||||
<div class="m-kpi">
|
||||
<TrophyOutlined class="ico emerald" />
|
||||
@@ -88,7 +88,7 @@ onMounted(() => {
|
||||
<div class="m-kpi">
|
||||
<UserOutlined class="ico slate" />
|
||||
<div class="val">{{ formatFarsiNumber(report?.zeroSpinUsers || 0) }}</div>
|
||||
<div class="lbl">بدون اسپین</div>
|
||||
<div class="lbl">بدون چرخش</div>
|
||||
</div>
|
||||
<div class="m-kpi wide">
|
||||
<ThunderboltOutlined class="ico indigo" />
|
||||
|
||||
@@ -40,7 +40,7 @@ const moduleLabels: Record<string, string> = {
|
||||
prizes: 'جوایز',
|
||||
users: 'کاربران',
|
||||
chances: 'شانسها',
|
||||
spins: 'اسپینها',
|
||||
spins: 'چرخشها',
|
||||
winners: 'برندگان',
|
||||
sms: 'پیامک',
|
||||
audit: 'بازرسی',
|
||||
|
||||
@@ -91,7 +91,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="!spins.length && !loading" class="m-empty">اسپینی یافت نشد</div>
|
||||
<div v-if="!spins.length && !loading" class="m-empty">هیچ چرخشی یافت نشد</div>
|
||||
<div v-for="s in spins" :key="s.id" class="m-card">
|
||||
<div class="m-card__top">
|
||||
<button
|
||||
|
||||
@@ -109,7 +109,7 @@ onMounted(() => {
|
||||
<div class="m-tabs">
|
||||
<button type="button" :class="{ active: tab === 'chances' }" @click="tab = 'chances'">شانسها</button>
|
||||
<button type="button" :class="{ active: tab === 'prizes' }" @click="tab = 'prizes'">جوایز</button>
|
||||
<button type="button" :class="{ active: tab === 'spins' }" @click="tab = 'spins'">اسپینها</button>
|
||||
<button type="button" :class="{ active: tab === 'spins' }" @click="tab = 'spins'">چرخشها</button>
|
||||
</div>
|
||||
|
||||
<div v-if="tab === 'chances'">
|
||||
@@ -157,7 +157,7 @@ onMounted(() => {
|
||||
<div class="m-card__row"><span>IP</span><strong dir="ltr">{{ s.ipAddress || '—' }}</strong></div>
|
||||
<div class="m-card__row"><span>زمان</span><strong>{{ formatDate(s.createdAt) }}</strong></div>
|
||||
</div>
|
||||
<div v-if="!(user?.spins || []).length" class="m-empty">اسپینی نیست</div>
|
||||
<div v-if="!(user?.spins || []).length" class="m-empty">هیچ چرخشی ثبت نشده است</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ onMounted(() => {
|
||||
</span>
|
||||
</div>
|
||||
<div class="m-user-card__meta">
|
||||
{{ formatFarsiNumber(u._count?.spins || 0) }} اسپین ·
|
||||
{{ formatFarsiNumber(u._count?.spins || 0) }} چرخش ·
|
||||
{{ formatFarsiNumber(u._count?.userPrizes || u._count?.prizes || 0) }} جایزه ·
|
||||
عضویت {{ formatDate(u.createdAt) }}
|
||||
</div>
|
||||
|
||||
@@ -542,7 +542,7 @@ onMounted(() => {
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="ترتیب در گردونه (۱ به بعد)" required tooltip="شماره موقعیت این اسلایس روی گردونه">
|
||||
<a-form-item label="ترتیب در گردونه (۱ به بعد)" required tooltip="شماره موقعیت این بخش روی گردونه">
|
||||
<a-input-number
|
||||
v-model:value="formState.sortOrder"
|
||||
:min="1"
|
||||
@@ -641,7 +641,7 @@ onMounted(() => {
|
||||
<!-- بخش ۴: ظاهر و قالب پیامک -->
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="رنگ پسزمینه اسلایس (Hex)" required>
|
||||
<a-form-item label="رنگ پسزمینه بخش (Hex)" required>
|
||||
<div style="display: flex; gap: 8px; align-items: center">
|
||||
<input
|
||||
v-model="formState.segmentColor"
|
||||
|
||||
@@ -47,7 +47,7 @@ const moduleLabels: Record<string, string> = {
|
||||
prizes: 'مدیریت جوایز',
|
||||
users: 'کاربران سامانه',
|
||||
chances: 'تنظیم دستی شانسها',
|
||||
spins: 'تاریخچه اسپینها',
|
||||
spins: 'تاریخچه چرخشها',
|
||||
winners: 'مدیریت برندگان و کدهای هدیه',
|
||||
sms: 'لاگ و ارسال پیامک',
|
||||
audit: 'لاگ بازرسی (Audit)',
|
||||
|
||||
@@ -139,7 +139,7 @@ onMounted(() => {
|
||||
<div class="spin-list-page">
|
||||
<div class="page-header-title">
|
||||
<div>
|
||||
<h1 class="title">تاریخچه چرخشها (Spins)</h1>
|
||||
<h1 class="title">تاریخچه چرخشهای گردونه</h1>
|
||||
<p class="subtitle">گزارش لحظهای تمام تلاشها، برندگان، کدها و آیپیهای ثبتشده</p>
|
||||
</div>
|
||||
<a-space>
|
||||
|
||||
@@ -122,7 +122,7 @@ const spinColumns = [
|
||||
{ title: 'نتیجه / جایزه', dataIndex: ['prize', 'title'], key: 'prize' },
|
||||
{ title: 'برد؟', dataIndex: 'isWin', key: 'isWin' },
|
||||
{ title: 'آیپی', dataIndex: 'ipAddress', key: 'ipAddress' },
|
||||
{ title: 'تاریخ اسپین', dataIndex: 'createdAt', key: 'createdAt' },
|
||||
{ title: 'زمان چرخش', dataIndex: 'createdAt', key: 'createdAt' },
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
@@ -245,7 +245,7 @@ onMounted(() => {
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="spins" tab="تاریخچه اسپینها">
|
||||
<a-tab-pane key="spins" tab="تاریخچه چرخشها">
|
||||
<a-table
|
||||
:dataSource="user?.spins || []"
|
||||
:columns="spinColumns"
|
||||
|
||||
@@ -141,7 +141,7 @@ const columns = [
|
||||
{ title: 'نام کاربر', dataIndex: 'name', key: 'name' },
|
||||
{ title: 'تاریخ ثبتنام', dataIndex: 'createdAt', key: 'createdAt' },
|
||||
{ title: 'آخرین ورود', dataIndex: 'lastLoginAt', key: 'lastLoginAt' },
|
||||
{ title: 'تعداد اسپین', dataIndex: ['_count', 'spins'], key: 'spinsCount', align: 'center' as const },
|
||||
{ title: 'تعداد چرخشها', dataIndex: ['_count', 'spins'], key: 'spinsCount', align: 'center' as const },
|
||||
{ title: 'تعداد جوایز برنده', key: 'prizesCount', align: 'center' as const },
|
||||
{ title: 'وضعیت حساب', dataIndex: 'status', key: 'status', align: 'center' as const },
|
||||
{ title: 'عملیات', key: 'actions', width: 70, align: 'center' as const },
|
||||
|
||||
Reference in New Issue
Block a user