w-funnel/src/hooks/auth/useAuth.ts
2025-12-01 04:09:26 +03:00

179 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useCallback, useMemo, useState } from "react";
import { useSession } from "../session/useSession";
import { getClientTimezone } from "@/shared/utils/locales";
import { ICreateAuthorizeRequest } from "@/entities/user/types";
import { filterNullKeysOfObject } from "@/shared/utils/filter-object";
import { createAuthorization } from "@/entities/user/actions";
import { setAuthTokenToCookie } from "@/entities/user/serverActions";
import analyticsService, { AnalyticsEvent, AnalyticsPlatform } from "@/services/analytics/analyticsService";
import { metricService } from "@/services/analytics/metricService";
import { ApiError } from "@/shared/api/httpClient";
// TODO
const locale = "en";
interface IUseAuthProps {
funnelId: string;
googleAnalyticsId?: string;
/**
* Дополнительные данные для регистрации пользователя.
* Будут объединены с базовым payload при авторизации.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
registrationData?: Record<string, any>;
}
export const useAuth = ({ funnelId, googleAnalyticsId, registrationData }: IUseAuthProps) => {
const { updateSession } = useSession({ funnelId, googleAnalyticsId });
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [suggestedEmail, setSuggestedEmail] = useState<string | null>(null);
const getAllCookies = useCallback(() => {
// Токены которые не должны передаваться на backend
const EXCLUDED_COOKIES = ["accessToken", "activeSessionId"];
const cookies: Record<string, string> = {};
document.cookie.split(";").forEach((cookie) => {
const [name, value] = cookie.trim().split("=");
if (name && value && !EXCLUDED_COOKIES.includes(name)) {
cookies[name] = decodeURIComponent(value);
}
});
return cookies;
}, []);
const getAuthorizationPayload = useCallback(
(email: string): ICreateAuthorizeRequest => {
const timezone = getClientTimezone();
const basePayload = {
timezone,
locale,
email,
source: funnelId,
sign: true,
signDate: new Date().toISOString(),
feature: "stripe"
};
// Объединяем базовый payload с данными регистрации из воронки
const mergedPayload = registrationData
? { ...basePayload, ...registrationData }
: basePayload;
return filterNullKeysOfObject<ICreateAuthorizeRequest>(mergedPayload);
},
[funnelId, registrationData]
);
const authorization = useCallback(
async (email: string) => {
try {
setIsLoading(true);
setError(null);
// Обновляем сессию с куки перед авторизацией
try {
const cookies = getAllCookies();
await updateSession({ cookies });
console.log(
"Session updated with cookies before authorization:",
cookies
);
} catch (sessionError) {
console.warn("Failed to update session with cookies:", sessionError);
// Продолжаем авторизацию даже если обновление сессии не удалось
}
const payload = getAuthorizationPayload(email);
const { token, userId } = await createAuthorization(payload);
// Track registration events in analytics
// Send EnteredEmail to Yandex Metrika and Google Analytics
analyticsService.trackEvent(
AnalyticsEvent.ENTERED_EMAIL,
[AnalyticsPlatform.YANDEX_METRIKA, AnalyticsPlatform.GOOGLE_ANALYTICS]
);
// Send Lead to Facebook Pixel
analyticsService.trackEvent(
AnalyticsEvent.LEAD,
[AnalyticsPlatform.FACEBOOK]
);
// Set user ID and properties in analytics
if (userId) {
analyticsService.setUserId(userId);
analyticsService.setUserProperties({
email,
source: funnelId,
UserID: userId,
});
// ✅ Отправляем UserID и email в userParams (параметры посетителя)
metricService.setUserID(userId);
metricService.userParams({
UserID: userId,
email,
});
// ✅ Отправляем email и userId в params (параметры визита)
metricService.sendVisitContext({
email,
userId,
});
}
await setAuthTokenToCookie(token);
return token;
} catch (err) {
// Extract error message and suggestion from API error
if (err instanceof ApiError && err.data) {
const errorData = err.data as { errors?: Array<{ msg: string; path: string }> };
const emailError = errorData.errors?.find(e => e.path === 'email');
if (emailError) {
setError(emailError.msg);
// Extract suggested email from "Did you mean user@gmail.com?" message
const suggestionMatch = emailError.msg.match(/Did you mean (.+)\?/);
if (suggestionMatch) {
setSuggestedEmail(suggestionMatch[1]);
} else {
setSuggestedEmail(null);
}
} else {
setError('Email validation failed');
setSuggestedEmail(null);
}
} else {
setError((err as Error).message);
setSuggestedEmail(null);
}
} finally {
setIsLoading(false);
}
},
[getAllCookies, getAuthorizationPayload, updateSession, funnelId]
);
const clearError = useCallback(() => {
setError(null);
setSuggestedEmail(null);
}, []);
return useMemo(
() => ({
authorization,
isLoading,
error,
suggestedEmail,
clearError,
}),
[authorization, isLoading, error, suggestedEmail, clearError]
);
};