feat(ui): standardize design system, modals, sidebar, and form inputs

- Implement unified design system tokens, 10px radius, and IRANSans typography
- Modernize sidebar with Gemini-style collapsed expander, square 1:1 items, and elevated profile flyout
- Standardize all modal dialogs with 16px padding, fixed header/footer, 16px medium title, and sticky bottom actions
- Refactor DealFormView and CustomFieldsSection with standard UiField, UiInput, and UiSelect components
- Add draggable bottom-left QuickCreate floating action button
This commit is contained in:
s.esmaeili
2026-09-12 01:33:07 +03:30
parent 9a991fc1f1
commit 1ae87c97b7
87 changed files with 7844 additions and 5718 deletions
+47 -58
View File
@@ -2,6 +2,7 @@
import { onMounted, ref, watch } from 'vue';
import { api } from '@/lib/api';
import JalaliDateInput from '@/components/JalaliDateInput.vue';
import { UiField, UiInput, UiSelect } from '@/components/ui';
import type {
CustomFieldDefinitionDto,
CustomFieldEntity,
@@ -58,27 +59,28 @@ onMounted(load);
<template>
<div v-if="loading" class="muted">بارگذاری فیلدهای سفارشی…</div>
<div v-else-if="fields.length" class="custom-block">
<h3>فیلدهای سفارشی</h3>
<div class="grid">
<div v-for="field in fields" :key="field.id" class="field">
<label :for="`cf-${field.key}`">
{{ field.label }}
<span v-if="field.required" class="req">*</span>
</label>
<select
<h3 class="custom-title">فیلدهای سفارشی</h3>
<div class="crm-form-grid-2">
<UiField
v-for="field in fields"
:key="field.id"
:label="field.label"
:required="field.required"
:for-id="`cf-${field.key}`"
>
<UiSelect
v-if="field.fieldType === 'select'"
:id="`cf-${field.key}`"
:value="String(displayValue(field.key))"
@change="setValue(field.key, ($event.target as HTMLSelectElement).value)"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event)"
>
<option value="">انتخاب کنید</option>
<option v-for="opt in field.options" :key="opt" :value="opt">
{{ opt }}
</option>
</select>
</UiSelect>
<label v-else-if="field.fieldType === 'boolean'" class="check">
<label v-else-if="field.fieldType === 'boolean'" class="cf-check">
<input
:id="`cf-${field.key}`"
type="checkbox"
@@ -87,20 +89,18 @@ onMounted(load);
setValue(field.key, ($event.target as HTMLInputElement).checked)
"
/>
فعال
<span>فعال</span>
</label>
<input
<UiInput
v-else-if="field.fieldType === 'number'"
:id="`cf-${field.key}`"
type="number"
:value="String(displayValue(field.key))"
@input="
:model-value="props.modelValue[field.key] != null ? Number(props.modelValue[field.key]) : ''"
@update:model-value="
setValue(
field.key,
($event.target as HTMLInputElement).value === ''
? null
: Number(($event.target as HTMLInputElement).value),
$event === '' || $event == null ? null : Number($event),
)
"
/>
@@ -112,66 +112,55 @@ onMounted(load);
@update:model-value="setValue(field.key, $event || null)"
/>
<input
<UiInput
v-else
:id="`cf-${field.key}`"
type="text"
:value="String(displayValue(field.key))"
@input="setValue(field.key, ($event.target as HTMLInputElement).value)"
:model-value="String(displayValue(field.key))"
@update:model-value="setValue(field.key, $event)"
/>
</div>
</UiField>
</div>
</div>
</template>
<style scoped>
.custom-block {
display: grid;
gap: 0.75rem;
padding-top: 0.4rem;
display: flex;
flex-direction: column;
gap: var(--sp-3);
padding-top: var(--sp-4);
border-top: 1px dashed var(--line);
width: 100%;
}
h3 {
.custom-title {
margin: 0;
font-size: 1rem;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.9rem;
font-size: var(--fs-sm);
font-weight: var(--fw-semibold);
color: var(--ink-secondary);
}
.muted {
color: var(--muted);
font-size: var(--fs-xs);
}
.req {
color: #b42318;
}
.check {
display: flex;
.cf-check {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
gap: var(--sp-2);
min-height: var(--control-h-md);
font-size: var(--fs-base);
font-weight: var(--fw-medium);
color: var(--ink);
cursor: pointer;
}
select,
input[type='text'],
input[type='number'] {
border: 1px solid var(--line);
border-radius: 12px;
padding: 0.8rem 0.95rem;
background: #fff;
font: inherit;
width: 100%;
}
@media (max-width: 700px) {
.grid {
grid-template-columns: 1fr;
}
.cf-check input[type='checkbox'] {
width: 18px;
height: 18px;
accent-color: var(--accent);
cursor: pointer;
}
</style>