gg
Deploy Iddaai Frontend / build-and-deploy (push) Failing after 34s

This commit is contained in:
2026-05-10 22:59:27 +03:00
parent 6dadc5f613
commit 5c8619b282
161 changed files with 6708 additions and 3435 deletions
+9
View File
@@ -61,6 +61,14 @@ const resetAllUsageLimits = () => {
});
};
const resetUserUsageLimits = (userId: string) => {
return apiRequest<ApiResponse<null>>({
url: `/admin/usage-limits/reset/${userId}`,
client: "admin",
method: "post",
});
};
// Users
const getAllUsers = (params?: AdminPaginationParams) => {
return apiRequest<ApiResponse<PaginatedData<AdminUserDto>>>({
@@ -119,6 +127,7 @@ export const adminService = {
updateSetting,
getAllUsageLimits,
resetAllUsageLimits,
resetUserUsageLimits,
getAllUsers,
getUserById,
deleteUser,
+2 -1
View File
@@ -49,6 +49,7 @@ export interface AdminUserDto {
role?: string;
isActive: boolean;
subscription?: string;
subscriptionStatus?: string;
createdAt: string;
[key: string]: unknown;
}
@@ -58,7 +59,7 @@ export interface UpdateUserRoleDto {
}
export interface UpdateUserSubscriptionDto {
subscription: string;
plan: string;
}
// ========================
+12
View File
@@ -66,6 +66,18 @@ export const useResetAllUsageLimits = () => {
});
};
export const useResetUserUsageLimits = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (userId: string) => adminService.resetUserUsageLimits(userId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: AdminQueryKeys.usageLimits() });
queryClient.invalidateQueries({ queryKey: AdminQueryKeys.users() });
},
});
};
// Users
export const useAdminUsers = (
params?: AdminPaginationParams,
+5 -1
View File
@@ -1,6 +1,10 @@
import { apiRequest } from "@/lib/api/api-service";
import { ApiResponse } from "@/types/api-response";
import type { AnalyzeMatchesDto, AnalysisResultDto, AnalysisHistoryDto } from "./types";
import type {
AnalyzeMatchesDto,
AnalysisResultDto,
AnalysisHistoryDto,
} from "./types";
/**
* Analysis Service
+12 -5
View File
@@ -1,5 +1,5 @@
import { clientMap } from '@/lib/api/client-map';
import { Method } from 'axios';
import { clientMap } from "@/lib/api/client-map";
import { Method } from "axios";
interface ApiRequestOptions {
url: string;
@@ -9,14 +9,21 @@ interface ApiRequestOptions {
params?: Record<string, string | number | boolean | undefined> | object;
}
export async function apiRequest<T = unknown>(options: ApiRequestOptions): Promise<T> {
const { url, client, method = 'get', data, params } = options;
export async function apiRequest<T = unknown>(
options: ApiRequestOptions,
): Promise<T> {
const { url, client, method = "get", data, params } = options;
const clientInstance = clientMap[client];
if (!url || !clientInstance) {
throw new Error(`Invalid API request: ${client} - ${url}`);
}
const response = await clientInstance.request<T>({ method, url, data, params });
const response = await clientInstance.request<T>({
method,
url,
data,
params,
});
return response.data;
}
+1 -6
View File
@@ -1,11 +1,6 @@
import { apiRequest } from "@/lib/api/api-service";
import { ApiResponse } from "@/types/api-response";
import {
LoginDto,
AuthResponse,
RegisterDto,
RefreshTokenDto,
} from "./types";
import { LoginDto, AuthResponse, RegisterDto, RefreshTokenDto } from "./types";
const login = (data: LoginDto) => {
return apiRequest<ApiResponse<AuthResponse>>({
+3 -3
View File
@@ -1,6 +1,6 @@
import baseUrl from '@/config/base-url';
import { createApiClient } from '@/lib/api/create-api-client';
import { AxiosInstance } from 'axios';
import baseUrl from "@/config/base-url";
import { createApiClient } from "@/lib/api/create-api-client";
import { AxiosInstance } from "axios";
export const clientMap: Record<keyof typeof baseUrl, AxiosInstance> = {
auth: createApiClient(baseUrl.auth!),
-1
View File
@@ -90,4 +90,3 @@ export const couponsService = {
suggestCoupon,
generateFrequencyCoupon,
};
-1
View File
@@ -63,4 +63,3 @@ export const useGenerateFrequencyCoupon = () => {
couponsService.generateFrequencyCoupon(dto),
});
};
+11 -5
View File
@@ -171,10 +171,10 @@ export interface OddsBandEntryDto {
odd_rate?: number;
even_rate?: number;
"1x_rate"?: number;
"x2_rate"?: number;
x2_rate?: number;
"12_rate"?: number;
"1x_sample"?: number;
"x2_sample"?: number;
x2_sample?: number;
"12_sample"?: number;
sample: number;
[key: string]: unknown;
@@ -208,9 +208,15 @@ export interface TripleValueEntryDto {
}
export type HtftComboKey =
| "11" | "1x" | "12"
| "x1" | "xx" | "x2"
| "21" | "2x" | "22";
| "11"
| "1x"
| "12"
| "x1"
| "xx"
| "x2"
| "21"
| "2x"
| "22";
export interface V27EngineDto {
version: string;
+1 -1
View File
@@ -17,7 +17,7 @@ export const usePrediction = (matchId: string) => {
return useQuery({
queryKey: PredictionsQueryKeys.detail(matchId),
queryFn: () => predictionsService.getPrediction(matchId),
enabled: !!matchId,
enabled: false, // DO NOT auto-fetch since it consumes limits
});
};
+4 -2
View File
@@ -55,13 +55,15 @@ export const useSyncBulletin = () => {
export const useGenerateColumns = () => {
return useMutation({
mutationFn: (dto: GenerateColumnsDto) => sporTotoService.generateColumns(dto),
mutationFn: (dto: GenerateColumnsDto) =>
sporTotoService.generateColumns(dto),
});
};
export const useEvaluateColumns = () => {
return useMutation({
mutationFn: (dto: EvaluateColumnsDto) => sporTotoService.evaluateColumns(dto),
mutationFn: (dto: EvaluateColumnsDto) =>
sporTotoService.evaluateColumns(dto),
});
};
+22
View File
@@ -0,0 +1,22 @@
export { subscriptionService } from "./service";
export {
useSubscriptionPlans,
useMySubscription,
useCheckoutConfig,
useCancelSubscription,
SubscriptionQueryKeys,
} from "./use-hooks";
export type {
PlanType,
BillingInterval,
PlanInfo,
SubscriptionDto,
UsageLimitDto,
CheckoutConfigDto,
CreateCheckoutRequest,
CancelSubscriptionRequest,
} from "./types";
export {
PlanType as PlanTypeEnum,
BillingInterval as BillingIntervalEnum,
} from "./types";
+59
View File
@@ -0,0 +1,59 @@
import { apiRequest } from "@/lib/api/api-service";
import type { ApiResponse } from "@/types/api-response";
import type {
PlanInfo,
SubscriptionDto,
CheckoutConfigDto,
CreateCheckoutRequest,
CancelSubscriptionRequest,
} from "./types";
/**
* Subscriptions Service
* Backend: /api/subscriptions/*
*/
const getPlans = (): Promise<ApiResponse<readonly PlanInfo[]>> => {
return apiRequest<ApiResponse<readonly PlanInfo[]>>({
url: "/subscriptions/plans",
client: "core",
method: "get",
});
};
const getMySubscription = (): Promise<ApiResponse<SubscriptionDto | null>> => {
return apiRequest<ApiResponse<SubscriptionDto | null>>({
url: "/subscriptions/me",
client: "core",
method: "get",
});
};
const getCheckoutConfig = (
dto: CreateCheckoutRequest,
): Promise<ApiResponse<CheckoutConfigDto>> => {
return apiRequest<ApiResponse<CheckoutConfigDto>>({
url: "/subscriptions/checkout",
client: "core",
method: "post",
data: dto,
});
};
const cancelSubscription = (
dto: CancelSubscriptionRequest,
): Promise<ApiResponse<null>> => {
return apiRequest<ApiResponse<null>>({
url: "/subscriptions/cancel",
client: "core",
method: "post",
data: dto,
});
};
export const subscriptionService = {
getPlans,
getMySubscription,
getCheckoutConfig,
cancelSubscription,
};
+79
View File
@@ -0,0 +1,79 @@
// ========================
// Enums — mirrors backend PlanType / BillingIntervalType
// ========================
export enum PlanType {
FREE = "free",
PLUS = "plus",
PREMIUM = "premium",
}
export enum BillingInterval {
MONTHLY = "monthly",
YEARLY = "yearly",
}
// ========================
// Response DTOs
// ========================
export interface UsageLimitDto {
analysisCount: number;
couponCount: number;
maxAnalyses: number;
maxCoupons: number;
}
export interface SubscriptionDto {
id: string;
plan: PlanType;
billingInterval: BillingInterval | null;
currentPeriodStart: string | null;
currentPeriodEnd: string | null;
cancelledAt: string | null;
cancelEffectiveDate: string | null;
paddlePriceId: string | null;
createdAt: string;
updatedAt: string;
}
export interface PlanLimits {
maxAnalyses: number;
maxCoupons: number;
}
export interface PlanInfo {
id: PlanType;
name: string;
description: string;
monthlyPrice: number;
yearlyPrice: number;
currency: string;
features: string[];
limits: PlanLimits;
highlighted: boolean;
}
// ========================
// Request DTOs
// ========================
export interface CreateCheckoutRequest {
plan: PlanType;
billingInterval: BillingInterval;
}
export interface CancelSubscriptionRequest {
reason?: string;
}
// ========================
// Checkout Config Response
// ========================
export interface CheckoutConfigDto {
priceId: string;
clientToken: string;
environment: string;
userId: string;
}
+58
View File
@@ -0,0 +1,58 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { subscriptionService } from "./service";
import type { CreateCheckoutRequest, CancelSubscriptionRequest } from "./types";
export const SubscriptionQueryKeys = {
all: ["subscriptions"] as const,
plans: () => [...SubscriptionQueryKeys.all, "plans"] as const,
me: () => [...SubscriptionQueryKeys.all, "me"] as const,
};
/**
* Fetch all available plans (public, no auth required)
*/
export const useSubscriptionPlans = () => {
return useQuery({
queryKey: SubscriptionQueryKeys.plans(),
queryFn: () => subscriptionService.getPlans(),
staleTime: 1000 * 60 * 30, // Plans rarely change — 30 min cache
});
};
/**
* Fetch current user subscription status
*/
export const useMySubscription = (enabled = true) => {
return useQuery({
queryKey: SubscriptionQueryKeys.me(),
queryFn: () => subscriptionService.getMySubscription(),
enabled,
});
};
/**
* Get Paddle checkout config for a plan
*/
export const useCheckoutConfig = () => {
return useMutation({
mutationFn: (dto: CreateCheckoutRequest) =>
subscriptionService.getCheckoutConfig(dto),
});
};
/**
* Cancel the current subscription
*/
export const useCancelSubscription = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (dto: CancelSubscriptionRequest) =>
subscriptionService.cancelSubscription(dto),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: SubscriptionQueryKeys.me(),
});
},
});
};
+9
View File
@@ -16,6 +16,14 @@ export interface ChangePasswordDto {
newPassword: string;
}
export interface UsageLimitDto {
analysisCount: number;
couponCount: number;
maxAnalyses: number;
maxCoupons: number;
lastResetDate: string;
}
export interface UserResponseDto {
id: string;
email: string;
@@ -25,6 +33,7 @@ export interface UserResponseDto {
isActive: boolean;
createdAt: string;
updatedAt: string;
usageLimit?: UsageLimitDto;
}
const getMe = () => {
+9 -1
View File
@@ -1,4 +1,4 @@
import { useMutation } from "@tanstack/react-query";
import { useMutation, useQuery } from "@tanstack/react-query";
import { usersService } from "./service";
import type { UpdateProfileDto, ChangePasswordDto } from "./service";
@@ -7,6 +7,14 @@ export const UsersQueryKeys = {
me: () => [...UsersQueryKeys.all, "me"] as const,
};
export const useGetMe = (enabled: boolean = true) => {
return useQuery({
queryKey: UsersQueryKeys.me(),
queryFn: () => usersService.getMe(),
enabled,
});
};
export const useUpdateProfile = () => {
return useMutation({
mutationFn: (dto: UpdateProfileDto) => usersService.updateMe(dto),