Files
CRM_Montzar/apps/web/src/components/pipeline/PipelineToolbar.vue
T

199 lines
4.2 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import UiFilterBar from '@/components/ui/UiFilterBar.vue';
import UiIcon from '@/components/UiIcon.vue';
import UiSegmented from '@/components/ui/UiSegmented.vue';
import UiSelect from '@/components/ui/UiSelect.vue';
export type PipelineViewMode = 'kanban' | 'list';
withDefaults(
defineProps<{
pipelineName: string;
pipelineOptions: Array<{ id: string; name: string }>;
canCreate?: boolean;
2026-09-15 19:13:30 +03:30
icon?: 'briefcase' | 'grid';
createOptionLabel?: string;
showAmountFilters?: boolean;
}>(),
2026-09-15 19:13:30 +03:30
{
canCreate: false,
icon: 'briefcase',
createOptionLabel: 'مسیر فروش جدید…',
2026-09-15 19:13:30 +03:30
showAmountFilters: true,
},
);
const emit = defineEmits<{
create: [];
}>();
const viewMode = defineModel<PipelineViewMode>('viewMode', { required: true });
const search = defineModel<string>('search', { default: '' });
const selectedPipelineId = defineModel<string>('selectedPipelineId', { required: true });
const amountMin = defineModel<string>('amountMin', { default: '' });
const amountMax = defineModel<string>('amountMax', { default: '' });
const viewOptions = [
{ value: 'kanban' as const, label: 'نمای ستونی', icon: 'grid' as const },
{ value: 'list' as const, label: 'نمای لیست', icon: 'list' as const },
];
function onPick(value: string) {
if (value === '__create__') {
emit('create');
return;
}
selectedPipelineId.value = value;
}
</script>
<template>
<header class="toolbar" dir="rtl">
<div class="title-block">
<span class="brand-ico" aria-hidden="true">
2026-09-15 19:13:30 +03:30
<UiIcon :name="icon" :size="18" />
</span>
<h1>{{ pipelineName }}</h1>
</div>
<UiSegmented v-model="viewMode" :options="viewOptions">
<template #icon="{ option }">
<UiIcon :name="option.icon ?? 'grid'" :size="16" />
</template>
</UiSegmented>
2026-09-15 19:13:30 +03:30
<UiSelect
class="pipeline-pick"
:model-value="selectedPipelineId"
@update:model-value="onPick"
>
<option
v-for="opt in pipelineOptions"
:key="opt.id"
:value="opt.id"
>
{{ opt.name }}
</option>
2026-09-15 19:13:30 +03:30
<option v-if="canCreate" value="__create__">{{ createOptionLabel }}</option>
</UiSelect>
2026-09-15 19:13:30 +03:30
<div class="excel-actions">
<slot name="excel" />
</div>
<UiFilterBar
v-model:search="search"
class="filters"
:show-filters="showAmountFilters"
>
<template v-if="showAmountFilters">
<label class="field">
<span>حداقل مبلغ</span>
<input v-model="amountMin" type="number" min="0" placeholder="۰" />
</label>
<label class="field">
<span>حداکثر مبلغ</span>
<input v-model="amountMax" type="number" min="0" placeholder="—" />
</label>
</template>
</UiFilterBar>
</header>
</template>
<style scoped>
.toolbar {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 0.55rem;
margin-bottom: 0.85rem;
min-width: 0;
width: 100%;
}
.title-block {
display: flex;
align-items: center;
gap: 0.55rem;
flex: 0 0 auto;
min-width: 0;
}
.brand-ico {
width: var(--control-h);
height: var(--control-h);
border-radius: var(--radius);
display: grid;
place-items: center;
background: var(--accent-deep);
color: #fff;
flex: 0 0 auto;
}
h1 {
margin: 0;
font-size: 1.15rem;
font-weight: 600;
color: var(--ink);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.pipeline-pick {
flex: 0 1 13rem;
min-width: 8.5rem;
}
2026-09-15 19:13:30 +03:30
.excel-actions {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
flex: 0 0 auto;
}
.excel-actions :deep(.btn-ghost-sm) {
border: 1px solid #e2e8f0;
background: #fff;
color: #0f766e;
border-radius: 10px;
padding: 0.45rem 0.75rem;
font-weight: 700;
font-size: 0.85rem;
cursor: pointer;
font: inherit;
}
.filters {
flex: 1 1 12rem;
min-width: 0;
}
.field {
display: grid;
gap: 0.3rem;
}
.field span {
font-size: 0.8rem;
color: var(--muted);
font-weight: 700;
}
@container pipeline (max-width: 720px) {
.toolbar {
flex-wrap: wrap;
}
.title-block {
width: 100%;
}
.pipeline-pick,
2026-09-15 19:13:30 +03:30
.excel-actions,
.filters {
flex: 1 1 100%;
}
}
</style>