feat: move brand name to one config and redesign the accounting card
اپ: نام برند در همهی صفحهها و پیامها یکسان شده و از یک تنظیم مرکزی خوانده میشود. اپ: کارت حسابداری در صفحهی مشتری بازطراحی شد؛ ماندهها و وضعیت لینک واضحتر نمایش داده میشوند. پنل: کارت حسابداری در پروندهی مشتری بازطراحی شد؛ ماندهها، آخرین ارسال و فرم لینک دستی واضحتر شدهاند. پنل: ستون «کد» در جدولها بهطور پیشفرض پنهان است و از انتخاب ستونها قابل نمایش است. پنل: تغییر نمایش ستونها در جدولها بلافاصله اعمال میشود. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
5d2a1efb0c
commit
532ceee189
@@ -289,7 +289,7 @@ const THEMES: { value: ThemeChoice; label: string; icon: string }[] = [
|
||||
}
|
||||
.ap__theme.is-on {
|
||||
color: @ink;
|
||||
box-shadow: inset 0 0 0 1.5px @ink;
|
||||
box-shadow: inset 0 0 0 1.5px @wine;
|
||||
}
|
||||
|
||||
.ap__preview {
|
||||
@@ -306,8 +306,8 @@ const THEMES: { value: ThemeChoice; label: string; icon: string }[] = [
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
}
|
||||
|
||||
.ap__row {
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
v-model:show="data.notify"
|
||||
art="logout"
|
||||
title="آیا می خواهید از برنامه خارج شوید؟"
|
||||
description="برای استفاده دوباره از امکانات منتزر، باید دوباره وارد حساب کاربری خود شوید."
|
||||
:description="`برای استفاده دوباره از امکانات ${brand.name}، باید دوباره وارد حساب کاربری خود شوید.`"
|
||||
confirm-text="بله"
|
||||
cancel-text="بی خیال"
|
||||
@confirm="logOut"
|
||||
@@ -232,7 +232,7 @@
|
||||
v-model:show="data.loginNotify"
|
||||
art="login"
|
||||
title="ابتدا وارد حساب کاربری شوید"
|
||||
description="برای دسترسی به تمامی امکانات نرم افزار منتزر، گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید."
|
||||
:description="`برای دسترسی به تمامی امکانات نرم افزار ${brand.name}، گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید.`"
|
||||
confirm-text="ورود/ثبت نام"
|
||||
cancel-text="بیخیال"
|
||||
@confirm="router.push({ name: 'login' })"
|
||||
@@ -241,6 +241,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../../brand'
|
||||
import { computed, nextTick, onActivated, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Loading, PullRefresh } from 'vant'
|
||||
@@ -453,9 +454,9 @@ onMounted(async () => {
|
||||
margin-bottom: 28px;
|
||||
padding: 20px 18px 16px;
|
||||
border-radius: 26px;
|
||||
background: linear-gradient(155deg, @heroSurface 0%, @heroSurface 55%, #5E1427 130%);
|
||||
background: linear-gradient(155deg, @heroSurface 0%, @heroSurface 55%, #5A0027 130%);
|
||||
color: #fff;
|
||||
box-shadow: 0 22px 40px -26px rgba(20, 22, 26, 0.32);
|
||||
box-shadow: 0 22px 40px -26px rgba(124, 0, 53, 0.32);
|
||||
}
|
||||
|
||||
.dash__heroTop {
|
||||
|
||||
@@ -125,7 +125,7 @@ watch(() => favorites.ids.join(','), load)
|
||||
font-family: inherit;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="auth__mark" aria-hidden="true">
|
||||
<Icon icon="vuesax-linear:security-user" size="28" />
|
||||
</div>
|
||||
<h2 class="auth__title">ورود به منتزر</h2>
|
||||
<h2 class="auth__title">ورود به {{ brand.name }}</h2>
|
||||
<p class="auth__sub">
|
||||
{{ body.step === 1 ? 'شماره موبایل خود را وارد کنید' : 'رمز یکبارمصرف را وارد کنید' }}
|
||||
</p>
|
||||
@@ -81,6 +81,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../../brand'
|
||||
import { computed, nextTick, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Form, FormItem, Input } from 'ant-design-vue'
|
||||
@@ -147,7 +148,7 @@ const sendPhone = async () => {
|
||||
// this branch the person is told "خطا در ارسال کد"، which is the
|
||||
// very confusion the 404 was added to remove.
|
||||
const text =
|
||||
e instanceof Error && /\((?:429|404)\)/.test(e.message) ? extractServerMessage(e) : null
|
||||
e instanceof Error && /\((?:429|404|403)\)/.test(e.message) ? extractServerMessage(e) : null
|
||||
toast.error(text ?? 'خطا در ارسال کد. لطفاً دوباره تلاش کنید.')
|
||||
} finally {
|
||||
body.loading = false
|
||||
@@ -294,8 +295,8 @@ const btnText = computed(() => (body.step === 1 ? 'دریافت رمز عبور'
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
transition: transform 0.15s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
@@ -347,7 +348,7 @@ const btnText = computed(() => (body.step === 1 ? 'دریافت رمز عبور'
|
||||
|
||||
.otp-input:focus {
|
||||
outline: none;
|
||||
border-color: @ink;
|
||||
border-color: @wine;
|
||||
}
|
||||
|
||||
.otp-input.error {
|
||||
|
||||
@@ -298,9 +298,9 @@ onActivated(() => {
|
||||
}
|
||||
|
||||
.ord__chip.is-on {
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
border-color: @ink;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
border-color: @wine;
|
||||
}
|
||||
|
||||
.ord__chip:active {
|
||||
@@ -430,15 +430,15 @@ onActivated(() => {
|
||||
}
|
||||
|
||||
.detail__step.is-done .detail__stepDot {
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
border-color: @ink;
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
border-color: @wine;
|
||||
}
|
||||
|
||||
.detail__step.is-active .detail__stepDot {
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
border-color: @wine;
|
||||
background: @wineDeep;
|
||||
border-color: @wineDeep;
|
||||
}
|
||||
|
||||
.detail__stepTitle {
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
drops the leading 0 and shows spinners), and Persian/Arabic digits are
|
||||
converted to Latin as the user types, so the validators accept them.
|
||||
|
||||
STUBBED, unchanged from before: legacy's POST /major/register has no
|
||||
counterpart in apps/api yet. Submitting shows the "awaiting review" state
|
||||
without persisting anything — the copy says the request is under review,
|
||||
which is what legacy said too, so it does not claim an account exists.
|
||||
Submits to POST customer/auth/register (legacy POST /major/register). The new
|
||||
customer is created inactive and lands in the admin's «مشتریان در انتظار»
|
||||
list; they can sign in once an admin approves them.
|
||||
-->
|
||||
<template>
|
||||
<div class="reg">
|
||||
@@ -192,6 +191,8 @@ import { useRouter } from 'vue-router'
|
||||
import { Form, FormItem, Input, Textarea } from 'ant-design-vue'
|
||||
import Icon from '../../components/icon/Icon.vue'
|
||||
import PageBar from '../../components/ui/PageBar.vue'
|
||||
import { api } from '../../api/client'
|
||||
import { toast } from '../../composables/useToast'
|
||||
|
||||
const emit = defineEmits<{ (e: 'register', value: boolean): void }>()
|
||||
const router = useRouter()
|
||||
@@ -235,14 +236,35 @@ async function onFinish() {
|
||||
}
|
||||
try {
|
||||
loading.value = true
|
||||
// Stubbed — no create-customer endpoint exists in apps/api yet.
|
||||
await new Promise((r) => setTimeout(r, 600))
|
||||
await api.register({
|
||||
fname: String(formModel.fname ?? ''),
|
||||
lname: String(formModel.lname ?? ''),
|
||||
mobile: String(formModel.mobile ?? ''),
|
||||
store_name: String(formModel.store_name ?? ''),
|
||||
phone: String(formModel.phone ?? ''),
|
||||
address: String(formModel.address ?? ''),
|
||||
city: String(formModel.city ?? ''),
|
||||
})
|
||||
done.value = true
|
||||
window.scrollTo({ top: 0 })
|
||||
} catch (e: unknown) {
|
||||
toast.error(serverMessage(e) ?? 'خطا در ثبت اطلاعات. لطفاً دوباره تلاش کنید.')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** The API's Persian `message` (duplicate number, bad mobile, throttle), if any. */
|
||||
function serverMessage(e: unknown): string | null {
|
||||
const match = e instanceof Error ? e.message.match(/\{.*\}/) : null
|
||||
if (!match) return null
|
||||
try {
|
||||
const parsed = JSON.parse(match[0]) as { message?: string | string[] }
|
||||
return Array.isArray(parsed.message) ? parsed.message[0] ?? null : parsed.message ?? null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -364,16 +386,19 @@ async function onFinish() {
|
||||
}
|
||||
}
|
||||
|
||||
// Same field language as the rest of the app (SearchInput, login): @canvas
|
||||
// fill, 1px @hairline edge, and on focus only the edge turns @wineSoft —
|
||||
// no tinted fill, no glow ring.
|
||||
:deep(.ant-input),
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
border-radius: 14px;
|
||||
border: 1.5px solid @hairline;
|
||||
background: @canvasSoft;
|
||||
border: 1px solid @hairline;
|
||||
background: @canvas;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: @ink;
|
||||
box-shadow: none;
|
||||
transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease;
|
||||
transition: border-color 0.18s ease;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
@@ -386,9 +411,10 @@ async function onFinish() {
|
||||
padding: 0 14px;
|
||||
|
||||
.ant-input {
|
||||
min-height: 46px;
|
||||
min-height: 48px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -403,7 +429,7 @@ async function onFinish() {
|
||||
:deep(.ant-input-prefix) {
|
||||
margin-inline-end: 10px;
|
||||
color: @inkFaint;
|
||||
transition: color 0.2s ease;
|
||||
transition: color 0.18s ease;
|
||||
}
|
||||
|
||||
:deep(.ant-input::placeholder) {
|
||||
@@ -418,29 +444,21 @@ async function onFinish() {
|
||||
:deep(.ant-input:focus),
|
||||
:deep(.ant-input-focused),
|
||||
:deep(.ant-input-affix-wrapper-focused) {
|
||||
border-color: @wine;
|
||||
border-color: @wineSoft;
|
||||
background: @canvas;
|
||||
box-shadow: 0 0 0 4px alpha(@wine, 10%);
|
||||
|
||||
.ant-input-prefix {
|
||||
color: @wine;
|
||||
}
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper-focused .ant-input-prefix) {
|
||||
color: @wine;
|
||||
color: @wineSoft;
|
||||
}
|
||||
|
||||
// Errors
|
||||
// Errors: red edge only — like focus, no ring.
|
||||
:deep(.ant-form-item-has-error .ant-input),
|
||||
:deep(.ant-form-item-has-error .ant-input-affix-wrapper) {
|
||||
border-color: @red !important;
|
||||
background: alpha(@red, 3%);
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-has-error .ant-input:focus),
|
||||
:deep(.ant-form-item-has-error .ant-input-affix-wrapper-focused) {
|
||||
box-shadow: 0 0 0 4px alpha(@red, 10%) !important;
|
||||
background: @canvas;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-has-error .ant-input-affix-wrapper .ant-input) {
|
||||
@@ -481,14 +499,15 @@ async function onFinish() {
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
background: @wineWash;
|
||||
color: @wine;
|
||||
color: @inkSoft;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
line-height: 1.8;
|
||||
|
||||
:deep(svg) {
|
||||
flex: 0 0 auto;
|
||||
margin-top: 2px;
|
||||
color: @wine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,9 +545,9 @@ async function onFinish() {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: @onInk;
|
||||
background: @ink;
|
||||
box-shadow: 0 10px 22px -12px alpha(@ink, 70%);
|
||||
color: #fff;
|
||||
background: @wine;
|
||||
box-shadow: 0 10px 22px -12px alpha(@wine, 70%);
|
||||
transition: transform 0.15s ease, opacity 0.2s ease;
|
||||
|
||||
&:active {
|
||||
|
||||
Reference in New Issue
Block a user