feat(web): migrate CRM screens to Ant Design Vue with central theme

Adopt ant-design-vue and a shared antdTheme for leads, pipeline, invoices, and shared form controls under strict single-page governance.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-18 19:39:59 +03:30
co-authored by Cursor
parent ab538b8304
commit 4abab55a6b
40 changed files with 7529 additions and 11795 deletions
+17 -93
View File
@@ -1,7 +1,10 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, ref } from 'vue';
import { Button as AButton, Typography } from 'ant-design-vue';
import { AudioOutlined, PauseCircleOutlined } from '@ant-design/icons-vue';
import { toFaDigits } from '@/lib/jalali';
import UiIcon from '@/components/UiIcon.vue';
const { Text: ATypographyText } = Typography;
type SpeechResult = {
isFinal: boolean;
@@ -186,102 +189,23 @@ onBeforeUnmount(() => {
</script>
<template>
<div class="vtt-wrap" :class="[variant, { live: recording }]">
<button
class="vtt"
type="button"
:class="{ on: recording }"
<div>
<a-button
:type="recording ? 'primary' : 'default'"
:danger="recording"
:disabled="disabled"
:aria-label="ariaLabel"
:title="ariaLabel"
@click="toggle"
>
<span class="glyph" aria-hidden="true">
<UiIcon :name="recording ? 'stop' : 'mic'" :size="variant === 'toolbar' ? 18 : 16" />
</span>
<span v-if="recording" class="timer">{{ timerLabel }}</span>
</button>
<p v-if="error && !hideError" class="err">{{ error }}</p>
<template #icon>
<PauseCircleOutlined v-if="recording" />
<AudioOutlined v-else />
</template>
<span v-if="recording">{{ timerLabel }}</span>
</a-button>
<div v-if="error && !hideError">
<a-typography-text type="danger">{{ error }}</a-typography-text>
</div>
</div>
</template>
<style scoped>
.vtt-wrap {
display: grid;
gap: 0.3rem;
justify-items: start;
}
.vtt {
display: inline-flex;
align-items: center;
gap: 0.45rem;
border: 1px solid #d7e3dc;
background: #f4faf7;
color: #0f766e;
border-radius: var(--radius);
min-height: var(--control-h);
height: var(--control-h);
padding: 0 0.75rem;
font: inherit;
font-size: 0.8rem;
font-weight: 700;
cursor: pointer;
}
.vtt:hover:not(:disabled) {
background: #ecfdf5;
}
.vtt.on {
border-color: #5eead4;
background: #042f2e;
color: #ecfdf5;
padding-inline: 0.7rem 0.85rem;
box-shadow: 0 10px 24px rgba(15, 118, 110, 0.22);
}
.vtt:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.glyph {
width: 22px;
height: 22px;
display: grid;
place-items: center;
flex-shrink: 0;
}
.timer {
font-variant-numeric: tabular-nums;
min-width: 2.4rem;
}
.toolbar .vtt {
width: 38px;
height: 38px;
min-height: 38px;
padding: 0;
border-radius: var(--radius);
border-color: transparent;
background: #f8fafc;
justify-content: center;
}
.toolbar.live .vtt {
width: auto;
min-height: var(--control-h);
height: var(--control-h);
padding: 0 0.7rem;
border-radius: var(--radius);
background: #042f2e;
}
.err {
margin: 0;
color: #b91c1c;
font-size: 0.78rem;
}
</style>