diff --git a/apps/web/src/components/DataAccessEditor.vue b/apps/web/src/components/DataAccessEditor.vue index 73571f4..ce48ad4 100644 --- a/apps/web/src/components/DataAccessEditor.vue +++ b/apps/web/src/components/DataAccessEditor.vue @@ -1,6 +1,17 @@ - - diff --git a/apps/web/src/components/DataAccessPolicyPanel.vue b/apps/web/src/components/DataAccessPolicyPanel.vue index 869171d..5fc866c 100644 --- a/apps/web/src/components/DataAccessPolicyPanel.vue +++ b/apps/web/src/components/DataAccessPolicyPanel.vue @@ -1,5 +1,6 @@ - - diff --git a/apps/web/src/components/RoleDetailPanel.vue b/apps/web/src/components/RoleDetailPanel.vue new file mode 100644 index 0000000..9ccfdd8 --- /dev/null +++ b/apps/web/src/components/RoleDetailPanel.vue @@ -0,0 +1,144 @@ + + + diff --git a/apps/web/src/components/TeamsSettingsPanel.vue b/apps/web/src/components/TeamsSettingsPanel.vue index c6f35de..4e2c858 100644 --- a/apps/web/src/components/TeamsSettingsPanel.vue +++ b/apps/web/src/components/TeamsSettingsPanel.vue @@ -18,9 +18,36 @@ import { emptySectionDataAccess, sectionHasDataAccessLimits, } from '@mento/shared'; -import { UiButton, UiEmptyState, UiField, UiInput } from '@/components/ui'; import DataAccessPolicyPanel from '@/components/DataAccessPolicyPanel.vue'; import { formatLocalDateInput, parseLocalInput, toFaDigits } from '@/lib/jalali'; +import { + Alert as AAlert, + Button as AButton, + Card as ACard, + Checkbox as ACheckbox, + Col as ACol, + Collapse as ACollapse, + CollapsePanel as ACollapsePanel, + Divider as ADivider, + Empty as AEmpty, + Flex as AFlex, + Form as AForm, + FormItem as AFormItem, + Input as AInput, + List as AList, + ListItem as AListItem, + ListItemMeta as AListItemMeta, + Radio as ARadio, + RadioGroup as ARadioGroup, + Row as ARow, + Select as ASelect, + Space as ASpace, + Spin as ASpin, + Tag as ATag, + Typography, +} from 'ant-design-vue'; + +const { Text: ATypographyText } = Typography; const props = defineProps<{ users: OrgUserDto[]; @@ -36,7 +63,7 @@ const showPriorArchive = computed( const priorUsers = computed(() => props.users.filter((u) => u.isActive !== false)); const selectedPriorUsers = computed(() => priorUsers.value.filter((u) => u.priorArchiveEnabled)); const priorSearch = ref(''); -const priorPick = ref(''); +const priorPick = ref(undefined); const priorSavingId = ref(''); const cutoffLocal = ref(''); const cutoffSaving = ref(false); @@ -47,10 +74,16 @@ const availablePriorUsers = computed(() => { return priorUsers.value.filter((u) => { if (u.priorArchiveEnabled) return false; if (!q) return true; - return [u.fullName, u.email, u.mobile].filter(Boolean).some((v) => String(v).toLowerCase().includes(q)); + return [u.fullName, u.email, u.mobile] + .filter(Boolean) + .some((v) => String(v).toLowerCase().includes(q)); }); }); +const availablePriorOptions = computed(() => + availablePriorUsers.value.map((u) => ({ value: u.id, label: u.fullName })), +); + const emit = defineEmits<{ refreshUsers: []; }>(); @@ -73,9 +106,14 @@ const editableUsers = computed(() => props.users.filter((u) => u.isActive !== false && u.role !== 'owner' && u.role !== 'admin'), ); +const memberRoleOptions = [ + { value: 'member', label: 'عضو' }, + { value: 'manager', label: 'سرپرست' }, +]; + async function setPriorArchive(userId: string, enabled: boolean) { priorSavingId.value = userId; - priorPick.value = ''; + priorPick.value = undefined; error.value = ''; ok.value = ''; try { @@ -113,7 +151,10 @@ async function loadPriorArchiveCutoff() { applyCutoffFromOrg(data); } catch (e) { error.value = axios.isAxiosError(e) - ? String((e.response?.data as { message?: string })?.message ?? 'بارگذاری تاریخ بایگانی ناموفق بود.') + ? String( + (e.response?.data as { message?: string })?.message ?? + 'بارگذاری تاریخ بایگانی ناموفق بود.', + ) : 'بارگذاری تاریخ بایگانی ناموفق بود.'; } finally { cutoffLoading.value = false; @@ -135,7 +176,10 @@ async function onCutoffChange(value: string) { await auth.hydrate(); } catch (e) { error.value = axios.isAxiosError(e) - ? String((e.response?.data as { message?: string })?.message ?? 'ذخیره تاریخ بایگانی ناموفق بود.') + ? String( + (e.response?.data as { message?: string })?.message ?? + 'ذخیره تاریخ بایگانی ناموفق بود.', + ) : 'ذخیره تاریخ بایگانی ناموفق بود.'; await loadPriorArchiveCutoff(); } finally { @@ -143,9 +187,10 @@ async function onCutoffChange(value: string) { } } -function pickPriorUser(userId: string) { - if (!userId) return; - void setPriorArchive(userId, true); +function pickPriorUser(userId: unknown) { + const id = userId == null ? '' : String(userId); + if (!id) return; + void setPriorArchive(id, true); } async function load() { @@ -242,9 +287,12 @@ function toggleMember(userId: string) { } } -function setRole(userId: string, role: TeamMemberRole) { +function setRole(userId: string, role: unknown) { if (!memberDraft.value[userId]) return; - memberDraft.value = { ...memberDraft.value, [userId]: role }; + memberDraft.value = { + ...memberDraft.value, + [userId]: String(role) as TeamMemberRole, + }; } async function saveMembers() { @@ -358,360 +406,230 @@ onMounted(() => { - - diff --git a/apps/web/src/components/automations/AutomationCanvas.vue b/apps/web/src/components/automations/AutomationCanvas.vue index 6c55871..1257b67 100644 --- a/apps/web/src/components/automations/AutomationCanvas.vue +++ b/apps/web/src/components/automations/AutomationCanvas.vue @@ -1,5 +1,5 @@ - - diff --git a/apps/web/src/components/ui/UiPageHeader.vue b/apps/web/src/components/ui/UiPageHeader.vue index 4b0a157..ae5806f 100644 --- a/apps/web/src/components/ui/UiPageHeader.vue +++ b/apps/web/src/components/ui/UiPageHeader.vue @@ -23,9 +23,7 @@ withDefaults(

{{ title }}

- -

{{ lead || description }}

- +
@@ -87,13 +85,6 @@ withDefaults( line-height: var(--lh-tight); } -.lead { - margin: 0; - font-size: var(--fs-sm); - color: var(--muted); - line-height: var(--lh-normal); -} - .actions { display: flex; align-items: center; diff --git a/apps/web/src/views/AttachmentsView.vue b/apps/web/src/views/AttachmentsView.vue index 25e0ca8..5a7a051 100644 --- a/apps/web/src/views/AttachmentsView.vue +++ b/apps/web/src/views/AttachmentsView.vue @@ -1,27 +1,69 @@ + + پیوست‌ها - + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/src/views/AuditLogsView.vue b/apps/web/src/views/AuditLogsView.vue index 3c27892..866cbad 100644 --- a/apps/web/src/views/AuditLogsView.vue +++ b/apps/web/src/views/AuditLogsView.vue @@ -13,21 +13,45 @@ import type { DeletionArchiveListResponse, DeletionRetention, } from '@mento/shared'; +import { formatClockFa, formatJalaliNumeric, toFaDigits } from '@/lib/jalali'; +import { registerTableColumns, useTableColumns } from '@/lib/tableColumns'; import { - UiButton, - UiCard, - UiEmptyState, - UiField, - UiInput, - UiModal, - UiPageHeader, - UiPagination, -} from '@/components/ui'; -import UiIcon from '@/components/UiIcon.vue'; -import { formatClockFa, formatJalaliNumeric } from '@/lib/jalali'; + Alert as AAlert, + Button as AButton, + Card as ACard, + Col as ACol, + Empty as AEmpty, + Flex as AFlex, + Form as AForm, + FormItem as AFormItem, + Input as AInput, + Modal as AModal, + Pagination as APagination, + Row as ARow, + Select as ASelect, + Space as ASpace, + Spin as ASpin, + Table as ATable, + Tabs as ATabs, + TabPane as ATabPane, + Tag as ATag, + Typography, +} from 'ant-design-vue'; +import type { TableColumnsType } from 'ant-design-vue'; +import { ReloadOutlined } from '@ant-design/icons-vue'; + +const { Title: ATypographyTitle, Text: ATypographyText } = Typography; type LogTab = 'all' | 'creates' | 'edits' | 'deletes' | 'attendance'; +type AttendanceRow = { + id: string; + fullName: string; + kind: 'login' | 'logout' | 'forced_logout'; + at: string; + afterHours: boolean; +}; + const TABS: Array<{ key: LogTab; label: string; action?: string }> = [ { key: 'all', label: 'همه' }, { key: 'attendance', label: 'ورود و خروج' }, @@ -61,6 +85,10 @@ const RETENTION_OPTIONS: Array<{ value: DeletionRetention; label: string }> = [ { value: 'never', label: 'هرگز' }, ]; +const AUDIT_COL_LABELS = ['زمان', 'کاربر', 'عملیات', 'موضوع', 'شرح'] as const; +registerTableColumns('audit-logs', [...AUDIT_COL_LABELS]); +const tableCols = useTableColumns(() => 'audit-logs'); + const router = useRouter(); const auth = useAuthStore(); const isOwner = computed(() => auth.user?.role === 'owner'); @@ -84,16 +112,7 @@ const page = ref(1); const pageSize = usePageSize('audit-logs'); const data = ref(null); const deletions = ref(null); -const attendance = ref<{ - items: Array<{ - id: string; - fullName: string; - kind: 'login' | 'logout' | 'forced_logout'; - at: string; - afterHours: boolean; - }>; - total: number; -} | null>(null); +const attendance = ref<{ items: AttendanceRow[]; total: number } | null>(null); const retention = ref('never'); const purgeId = ref(null); @@ -118,6 +137,72 @@ const purgeItem = computed( () => deletions.value?.items.find((item) => item.id === purgeId.value) ?? null, ); +const actorOptions = computed(() => [ + { value: '', label: 'همه' }, + ...actors.value.map((user) => ({ value: user.id, label: user.fullName })), +]); + +const auditColumns = computed>(() => { + const all: TableColumnsType = [ + { title: 'زمان', key: 'زمان', width: 120 }, + { title: 'کاربر', key: 'کاربر' }, + { title: 'عملیات', key: 'عملیات', width: 110 }, + { title: 'موضوع', key: 'موضوع' }, + { title: 'شرح', key: 'شرح' }, + { title: '', key: 'مشاهده', width: 100 }, + ]; + return all.filter((col) => { + const key = String(col.key ?? ''); + if (key === 'مشاهده') return true; + if (!AUDIT_COL_LABELS.includes(key as (typeof AUDIT_COL_LABELS)[number])) return true; + return tableCols.isVisible(key); + }); +}); + +const deletionColumns: TableColumnsType = [ + { title: 'زمان', key: 'زمان', width: 120 }, + { title: 'انجام‌دهنده', key: 'انجام‌دهنده' }, + { title: 'نوع', key: 'نوع' }, + { title: 'عنوان', key: 'عنوان' }, + { title: 'شرح', key: 'شرح' }, + { title: '', key: 'عملیات', width: 220 }, +]; + +const attendanceColumns: TableColumnsType = [ + { title: 'زمان', key: 'زمان', width: 120 }, + { title: 'کاربر', key: 'کاربر' }, + { title: 'رویداد', key: 'رویداد' }, +]; + +const initialLoading = computed( + () => loading.value && !data.value && !deletions.value && !attendance.value, +); + +const showAuditEmpty = computed( + () => + !showingArchive.value && + !showingAttendance.value && + !loading.value && + data.value && + !data.value.items.length, +); + +const showArchiveEmpty = computed( + () => + showingArchive.value && + !loading.value && + deletions.value && + !deletions.value.items.length, +); + +const showAttendanceEmpty = computed( + () => + showingAttendance.value && + !loading.value && + attendance.value && + !attendance.value.items.length, +); + function entityLabel(entityType: string | null) { if (!entityType) return 'مورد'; return ENTITY_LABELS[entityType] ?? 'مورد'; @@ -283,9 +368,10 @@ function attendanceLabel(kind: string, afterHours: boolean) { return 'خروج'; } -function selectTab(next: LogTab) { - if (tab.value === next) return; - tab.value = next; +function selectTab(next: string | number) { + const key = String(next) as LogTab; + if (tab.value === key) return; + tab.value = key; okMsg.value = ''; data.value = null; deletions.value = null; @@ -293,14 +379,16 @@ function selectTab(next: LogTab) { void load(true); } -function onPage(next: number) { - page.value = next; - void load(); -} - -function onPageSize(size: number) { - pageSize.value = size; - if (page.value === 1) void load(); +function onPaginationChange(nextPage: number, nextSize: number) { + const sizeChanged = nextSize !== pageSize.value; + page.value = nextPage; + if (sizeChanged) { + pageSize.value = nextSize; + if (page.value !== 1) { + page.value = 1; + } + } + void load(sizeChanged); } async function saveRetention() { @@ -350,6 +438,14 @@ async function confirmPurge() { } } +function formatWhen(iso: string) { + const d = new Date(iso); + return { + clock: formatClockFa(d), + date: formatJalaliNumeric(d), + }; +} + onMounted(() => { void loadActors(); void load(); @@ -357,503 +453,293 @@ onMounted(() => { + + + + + صفحه {{ toFaDigits(page) }} از {{ toFaDigits(totalPages) }} + + + - -

+ + + + + + + + صفحه {{ toFaDigits(page) }} از {{ toFaDigits(totalPages) }} + + + + + + + + + + + + + + + صفحه {{ toFaDigits(page) }} از {{ toFaDigits(totalPages) }} + + + + + + + «{{ purgeItem.label }}» برای همیشه پاک شود؟ بعد از این دیگر قابل بازگشت نیست. -

- -
- + + + - - diff --git a/apps/web/src/views/AutomationsSettingsView.vue b/apps/web/src/views/AutomationsSettingsView.vue index 3d5a24b..e501fd1 100644 --- a/apps/web/src/views/AutomationsSettingsView.vue +++ b/apps/web/src/views/AutomationsSettingsView.vue @@ -1,5 +1,5 @@ - - diff --git a/apps/web/src/views/CustomFieldsSettingsView.vue b/apps/web/src/views/CustomFieldsSettingsView.vue index bdd5df5..a727e14 100644 --- a/apps/web/src/views/CustomFieldsSettingsView.vue +++ b/apps/web/src/views/CustomFieldsSettingsView.vue @@ -2,23 +2,37 @@ import { computed, onMounted, ref } from 'vue'; import axios from 'axios'; import { api } from '@/lib/api'; -import { - UiBadge, - UiButton, - UiCard, - UiEmptyState, - UiField, - UiInput, - UiPageHeader, - UiSelect, -} from '@/components/ui'; -import UiIcon from '@/components/UiIcon.vue'; import type { CreateCustomFieldRequest, CustomFieldDefinitionDto, CustomFieldEntity, CustomFieldType, } from '@mento/shared'; +import { + Alert as AAlert, + Button as AButton, + Card as ACard, + Checkbox as ACheckbox, + Col as ACol, + Divider as ADivider, + Empty as AEmpty, + Flex as AFlex, + Form as AForm, + FormItem as AFormItem, + Input as AInput, + List as AList, + ListItem as AListItem, + ListItemMeta as AListItemMeta, + Row as ARow, + Select as ASelect, + Space as ASpace, + Spin as ASpin, + Tag as ATag, + Typography, +} from 'ant-design-vue'; +import { PlusOutlined, ReloadOutlined } from '@ant-design/icons-vue'; + +const { Title: ATypographyTitle, Text: ATypographyText } = Typography; const loading = ref(false); const saving = ref(false); @@ -47,12 +61,44 @@ const typeLabels: Record = { select: 'لیست انتخابی', }; +const entityOptions = [ + { value: 'person', label: 'شخص' }, + { value: 'company', label: 'شرکت' }, + { value: 'deal', label: 'معامله' }, + { value: 'note', label: 'یادداشت' }, +]; + +const typeOptions = [ + { value: 'text', label: 'متن' }, + { value: 'number', label: 'عدد' }, + { value: 'date', label: 'تاریخ' }, + { value: 'boolean', label: 'بله/خیر' }, + { value: 'select', label: 'لیست انتخابی' }, +]; + +const filterOptions = [ + { value: 'all', label: 'همه موجودیت‌ها' }, + ...entityOptions, +]; + const visible = computed(() => filter.value === 'all' ? fields.value : fields.value.filter((f) => f.entityType === filter.value), ); +function entityTagColor(entity: CustomFieldEntity) { + if (entity === 'deal') return 'warning'; + if (entity === 'company') return 'processing'; + return 'blue'; +} + +function fieldDescription(field: CustomFieldDefinitionDto) { + const parts = [`کلید سیستمی: ${field.key}`]; + if (field.options?.length) parts.push(`گزینه‌ها: ${field.options.join('، ')}`); + return parts.join(' · '); +} + async function load() { loading.value = true; error.value = ''; @@ -124,258 +170,114 @@ onMounted(load); - - diff --git a/apps/web/src/views/DeletionArchivesView.vue b/apps/web/src/views/DeletionArchivesView.vue index 293497c..f8bd9ba 100644 --- a/apps/web/src/views/DeletionArchivesView.vue +++ b/apps/web/src/views/DeletionArchivesView.vue @@ -5,13 +5,38 @@ import axios from 'axios'; import type { AuditLogDto, AuditLogListResponse, + DeletionArchiveDto, DeletionArchiveListResponse, DeletionRetention, } from '@mento/shared'; import { api } from '@/lib/api'; import { formatJalaliDateTime, toFaDigits } from '@/lib/jalali'; -import { UiButton, UiCard, UiEmptyState, UiModal, UiPageHeader, UiPagination } from '@/components/ui'; import { PAGE_SIZE_OPTIONS, usePageSize } from '@/lib/pageSize'; +import { + Alert as AAlert, + Button as AButton, + Card as ACard, + Col as ACol, + Empty as AEmpty, + Flex as AFlex, + Form as AForm, + FormItem as AFormItem, + Input as AInput, + List as AList, + ListItem as AListItem, + ListItemMeta as AListItemMeta, + Modal as AModal, + Pagination as APagination, + Row as ARow, + Select as ASelect, + Space as ASpace, + Spin as ASpin, + Tabs as ATabs, + TabPane as ATabPane, + Typography, +} from 'ant-design-vue'; + +const { Title: ATypographyTitle, Text: ATypographyText } = Typography; type HistoryTab = 'creates' | 'edits' | 'deletes'; @@ -102,6 +127,19 @@ function rowNo(index: number) { const purgeItem = computed(() => data.value?.items.find((item) => item.id === purgeId.value) ?? null); +const actorOptions = computed(() => [ + { value: '', label: 'همه' }, + ...actors.value.map((user) => ({ value: user.id, label: user.fullName })), +]); + +const showDeletesEmpty = computed( + () => tab.value === 'deletes' && !error.value && !loading.value && !data.value?.items.length, +); + +const showAuditEmpty = computed( + () => tab.value !== 'deletes' && !error.value && !loading.value && !audit.value?.items.length, +); + function apiError(e: unknown, fallback: string) { if (axios.isAxiosError(e)) { const msg = (e.response?.data as { message?: string | string[] })?.message; @@ -352,25 +390,26 @@ async function loadActors() { } } -function onPage(next: number) { - page.value = next; - void load(); +function onPaginationChange(nextPage: number, nextSize: number) { + const sizeChanged = nextSize !== pageSize.value; + page.value = nextPage; + if (sizeChanged) { + pageSize.value = nextSize; + page.value = 1; + } + void load(sizeChanged); } -function onPageSize(size: number) { - pageSize.value = size; - if (page.value === 1) void load(); -} - -function selectTab(next: HistoryTab) { - if (next === tab.value) return; +function selectTab(next: string | number) { + const key = String(next) as HistoryTab; + if (key === tab.value) return; q.value = ''; page.value = 1; data.value = null; audit.value = null; error.value = ''; okMsg.value = ''; - void router.replace({ query: next === 'deletes' ? {} : { tab: next } }); + void router.replace({ query: key === 'deletes' ? {} : { tab: key } }); } async function load(resetPage = false) { @@ -450,6 +489,21 @@ async function confirmPurge() { } } +function deletionDescription(item: DeletionArchiveDto) { + return [ + item.modelLabel, + `${item.deletedByName || 'سیستم'} · ${formatJalaliDateTime(new Date(item.deletedAt))}`, + ].join(' · '); +} + +function auditDescription(row: AuditLogDto) { + const parts: string[] = []; + if (pathPhrase(row) !== rowHeading(row)) parts.push(pathPhrase(row)); + parts.push(row.actor?.fullName || 'سیستم'); + parts.push(formatJalaliDateTime(new Date(row.createdAt))); + return parts.join(' · '); +} + watch(tab, () => { void load(); }, { immediate: true }); @@ -462,319 +516,187 @@ void loadActors(); - - diff --git a/apps/web/src/views/FaqSettingsView.vue b/apps/web/src/views/FaqSettingsView.vue index c4b0d48..9bdc164 100644 --- a/apps/web/src/views/FaqSettingsView.vue +++ b/apps/web/src/views/FaqSettingsView.vue @@ -8,16 +8,28 @@ import type { UpdateFaqArticleRequest, } from '@mento/shared'; import { - UiBadge, - UiButton, - UiCard, - UiEmptyState, - UiField, - UiInput, - UiPageHeader, - UiTextarea, -} from '@/components/ui'; -import UiIcon from '@/components/UiIcon.vue'; + Alert as AAlert, + Button as AButton, + Card as ACard, + Checkbox as ACheckbox, + Col as ACol, + Empty as AEmpty, + Flex as AFlex, + Form as AForm, + FormItem as AFormItem, + Input as AInput, + InputNumber as AInputNumber, + Row as ARow, + Space as ASpace, + Spin as ASpin, + Tag as ATag, + Textarea as ATextarea, + Typography, +} from 'ant-design-vue'; +import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue'; + +const { Title: ATypographyTitle, Text: ATypographyText, Paragraph: ATypographyParagraph } = + Typography; const loading = ref(false); const saving = ref(false); @@ -127,204 +139,107 @@ onMounted(load); - - diff --git a/apps/web/src/views/InvoiceFormView.vue b/apps/web/src/views/InvoiceFormView.vue index cf939d1..470455f 100644 --- a/apps/web/src/views/InvoiceFormView.vue +++ b/apps/web/src/views/InvoiceFormView.vue @@ -216,13 +216,6 @@ const pageTitle = computed(() => { return 'افزودن فاکتور'; }); -const pageSubtitle = computed(() => { - if (mobileStep.value === 2) return 'محصولات و خدمات را اضافه کنید'; - if (docMode.value === 'quote') return 'اطلاعات پیش فاکتور را وارد کنید'; - if (docMode.value === 'credit') return 'اطلاعات فاکتور برگشتی را وارد کنید'; - return 'اطلاعات فاکتور را وارد کنید'; -}); - const defaultTitle = computed(() => { if (docMode.value === 'quote') return 'پیش فاکتور'; if (docMode.value === 'credit') return 'فاکتور برگشتی'; @@ -1032,7 +1025,6 @@ onMounted(load);
{{ pageTitle }} - {{ pageSubtitle }}
diff --git a/apps/web/src/views/InvoicesView.vue b/apps/web/src/views/InvoicesView.vue index 67cd985..9ba7808 100644 --- a/apps/web/src/views/InvoicesView.vue +++ b/apps/web/src/views/InvoicesView.vue @@ -556,9 +556,6 @@ defineExpose({ toggleSelect, pageCount, statusIcon }); >
فاکتور ها - - مدیریت فاکتورهای فروش، پیش فاکتور و خدمات -
diff --git a/apps/web/src/views/LeadBoardView.vue b/apps/web/src/views/LeadBoardView.vue index b02c80a..2439109 100644 --- a/apps/web/src/views/LeadBoardView.vue +++ b/apps/web/src/views/LeadBoardView.vue @@ -542,9 +542,6 @@ onMounted(load); مسیر لیدها - - کشیدن کارت برای تغییر وضعیت. از منوی هر ستون می‌توانید نام، ترتیب و حذف را عوض کنید. - diff --git a/apps/web/src/views/LeadDetailView.vue b/apps/web/src/views/LeadDetailView.vue index 79d70ef..5c018c7 100644 --- a/apps/web/src/views/LeadDetailView.vue +++ b/apps/web/src/views/LeadDetailView.vue @@ -505,9 +505,6 @@ onBeforeUnmount(() => narrowMq?.removeEventListener('change', syncNarrow)); {{ lead?.title || 'جزئیات لید' }} - - {{ lead ? `موبایل ${lead.mobile}` : 'در حال بارگذاری…' }} - diff --git a/apps/web/src/views/LeadFormBuilderView.vue b/apps/web/src/views/LeadFormBuilderView.vue index 733aad0..f5ec17c 100644 --- a/apps/web/src/views/LeadFormBuilderView.vue +++ b/apps/web/src/views/LeadFormBuilderView.vue @@ -1,10 +1,8 @@