Files
portal/app/resources/js/stores/leadStatuses.ts
T

58 lines
2.0 KiB
TypeScript
Raw Normal View History

import { computed, ref } from 'vue';
import { defineStore } from 'pinia';
import { LEAD_STATUSES, type LeadStatus } from '../composables/leadStatuses';
import { type ApiLeadStatus, listLeadStatuses } from '../api/leadStatuses';
/**
* Pinia store для статусов воронки. Заменяет static-снапшот `LEAD_STATUSES`
* (composables/leadStatuses.ts) на live-данные из API при наличии auth.
*
* Initial state — snapshot (UI работает сразу без fetch'а). `load()` вызывается
* из DealsView/KanbanView на mount и replace'ит snapshot реальными данными.
* На fail — снапшот остаётся.
*/
export const useLeadStatusesStore = defineStore('leadStatuses', () => {
const statuses = ref<LeadStatus[]>([...LEAD_STATUSES]);
const loading = ref(false);
const fetchError = ref(false);
const loaded = ref(false);
const bySlug = computed(() => {
const map = new Map<string, LeadStatus>();
statuses.value.forEach((s) => map.set(s.slug, s));
return map;
});
function findBySlug(slug: string): LeadStatus | null {
return bySlug.value.get(slug) ?? null;
}
async function load(force = false): Promise<void> {
if (loaded.value && !force) return;
loading.value = true;
fetchError.value = false;
try {
const apiList = await listLeadStatuses();
statuses.value = apiList.map(mapApi);
loaded.value = true;
} catch {
fetchError.value = true;
// Snapshot остаётся как fallback.
} finally {
loading.value = false;
}
}
return { statuses, loading, fetchError, loaded, bySlug, findBySlug, load };
});
function mapApi(api: ApiLeadStatus): LeadStatus {
return {
slug: api.slug,
nameRu: api.name_ru,
isSystem: api.is_system,
sortOrder: api.sort_order,
colorHex: api.color_hex,
};
}