Files
tehran_taj/apps/admin-panel/src/views/manage/wallet.vue
T
s.esmaeiliandClaude Sonnet 5 f0de478940 admin-panel: fix CSS bugs and dedupe scoped grid utilities
Approved departure from the port's byte-faithful rule (docs/ADMIN-PORT-PLAN.md
bug rows 4-6), fixed and logged rather than silently changed:
- farsi.less: bold/700 @font-face blocks now load iran-yekan-700.woff2
  instead of the 400 file, so bold text actually renders bold. No source
  file exists for weight 300, so that face is dropped rather than faked.
- format("woff") corrected to format("woff2") on all faces.
- Removed 'font-weight: inherit' from the body,* reset, which was flattening
  every heading's weight.

Also deduped 24 view files that each redeclared .grid-2/.grid-3/.grid-4 in
a scoped <style> block with drifted minmax values, though utilities.less
already defines all three globally. Removed the scoped copies; kept
PromotionForm's genuinely different 0.5rem gap as a small .grid-4--tight
modifier instead of a full redeclaration.

Verified: pnpm build passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 15:09:11 +03:30

157 lines
5.4 KiB
Vue

<template>
<!--
Ported from legacy henza_admin/src/views/manage/wallet.vue — the wallet
settings screen. Two tabs, each writing one settings key.
Legacy quirk preserved: the "تاریخ تولد" tab is declared FIRST in the markup
but carries key="2", while "پس از خرید" carries key="1" and is the default
active tab — so the visually-second tab opens first.
-->
<div>
<a-card v-if="hasPermission('config.wallet')" title="مدیریت کیف پول">
<a-tabs default-active-key="1">
<a-tab-pane key="2" tab="تاریخ تولد" force-render>
<a-spin :spinning="loading" tip="لطفا کمی تامل کنید">
<a-form layout="vertical" @submit.prevent="setWallet2">
<div class="grid-2">
<a-form-item
label="انقضا (روز)"
:validate-status="err2.birthday_expire ? 'error' : ''"
:help="err2.birthday_expire"
>
<a-input-number v-model:value="form2.birthday_expire" class="w-full" :min="0" />
</a-form-item>
<a-form-item
label="میزان (تومان)"
:validate-status="err2.birthday_credit ? 'error' : ''"
:help="err2.birthday_credit"
>
<a-input-number v-model:value="form2.birthday_credit" class="w-full" :min="0" />
</a-form-item>
</div>
<a-button type="primary" class="mb-4 mt-4 mr-4" @click="setWallet2">ارسال</a-button>
</a-form>
</a-spin>
</a-tab-pane>
<a-tab-pane key="1" tab="پس از خرید" force-render>
<a-spin :spinning="loading" tip="لطفا کمی تامل کنید">
<a-form layout="vertical" @submit.prevent="setWallet1">
<div class="grid-2">
<a-form-item
label="انقضا (روز)"
:validate-status="err1.expire ? 'error' : ''"
:help="err1.expire"
>
<a-input-number v-model:value="form1.expire" class="w-full" :min="0" />
</a-form-item>
<a-form-item
label="درصد"
:validate-status="err1.percent ? 'error' : ''"
:help="err1.percent"
>
<a-input-number
v-model:value="form1.percent"
class="w-full"
:min="0"
:max="100"
:formatter="(v: number | string) => `${v}%`"
:parser="(v: string) => v.replace('%', '')"
/>
</a-form-item>
</div>
<a-button type="primary" class="mb-4 mt-4 mr-4" @click="setWallet1">ارسال</a-button>
</a-form>
</a-spin>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { message } from 'ant-design-vue';
import { useUsersStore } from '../../stores/users';
import api from '../../api/client';
const users = useUsersStore();
const hasPermission = (rid: string) => Boolean(users.hasPermission(rid));
const loading = ref(false);
const form1 = reactive<Record<string, any>>({ expire: undefined, percent: undefined });
const form2 = reactive<Record<string, any>>({
birthday_expire: undefined,
birthday_credit: undefined,
});
const err1 = reactive<Record<string, string>>({});
const err2 = reactive<Record<string, string>>({});
const REQUIRED = 'لطفا مقدار را وارد کنید';
async function setWallet1() {
Object.keys(err1).forEach((k) => delete err1[k]);
if (form1.expire === undefined || form1.expire === null) err1.expire = REQUIRED;
if (form1.percent === undefined || form1.percent === null) err1.percent = REQUIRED;
if (Object.keys(err1).length) return;
try {
loading.value = true;
await api.post('wallet/config', { expire: form1.expire, percent: form1.percent });
message.success('تغییرات ثبت شد');
} catch (e: any) {
message.error(e?.response?.data?.message ?? 'خطا در ثبت تغییرات');
} finally {
loading.value = false;
}
}
async function setWallet2() {
Object.keys(err2).forEach((k) => delete err2[k]);
if (form2.birthday_expire === undefined || form2.birthday_expire === null) {
err2.birthday_expire = REQUIRED;
}
if (form2.birthday_credit === undefined || form2.birthday_credit === null) {
err2.birthday_credit = REQUIRED;
}
if (Object.keys(err2).length) return;
try {
loading.value = true;
await api.post('wallet/birthday/config', {
birthday_expire: form2.birthday_expire,
birthday_credit: form2.birthday_credit,
});
message.success('تغییرات ثبت شد');
} catch (e: any) {
message.error(e?.response?.data?.message ?? 'خطا در ثبت تغییرات');
} finally {
loading.value = false;
}
}
onMounted(async () => {
try {
const [c1, c2] = await Promise.all([
api.get('wallet/config'),
api.get('wallet/birthday/config'),
]);
Object.assign(form1, c1.data ?? {});
Object.assign(form2, c2.data ?? {});
} catch (e) {
console.log(e);
}
});
</script>
<style scoped>
/* .grid-2 now comes from the global utilities.less (dedup) */
.mb-4 {
margin-bottom: 1rem;
}
.mt-4 {
margin-top: 1rem;
}
</style>