refactor: decompose CrmWorkspaceApp.vue into 15 composables
Split the 6000+ line monolithic component into modular composables: - crm-types.ts: shared types and utility functions - useAuth, useContacts, useContactInboxes, useCalendar, useDeals, useDocuments, useFeed, useTimeline, usePilotChat, useCallAudio, usePins, useChangeReview, useCrmRealtime, useWorkspaceRouting CrmWorkspaceApp.vue is now a thin orchestrator (~2500 lines) that wires composables together with glue code, keeping template and styles intact. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
205
frontend/app/composables/useAuth.ts
Normal file
205
frontend/app/composables/useAuth.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useQuery, useMutation } from "@vue/apollo-composable";
|
||||
import { MeQueryDocument, LogoutMutationDocument } from "~~/graphql/generated";
|
||||
|
||||
type TelegramConnectStatus =
|
||||
| "not_connected"
|
||||
| "pending_link"
|
||||
| "pending_business_connection"
|
||||
| "connected"
|
||||
| "disabled"
|
||||
| "no_reply_rights";
|
||||
|
||||
type TelegramConnectionSummary = {
|
||||
businessConnectionId: string;
|
||||
isEnabled: boolean | null;
|
||||
canReply: boolean | null;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export function useAuth() {
|
||||
// -------------------------------------------------------------------------
|
||||
// Auth state
|
||||
// -------------------------------------------------------------------------
|
||||
const authMe = ref<{
|
||||
user: { id: string; phone: string; name: string };
|
||||
team: { id: string; name: string };
|
||||
conversation: { id: string; title: string };
|
||||
} | null>(null);
|
||||
|
||||
const authResolved = ref(false);
|
||||
|
||||
const apolloAuthReady = computed(() => !!authMe.value);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Apollo: Me query
|
||||
// -------------------------------------------------------------------------
|
||||
const { result: meResult, refetch: refetchMe, loading: meLoading } = useQuery(
|
||||
MeQueryDocument,
|
||||
null,
|
||||
{ fetchPolicy: "network-only" },
|
||||
);
|
||||
|
||||
watch(() => meResult.value?.me, (me) => {
|
||||
if (me) authMe.value = me as typeof authMe.value;
|
||||
}, { immediate: true });
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Apollo: Logout mutation
|
||||
// -------------------------------------------------------------------------
|
||||
const { mutate: doLogout } = useMutation(LogoutMutationDocument);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// loadMe / logout
|
||||
// -------------------------------------------------------------------------
|
||||
async function loadMe() {
|
||||
const result = await refetchMe();
|
||||
const me = result?.data?.me;
|
||||
if (me) authMe.value = me as typeof authMe.value;
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await doLogout();
|
||||
authMe.value = null;
|
||||
telegramConnectStatus.value = "not_connected";
|
||||
telegramConnections.value = [];
|
||||
telegramConnectUrl.value = "";
|
||||
if (process.client) {
|
||||
await navigateTo("/login", { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Telegram connect state
|
||||
// -------------------------------------------------------------------------
|
||||
const telegramConnectStatus = ref<TelegramConnectStatus>("not_connected");
|
||||
const telegramConnectStatusLoading = ref(false);
|
||||
const telegramConnectBusy = ref(false);
|
||||
const telegramConnectUrl = ref("");
|
||||
const telegramConnections = ref<TelegramConnectionSummary[]>([]);
|
||||
const telegramConnectNotice = ref("");
|
||||
|
||||
const telegramStatusLabel = computed(() => {
|
||||
if (telegramConnectStatusLoading.value) return "Checking";
|
||||
if (telegramConnectStatus.value === "connected") return "Connected";
|
||||
if (telegramConnectStatus.value === "pending_link") return "Pending link";
|
||||
if (telegramConnectStatus.value === "pending_business_connection") return "Waiting business connect";
|
||||
if (telegramConnectStatus.value === "disabled") return "Disabled";
|
||||
if (telegramConnectStatus.value === "no_reply_rights") return "No reply rights";
|
||||
return "Not connected";
|
||||
});
|
||||
|
||||
const telegramStatusBadgeClass = computed(() => {
|
||||
if (telegramConnectStatus.value === "connected") return "badge-success";
|
||||
if (telegramConnectStatus.value === "pending_link" || telegramConnectStatus.value === "pending_business_connection") return "badge-warning";
|
||||
if (telegramConnectStatus.value === "disabled" || telegramConnectStatus.value === "no_reply_rights") return "badge-error";
|
||||
return "badge-ghost";
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Telegram connect functions
|
||||
// -------------------------------------------------------------------------
|
||||
async function loadTelegramConnectStatus() {
|
||||
if (!authMe.value) {
|
||||
telegramConnectStatus.value = "not_connected";
|
||||
telegramConnections.value = [];
|
||||
telegramConnectUrl.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
telegramConnectStatusLoading.value = true;
|
||||
try {
|
||||
const result = await $fetch<{
|
||||
ok: boolean;
|
||||
status: TelegramConnectStatus;
|
||||
connections?: TelegramConnectionSummary[];
|
||||
}>("/api/omni/telegram/business/connect/status", {
|
||||
method: "GET",
|
||||
});
|
||||
telegramConnectStatus.value = result?.status ?? "not_connected";
|
||||
telegramConnections.value = result?.connections ?? [];
|
||||
} catch {
|
||||
telegramConnectStatus.value = "not_connected";
|
||||
telegramConnections.value = [];
|
||||
} finally {
|
||||
telegramConnectStatusLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function startTelegramBusinessConnect() {
|
||||
if (telegramConnectBusy.value) return;
|
||||
telegramConnectBusy.value = true;
|
||||
try {
|
||||
const result = await $fetch<{
|
||||
ok: boolean;
|
||||
status: TelegramConnectStatus;
|
||||
connectUrl: string;
|
||||
expiresAt: string;
|
||||
}>("/api/omni/telegram/business/connect/start", { method: "POST" });
|
||||
telegramConnectStatus.value = result?.status ?? "pending_link";
|
||||
telegramConnectUrl.value = String(result?.connectUrl ?? "").trim();
|
||||
if (telegramConnectUrl.value && process.client) {
|
||||
window.location.href = telegramConnectUrl.value;
|
||||
}
|
||||
} catch {
|
||||
telegramConnectStatus.value = "not_connected";
|
||||
} finally {
|
||||
telegramConnectBusy.value = false;
|
||||
await loadTelegramConnectStatus();
|
||||
}
|
||||
}
|
||||
|
||||
async function completeTelegramBusinessConnectFromToken(token: string) {
|
||||
const t = String(token || "").trim();
|
||||
if (!t) return;
|
||||
|
||||
try {
|
||||
const result = await $fetch<{
|
||||
ok: boolean;
|
||||
status: string;
|
||||
businessConnectionId?: string;
|
||||
}>("/api/omni/telegram/business/connect/complete", {
|
||||
method: "POST",
|
||||
body: { token: t },
|
||||
});
|
||||
|
||||
if (result?.ok) {
|
||||
telegramConnectStatus.value = "connected";
|
||||
telegramConnectNotice.value = "Telegram успешно привязан.";
|
||||
await loadTelegramConnectStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (result?.status === "awaiting_telegram_start") {
|
||||
telegramConnectNotice.value = "Сначала нажмите Start в Telegram, затем нажмите кнопку в боте снова.";
|
||||
} else if (result?.status === "invalid_or_expired_token") {
|
||||
telegramConnectNotice.value = "Ссылка привязки истекла. Нажмите Connect в CRM заново.";
|
||||
} else {
|
||||
telegramConnectNotice.value = "Не удалось завершить привязку. Запустите Connect заново.";
|
||||
}
|
||||
} catch {
|
||||
telegramConnectNotice.value = "Ошибка завершения привязки. Попробуйте снова.";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
authMe,
|
||||
authResolved,
|
||||
apolloAuthReady,
|
||||
meLoading,
|
||||
loadMe,
|
||||
logout,
|
||||
// telegram
|
||||
telegramConnectStatus,
|
||||
telegramConnectStatusLoading,
|
||||
telegramConnectBusy,
|
||||
telegramConnectUrl,
|
||||
telegramConnections,
|
||||
telegramConnectNotice,
|
||||
telegramStatusLabel,
|
||||
telegramStatusBadgeClass,
|
||||
loadTelegramConnectStatus,
|
||||
startTelegramBusinessConnect,
|
||||
completeTelegramBusinessConnectFromToken,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user