connected api with profile, order

This commit is contained in:
Jelaletdin12
2025-11-15 16:14:01 +05:00
parent 21b9e88c5c
commit f867896817
70 changed files with 2370 additions and 2317 deletions

View File

@@ -1,58 +0,0 @@
/**
* API Endpoints Configuration
* Centralized mapping of all API endpoints
*/
export const API_ENDPOINTS = {
// Products
products: "/api/v1/products",
productDetail: (id: string | number) => `/api/v1/products/${id}`,
productsByCategory: (categoryId: string | number) => `/api/v1/categories/${categoryId}/products`,
productsByBrand: (brandId: string | number) => `/api/v1/brands/${brandId}/products`,
// Categories
categories: "/api/v1/categories",
categoryDetail: (id: string | number) => `/api/v1/categories/${id}`,
// Search & Filters
search: "/api/v1/search",
filters: "/api/v1/filters",
// Cart
cart: "/api/v1/carts",
cartItems: "/api/v1/carts",
cartItem: (itemId: string | number) => `/api/v1/carts/${itemId}`,
// Favorites
favorites: "/api/v1/favorites",
favoriteDetail: (productId: string | number) => `/api/v1/favorites/${productId}`,
// Orders
orders: "/api/v1/orders",
orderDetail: (id: string | number) => `/api/v1/orders/${id}`,
cancelOrder: (id: string | number) => `/api/v1/orders/${id}/cancel`,
// Regions & Addresses
regions: "/api/v1/regions",
addresses: "/api/v1/addresses",
// Payment & Shipping
paymentTypes: "/api/v1/order-payments",
shippingMethods: "/api/v1/shipping-methods",
// Profile
profile: "/api/v1/profile",
profileMe: "/api/v1/me",
// Auth
guestToken: "/api/v1/auth/guest-token",
verifyCode: "/api/v1/auth/verify-code",
// Media
banners: "/api/v1/media/banners",
// Forms
newsletter: "/api/v1/forms/newsletter-subscription",
contactUs: "/api/v1/forms/contact-us",
openStore: "/api/v1/forms/open-store",
} as const

View File

@@ -1,14 +1,14 @@
export * from "./useProducts"
export * from "./useCategories"
export * from "./useCart"
export * from "./useFavorites"
export * from "./useOrders"
export * from "../../features/products/hooks/useProducts"
export * from "../../features/category/hooks/useCategories"
export * from "../../features/cart/hooks/useCart"
export * from "../../features/favorites/hooks/useFavorites"
export * from "../../features/orders/hooks/useOrders"
export * from "./useSearch"
export * from "./useUserProfile"
export * from "../../features/profile/hooks/useUserProfile"
export * from "./useOpenStore"
export * from "./useRegions"
export * from "./useAddresses"
export * from "./usePaymentTypes"
export * from "../../features/cart/hooks/useAddresses"
export * from "../../features/cart/hooks/usePaymentTypes"
@@ -16,8 +16,8 @@ export * from "./usePaymentTypes"
export * from "./useMedia"
export * from "./useCollections"
export * from "../../features/home/hooks/useMedia"
export * from "../../features/home/hooks/useCollections"
// Export types
export type { Product, Category, Cart, CartItem, Order, Favorite, Banner } from "@/lib/types/api"

View File

@@ -1,192 +1,160 @@
import { useMutation, useQuery } from "@tanstack/react-query"
import { apiClient, setAuthToken, clearAuthToken, setGuestToken } from "@/lib/api"
import { queryClient } from "@/lib/queryClient"
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useState, useEffect } from "react";
import { apiClient, setAuthToken, clearAuthToken, setGuestToken } from "@/lib/api";
// ==================== TYPES ====================
interface LoginCredentials {
phone_number: string
password?: string
phone_number: string;
password?: string;
}
interface RegisterData {
phone_number: string
name?: string
email?: string
phone_number: string;
name?: string;
email?: string;
}
interface VerifyTokenData {
phone_number: string
code: string
phone_number: string;
code: string;
}
interface AuthResponse {
token?: string
data?: string
user?: any
token?: string;
data?: string;
user?: {
id: string;
phone_number: string;
name?: string;
email?: string;
};
}
/**
* Guest Token alma (RTK mantığı)
*/
// ==================== AUTH STATUS ====================
const getTokenFromCookie = (name: string): string | null => {
if (typeof document === "undefined") return null;
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(";").shift() || null;
return null;
};
export function useAuthStatus() {
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const authToken = getTokenFromCookie("authToken");
setIsAuthenticated(!!authToken);
setIsLoading(false);
}, []);
return {
isAuthenticated,
isLoading,
};
}
// ==================== GUEST TOKEN ====================
export function useGetGuestToken() {
return useMutation({
mutationFn: async (): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>("/auth/guest-token", {}, {
headers: {
"Content-Type": "application/json",
},
})
return response.data
const response = await apiClient.post<AuthResponse>("/auth/guest-token", {});
return response.data;
},
onSuccess: (data) => {
const token = data?.token || data?.data
const token = data?.token || data?.data;
if (token) {
setGuestToken(token)
setGuestToken(token);
}
},
onError: (error) => {
console.error("Error fetching guest token:", error)
console.error("Guest token hatası:", error);
},
})
});
}
/**
* Login mutation (RTK mantığı)
*/
// ==================== LOGIN ====================
export function useLogin() {
return useMutation({
mutationFn: async (credentials: LoginCredentials): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>("/auth/login", credentials)
return response.data
},
onSuccess: (data) => {
const token = data?.token || data?.data
if (token) {
setAuthToken(token)
apiClient.setAuthToken(token)
// Tüm cache'i temizle ve yeniden fetch et
queryClient.invalidateQueries()
}
const response = await apiClient.post<AuthResponse>("/auth/login", credentials);
return response.data;
},
onError: (error) => {
console.error("Login error:", error)
console.error("Login hatası:", error);
},
})
});
}
/**
* Register mutation (RTK mantığı)
*/
// ==================== REGISTER ====================
export function useRegister() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (userData: RegisterData): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>("/auth/register", userData)
return response.data
const response = await apiClient.post<AuthResponse>("/auth/register", userData);
return response.data;
},
onSuccess: (data) => {
const token = data?.token || data?.data
const token = data?.token || data?.data;
if (token) {
setAuthToken(token)
apiClient.setAuthToken(token)
// Tüm cache'i temizle
queryClient.invalidateQueries()
setAuthToken(token);
queryClient.invalidateQueries({ queryKey: ["auth-status"] });
}
},
onError: (error) => {
console.error("Register error:", error)
console.error("Register hatası:", error);
},
})
});
}
/**
* Token doğrulama (RTK mantığı)
*/
// ==================== VERIFY TOKEN ====================
export function useVerifyToken() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (verifyData: VerifyTokenData): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>(
"/auth/verify",
verifyData,
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
}
)
return response.data
const response = await apiClient.post<AuthResponse>("/auth/verify", verifyData);
return response.data;
},
onSuccess: (data) => {
const token = data?.data || data?.token
const token = data?.data || data?.token;
if (token) {
setAuthToken(token)
apiClient.setAuthToken(token)
// Tüm cache'i temizle
queryClient.invalidateQueries()
setAuthToken(token);
queryClient.invalidateQueries({ queryKey: ["auth-status"] });
}
},
onError: (error) => {
console.error("Error verifying token:", error)
console.error("Verify hatası:", error);
},
})
});
}
/**
* Logout işlemi
*/
// ==================== LOGOUT ====================
export function useLogout() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async () => {
// Backend'e logout isteği gönder (eğer endpoint varsa)
mutationFn: async (): Promise<void> => {
try {
await apiClient.post("/auth/logout")
await apiClient.post("/auth/logout");
} catch (error) {
// Logout endpoint yoksa da devam et
console.warn("Logout endpoint not available")
console.warn("Logout endpoint çalışmadı:", error);
}
},
onSuccess: () => {
clearAuthToken()
apiClient.clearAuthToken()
// Tüm cache'i temizle
queryClient.clear()
// Login sayfasına yönlendir
clearAuthToken();
queryClient.clear();
if (typeof window !== "undefined") {
window.location.href = "/login"
window.location.href = "/login";
}
},
})
}
/**
* Kullanıcı bilgilerini getir
*/
export function useUser(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["user", "me"],
queryFn: async () => {
const response = await apiClient.get("/auth/me")
return response.data
onError: (error) => {
console.error("Logout hatası:", error);
clearAuthToken();
queryClient.clear();
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 5, // 5 dakika
retry: false,
})
}
/**
* Authentication durumunu kontrol et
*/
export function useAuthStatus() {
const { data: user, isLoading, error } = useUser({ enabled: true })
return {
isAuthenticated: !!user && !error,
isLoading,
user,
}
});
}

View File

@@ -1,270 +0,0 @@
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Cart, CartItem } from "@/lib/types/api"
interface CartResponse {
message: string
data: CartItem[]
errorDetails?: string
}
// Transform response to handle HTML/malformed responses
function transformCartResponse(response: any): CartResponse {
if (
typeof response === "string" &&
(response.trim().startsWith("<!DOCTYPE") || response.trim().startsWith("<html"))
) {
console.error("Received HTML response instead of JSON:", response.substring(0, 100))
return {
message: "error",
data: [],
errorDetails: "Server returned HTML instead of JSON. The server might be down or experiencing issues.",
}
}
if (typeof response === "object") {
if (response.data) {
return response
}
return { message: "success", data: [] }
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response)
return parsed
} catch (error) {
console.error("Failed to parse response:", error)
return { message: "error", data: [] }
}
}
return { message: "unknown", data: [] }
}
export function useCart(options?: Partial<UseQueryOptions<CartResponse>>) {
return useQuery({
queryKey: ["cart"],
queryFn: async () => {
const response = await apiClient.get("/carts")
return transformCartResponse(response.data)
},
refetchInterval: 5000, // Poll every 5 seconds like RTK
refetchOnMount: true,
refetchOnWindowFocus: false,
refetchOnReconnect: true,
staleTime: 0,
retry: 1,
...options,
})
}
export function useAddToCart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ productId, quantity = 1 }: { productId: number; quantity?: number }) => {
const params = new URLSearchParams({
product_id: String(productId),
product_quantity: String(quantity),
})
const response = await apiClient.post("/carts", params.toString(), {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
if (typeof response.data === "object" && response.data.data) {
return response.data
}
if (typeof response.data === "string") {
try {
const parsed = JSON.parse(response.data)
return parsed
} catch (error) {
console.error("Failed to parse add to cart response:", error)
return { message: "success", data: "Added to cart" }
}
}
return { message: "success", data: "Added to cart" }
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] })
},
onError: (error: any) => {
console.error("Add to cart error:", error.response?.data?.message || error.message)
},
})
}
export function useRemoveFromCart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (productId: number) => {
const params = new URLSearchParams({ product_id: String(productId) })
const response = await apiClient.patch("/carts", params.toString(), {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
if (typeof response.data === "object" && response.data.data) {
return response.data.data
}
if (typeof response.data === "string") {
try {
const parsed = JSON.parse(response.data)
return parsed.data || []
} catch (error) {
console.error("Failed to parse cart response:", error)
return []
}
}
return []
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] })
},
onError: (error: any) => {
console.error("Remove from cart error:", error.response?.data?.message || error.message)
},
})
}
export function useCleanCart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async () => {
const response = await apiClient.delete("/carts", {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
if (typeof response.data === "object" && response.data.data) {
return response.data.data
}
if (typeof response.data === "string") {
try {
const parsed = JSON.parse(response.data)
return parsed.data || []
} catch (error) {
console.error("Failed to parse cart response:", error)
return []
}
}
return []
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] })
},
})
}
export function useUpdateCartItemQuantity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ productId, quantity }: { productId: number; quantity: number }) => {
const params = new URLSearchParams({
product_id: String(productId),
product_quantity: String(quantity),
})
const response = await apiClient.post("/carts", params.toString(), {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
if (typeof response.data === "object" && response.data.data) {
return response.data
}
if (typeof response.data === "string") {
try {
const parsed = JSON.parse(response.data)
return parsed
} catch (error) {
console.error("Failed to parse update cart response:", error)
return { message: "success", data: "Updated cart" }
}
}
return { message: "success", data: "Updated cart" }
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] })
},
onError: (error: any) => {
console.error("API update failed:", error.response?.data?.message || error.message)
},
})
}
export function useCreateOrder() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (payload: {
customer_name?: string
customer_phone?: string
customer_address: string
shipping_method: string
payment_type_id: number
delivery_time?: string
delivery_at?: string
region: string
note?: string
}) => {
const response = await apiClient.post("/api/v1/orders", payload)
return response.data
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart"] })
queryClient.invalidateQueries({ queryKey: ["orders"] })
},
onError: (error: any) => {
console.error("Create order error:", error.response?.data?.message || error.message)
},
})
}
import type { Region } from "@/lib/types/api"
export function useRegions() {
return useQuery({
queryKey: ["regions"],
queryFn: async () => {
const response = await apiClient.get<Region[]>("/api/v1/provinces")
return response.data
},
staleTime: 1000 * 60 * 60, // 1 hour
})
}
import type { Address } from "@/lib/types/api"
export function useAddresses() {
return useQuery({
queryKey: ["addresses"],
queryFn: async () => {
const response = await apiClient.get<Address[]>("/api/v1/addresses")
return response.data
},
staleTime: 1000 * 60 * 30, // 30 minutes
})
}

View File

@@ -1,161 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Category, Product, PaginatedResponse } from "@/lib/types/api"
// Get all categories as tree
export function useCategories(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["categories"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Category>>("/categories", {
params: { type: "tree" },
})
return response.data.data || response.data
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 30, // 30 minutes
})
}
// Get single category by ID
export function useCategory(id: number | string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["category", id],
queryFn: async () => {
const response = await apiClient.get<Category>(`/categories/${id}`)
return response.data
},
enabled: options?.enabled !== false && !!id,
staleTime: 1000 * 60 * 15,
})
}
// Get products for a single category with pagination
export function useCategoryProducts(
categoryId: number | string,
options?: {
enabled?: boolean
page?: number
limit?: number
}
) {
return useQuery({
queryKey: ["category", categoryId, "products", options?.page, options?.limit],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/categories/${categoryId}/products`,
{
params: {
page: options?.page || 1,
limit: options?.limit
},
}
)
return {
data: response.data.data || [],
pagination: response.data.pagination || {}
}
},
enabled: options?.enabled !== false && !!categoryId,
})
}
// Get ALL products from category and its children - NO pagination (for initial load)
export function useAllCategoryProducts(
category: Category | undefined,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["category", category?.id, "all-products"],
queryFn: async () => {
if (!category) return []
const fetchProducts = async (categoryId: number) => {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/categories/${categoryId}/products`
)
return response.data.data || []
}
let allProducts = await fetchProducts(category.id)
if (category.children && category.children.length > 0) {
for (const child of category.children) {
const childProducts = await fetchProducts(child.id)
allProducts = [...allProducts, ...childProducts]
}
}
return allProducts
},
enabled: options?.enabled !== false && !!category,
})
}
// Get products from category and children WITH pagination (mimics RTK getAllCategoryProductsPaginated)
export function useAllCategoryProductsPaginated(
category: Category | undefined,
options?: {
enabled?: boolean
page?: number
limit?: number
}
) {
const page = options?.page || 1
const limit = options?.limit || 6
return useQuery({
queryKey: ["category", category?.id, "paginated-products", page, limit],
queryFn: async () => {
if (!category) {
return {
data: [],
pagination: {
currentPage: page,
hasMorePages: false
}
}
}
const categoryIds = [category.id]
if (category.children && category.children.length > 0) {
category.children.forEach((child) => categoryIds.push(child.id))
}
const perCategoryLimit = Math.ceil(limit / categoryIds.length)
const hasMoreByCategory: Record<number, boolean> = {}
let allPageProducts: Product[] = []
for (const categoryId of categoryIds) {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/categories/${categoryId}/products`,
{
params: {
page,
limit: perCategoryLimit
}
}
)
if (response.data.data) {
allPageProducts = [...allPageProducts, ...response.data.data]
hasMoreByCategory[categoryId] = !!response.data.pagination?.next_page_url
}
}
const hasMorePages = Object.values(hasMoreByCategory).some((hasMore) => hasMore)
return {
data: allPageProducts,
pagination: {
currentPage: page,
hasMorePages
}
}
},
enabled: options?.enabled !== false && !!category,
})
}

View File

@@ -1,108 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Collection, Product, PaginatedResponse } from "@/lib/types/api"
// Get all collections
export function useCollections(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["collections"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Collection>>("/collections")
return response.data.data || response.data
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 30, // 30 minutes
})
}
// Get single collection by ID
export function useCollection(id: number | string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["collection", id],
queryFn: async () => {
const response = await apiClient.get<Collection>(`/collections/${id}`)
return response.data
},
enabled: options?.enabled !== false && !!id,
staleTime: 1000 * 60 * 15,
})
}
// Get collection products (non-paginated)
export function useCollectionProducts(
collectionId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["collection", collectionId, "products"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/collections/${collectionId}/products`
)
const data = response.data.data || []
return {
data,
isEmpty: data.length === 0,
}
},
enabled: options?.enabled !== false && !!collectionId,
})
}
// Check if collection has products (limit=1 for efficiency)
export function useCollectionHasProducts(
collectionId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["collection", collectionId, "has-products"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/collections/${collectionId}/products`,
{
params: { limit: 1 },
}
)
return {
hasProducts: response.data.data && response.data.data.length > 0,
}
},
enabled: options?.enabled !== false && !!collectionId,
staleTime: 1000 * 60 * 5, // 5 minutes
})
}
// Get collection products with pagination
export function useCollectionProductsPaginated(
collectionId: number | string,
options?: {
enabled?: boolean
page?: number
limit?: number
}
) {
const page = options?.page || 1
const limit = options?.limit || 6
return useQuery({
queryKey: ["collection", collectionId, "products-paginated", page, limit],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Product>>(
`/collections/${collectionId}/products`,
{
params: {
page,
limit,
},
}
)
const data = response.data.data || []
return {
data,
pagination: response.data.pagination || {},
isEmpty: data.length === 0,
}
},
enabled: options?.enabled !== false && !!collectionId,
})
}

View File

@@ -1,112 +0,0 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api";
import type { Favorite } from "@/lib/types/api";
// Response tiplerini tanımlayalım
interface FavoritesResponse {
data?: Favorite[];
[key: string]: any;
}
interface FavoriteActionResponse {
data?: string | Favorite[];
[key: string]: any;
}
// Response'u transform eden yardımcı fonksiyon
function transformFavoritesResponse(response: any): Favorite[] {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || [];
} catch (error) {
console.error("Failed to parse favorites response:", error);
return [];
}
}
return [];
}
function transformActionResponse(response: any, defaultValue: string): string {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || defaultValue;
} catch (error) {
if (response.includes("<!doctype html>")) {
return defaultValue;
}
console.error(`Failed to parse favorite response:`, error);
return defaultValue;
}
}
return defaultValue;
}
export function useFavorites() {
return useQuery({
queryKey: ["favorites"],
queryFn: async () => {
const response = await apiClient.get("/favorites");
return transformFavoritesResponse(response.data);
},
staleTime: 1000 * 60 * 5,
retry: 1,
});
}
export function useAddToFavorites() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (productId: number) => {
const formData = new URLSearchParams({
product_id: productId.toString(),
});
const response = await apiClient.post("/favorites", formData, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
return transformActionResponse(response.data, "Added");
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["favorites"] });
},
});
}
export function useRemoveFromFavorites() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (productId: number) => {
const formData = new URLSearchParams({
product_id: productId.toString(),
});
const response = await apiClient.post("/favorites", formData, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
return transformActionResponse(response.data, "Removed");
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["favorites"] });
},
});
}

View File

@@ -1,29 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Carousel, Banner, PaginatedResponse } from "@/lib/types/api"
// Get all carousels
export function useCarousels(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["carousels"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Carousel>>("/media/carousels")
return response.data.data || response.data
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 30, // 30 minutes
})
}
// Get all banners
export function useBanners(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["banners"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Banner>>("/media/banners")
return response.data.data || response.data
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 30, // 30 minutes
})
}

View File

@@ -1,38 +1,38 @@
"use client"
// "use client"
import { useMutation } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import { API_ENDPOINTS } from "@/lib/config/api-endpoints"
// import { useMutation } from "@tanstack/react-query"
// import { apiClient } from "@/lib/api"
// import { API_ENDPOINTS } from "@/lib/config/api-endpoints"
interface OpenStoreData {
firstName: string
lastName: string
email: string
phone: string
patentFile: File
}
// interface OpenStoreData {
// firstName: string
// lastName: string
// email: string
// phone: string
// patentFile: File
// }
interface OpenStoreResponse {
success: boolean
message: string
}
// interface OpenStoreResponse {
// success: boolean
// message: string
// }
export function useOpenStore() {
return useMutation({
mutationFn: async (data: OpenStoreData) => {
const formData = new FormData()
formData.append("first_name", data.firstName)
formData.append("last_name", data.lastName)
formData.append("email", data.email)
formData.append("phone", data.phone)
formData.append("patent_file", data.patentFile)
// export function useOpenStore() {
// return useMutation({
// mutationFn: async (data: OpenStoreData) => {
// const formData = new FormData()
// formData.append("first_name", data.firstName)
// formData.append("last_name", data.lastName)
// formData.append("email", data.email)
// formData.append("phone", data.phone)
// formData.append("patent_file", data.patentFile)
const response = await apiClient.post<OpenStoreResponse>(API_ENDPOINTS.openStore, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
return response.data
},
})
}
// const response = await apiClient.post<OpenStoreResponse>(API_ENDPOINTS.openStore, formData, {
// headers: {
// "Content-Type": "multipart/form-data",
// },
// })
// return response.data
// },
// })
// }

View File

@@ -1,165 +0,0 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api";
import type { Order, PaginatedResponse } from "@/lib/types/api";
// Response tiplerini tanımlayalım
interface OrderResponse {
data?: Order | Order[];
[key: string]: any;
}
interface OrderActionResponse {
data?: string | Order;
[key: string]: any;
}
// Response'u transform eden yardımcı fonksiyonlar
function transformOrdersResponse(response: any): Order[] {
if (typeof response === "object" && response.data) {
return Array.isArray(response.data) ? response.data : [];
}
return [];
}
function transformOrderResponse(response: any): Order | null {
if (typeof response === "object" && response.data) {
return response.data;
}
return null;
}
function transformOrderActionResponse(
response: any,
defaultValue: string
): string | Order {
if (response && response.data) {
return response.data;
}
return defaultValue;
}
function isHtmlResponse(response: any): boolean {
return typeof response === "string" && response.includes("<!doctype html>");
}
// Orders list query
export function useOrders(options?: { page?: number; perPage?: number }) {
return useQuery({
queryKey: ["orders", options?.page],
queryFn: async () => {
const response = await apiClient.get("/orders", {
params: {
page: options?.page || 1,
per_page: options?.perPage || 20,
},
});
return transformOrdersResponse(response.data);
},
staleTime: 1000 * 60 * 5,
retry: 1,
});
}
// Single order query
export function useOrder(id: number | string) {
return useQuery({
queryKey: ["order", id],
queryFn: async () => {
const response = await apiClient.get(`/orders/${id}`);
return transformOrderResponse(response.data);
},
enabled: !!id,
staleTime: 1000 * 60 * 5,
retry: 1,
});
}
// Order times query
export function useOrderTimes() {
return useQuery({
queryKey: ["order-times"],
queryFn: async () => {
const response = await apiClient.get("/order-time");
return transformOrdersResponse(response.data);
},
staleTime: 1000 * 60 * 10,
retry: 1,
});
}
// Order payments query
export function useOrderPayments() {
return useQuery({
queryKey: ["order-payments"],
queryFn: async () => {
const response = await apiClient.get("/order-payments");
return transformOrdersResponse(response.data);
},
staleTime: 1000 * 60 * 10,
retry: 1,
});
}
// Create/Place order mutation
export function useCreateOrder() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (orderData: Record<string, any>) => {
try {
const formData = new URLSearchParams();
// Convert orderData to URLSearchParams
Object.entries(orderData).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
formData.append(key, String(value));
}
});
const response = await apiClient.post("/orders", formData, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
validateStatus: (status) => status >= 200 && status < 300,
});
// Check for HTML response
if (isHtmlResponse(response.data)) {
throw new Error(
"Server returned HTML instead of expected response format"
);
}
return transformOrderActionResponse(response.data, "Order placed");
} catch (error: any) {
// Handle HTML error response
if (error.response && isHtmlResponse(error.response.data)) {
throw new Error(
"Server returned HTML instead of expected response format"
);
}
throw error;
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["orders"] });
queryClient.invalidateQueries({ queryKey: ["cart"] });
},
});
}
// Cancel order mutation
export function useCancelOrder() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (orderId: number) => {
const response = await apiClient.delete(`/orders/${orderId}`);
return transformOrderActionResponse(response.data, "Order cancelled");
},
onSuccess: (_, orderId) => {
queryClient.invalidateQueries({ queryKey: ["orders"] });
queryClient.invalidateQueries({ queryKey: ["order", orderId] });
},
});
}

View File

@@ -1,14 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { PaymentTypeOption } from "@/lib/types/api"
export function usePaymentTypes() {
return useQuery({
queryKey: ["paymentTypes"],
queryFn: async () => {
const response = await apiClient.get<PaymentTypeOption[]>("/api/v1/order-payments")
return response.data
},
staleTime: 1000 * 60 * 60, // 1 hour
})
}

View File

@@ -1,216 +0,0 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api";
import type { Review, Product, PaginatedResponse } from "@/lib/types/api";
// Get single review by ID
export function useReview(
reviewId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["review", reviewId],
queryFn: async () => {
const response = await apiClient.get<Review>(`/reviews/${reviewId}`);
return response.data;
},
enabled: options?.enabled !== false && !!reviewId,
staleTime: 1000 * 60 * 10,
});
}
// Get all reviews with pagination
export function useReviews(options?: {
enabled?: boolean;
page?: number;
limit?: number;
}) {
return useQuery({
queryKey: ["reviews", options?.page, options?.limit],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Review>>(
`/reviews`,
{
params: {
page: options?.page || 1,
limit: options?.limit,
},
}
);
return {
data: response.data.data || [],
pagination: response.data.pagination || {},
};
},
enabled: options?.enabled !== false,
staleTime: 1000 * 60 * 5,
});
}
// Get related reviews for a review
export function useRelatedReviews(
reviewId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["review", reviewId, "related"],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Review>>(
`/reviews/${reviewId}/related`
);
return response.data.data || response.data;
},
enabled: options?.enabled !== false && !!reviewId,
staleTime: 1000 * 60 * 15,
});
}
export function useProducts(options?: UseProductsOptions) {
return useQuery({
queryKey: ["products", options?.page, options?.perPage],
queryFn: async () => {
const response = await apiClient.get<PaginatedResponse<Product>>(
"/products",
{
params: {
page: options?.page || 1,
per_page: options?.perPage || 20,
},
}
);
return response.data.data || response.data;
},
staleTime: options?.staleTime ?? 1000 * 60 * 5,
enabled: options?.enabled !== false,
});
}
// Get single product by ID (for review context)
export function useProduct(
productId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["product", productId],
queryFn: async () => {
const response = await apiClient.get<Product>(`/products/${productId}`);
return response.data;
},
enabled: options?.enabled !== false && !!productId,
staleTime: 1000 * 60 * 10,
});
}
export function useProductsBySlug(
slug: string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["products", "slug", slug],
queryFn: async () => {
const response = await apiClient.get(`/products/${slug}`);
// API returns { message: "success", data: {...} }
return response.data.data || response.data;
},
enabled: options?.enabled !== false && !!slug,
staleTime: 1000 * 60 * 10,
});
}
// Submit review mutation
export function useSubmitReview() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
productId,
rating,
title,
source,
}: {
productId: number | string;
rating: number;
title: string;
source: string;
}) => {
const response = await apiClient.post<Review>(
`/products/${productId}/reviews`,
{ rating, title, source },
{
headers: {
"Content-Type": "application/json",
},
}
);
return response.data;
},
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: ["reviews", "product", variables.productId],
});
queryClient.invalidateQueries({
queryKey: ["product", variables.productId],
});
queryClient.invalidateQueries({
queryKey: ["reviews"],
});
},
});
}
// Update review mutation
export function useUpdateReview() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
reviewId,
rating,
title,
source,
}: {
reviewId: number | string;
rating?: number;
title?: string;
source?: string;
}) => {
const response = await apiClient.put<Review>(
`/reviews/${reviewId}`,
{ rating, title, source },
{
headers: {
"Content-Type": "application/json",
},
}
);
return response.data;
},
onSuccess: (data, variables) => {
queryClient.invalidateQueries({
queryKey: ["review", variables.reviewId],
});
queryClient.invalidateQueries({
queryKey: ["reviews"],
});
},
});
}
// Delete review mutation
export function useDeleteReview() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (reviewId: number | string) => {
const response = await apiClient.delete(`/reviews/${reviewId}`);
return response.data;
},
onSuccess: (_, reviewId) => {
queryClient.invalidateQueries({
queryKey: ["review", reviewId],
});
queryClient.invalidateQueries({
queryKey: ["reviews"],
});
},
});
}

View File

@@ -1,28 +1,28 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { SearchFilters, SearchResponse } from "@/lib/types/api"
// import { useQuery } from "@tanstack/react-query"
// import { apiClient } from "@/lib/api"
// import type { SearchFilters, SearchResponse } from "@/lib/types/api"
export function useSearch(options: SearchFilters) {
const { q, category_id, brand_id, price_from, price_to, page = 1, per_page = 20 } = options
// export function useSearch(options: SearchFilters) {
// const { q, category_id, brand_id, price_from, price_to, page = 1, per_page = 20 } = options
return useQuery({
queryKey: ["search", { q, category_id, brand_id, price_from, price_to, page, per_page }],
queryFn: async () => {
const params = new URLSearchParams({
page: String(page),
per_page: String(per_page),
})
// return useQuery({
// queryKey: ["search", { q, category_id, brand_id, price_from, price_to, page, per_page }],
// queryFn: async () => {
// const params = new URLSearchParams({
// page: String(page),
// per_page: String(per_page),
// })
if (q) params.append("q", q)
if (category_id) params.append("category_id", String(category_id))
if (brand_id) params.append("brand_id", String(brand_id))
if (price_from) params.append("price_from", String(price_from))
if (price_to) params.append("price_to", String(price_to))
// // if (q) params.append("q", q)
// if (category_id) params.append("category_id", String(category_id))
// if (brand_id) params.append("brand_id", String(brand_id))
// if (price_from) params.append("price_from", String(price_from))
// if (price_to) params.append("price_to", String(price_to))
const response = await apiClient.get<SearchResponse>(`/search?${params}`)
return response.data
},
enabled: !!q && q.length > 0,
staleTime: 1000 * 60 * 5,
})
}
// const response = await apiClient.get<SearchResponse>(`/search?${params}`)
// return response.data
// },
// enabled: !!q && q.length > 0,
// staleTime: 1000 * 60 * 5,
// })
// }

View File

@@ -1,18 +0,0 @@
"use client"
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import { API_ENDPOINTS } from "@/lib/config/api-endpoints"
import type { UserProfile } from "@/lib/types/api"
export function useUserProfile(options?: { enabled?: boolean }) {
return useQuery({
queryKey: ["user", "profile"],
queryFn: async () => {
const response = await apiClient.get<UserProfile>(API_ENDPOINTS.profile)
return response.data
},
staleTime: 1000 * 60 * 10, // 10 minutes
enabled: options?.enabled !== false,
})
}

View File

@@ -1,132 +0,0 @@
/**
* Custom axios adapter for mocking API responses in development
* Intercepts requests and routes them to mock handlers
*/
import type { AxiosRequestConfig } from "axios"
import { mockHandlers } from "./handlers"
interface MockRequest extends AxiosRequestConfig {
url?: string
}
export const createMockAdapter = () => {
return async (config: MockRequest) => {
const url = config.url || ""
const method = (config.method || "get").toLowerCase()
try {
if (method === "get") {
if (url === "/products") {
const response = await mockHandlers.getProducts()
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url.match(/^\/products\/\d+$/)) {
const id = Number.parseInt(url.split("/")[2])
const response = await mockHandlers.getProduct(id)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url === "/categories") {
const response = await mockHandlers.getCategories()
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url.match(/^\/categories\/.+$/)) {
const slug = url.split("/")[2]
const response = await mockHandlers.getCategory(slug)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url === "/cart") {
const response = await mockHandlers.getCart()
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url === "/favorites") {
const response = await mockHandlers.getFavorites()
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url === "/orders") {
const response = await mockHandlers.getOrders()
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url.match(/^\/orders\/\d+$/)) {
const id = Number.parseInt(url.split("/")[2])
const response = await mockHandlers.getOrder(id)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url.match(/^\/search/)) {
const params = new URLSearchParams(url.split("?")[1] || "")
const query = params.get("q") || ""
const response = await mockHandlers.search(query, {
category: params.get("category") || undefined,
priceFrom: params.get("priceFrom") ? Number.parseInt(params.get("priceFrom")!) : undefined,
priceTo: params.get("priceTo") ? Number.parseInt(params.get("priceTo")!) : undefined,
})
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
}
if (method === "post") {
if (url === "/cart/items") {
const { productId, quantity } = config.data
const response = await mockHandlers.addToCart(productId, quantity)
return Promise.resolve({ data: response.data, status: 201, config, headers: {}, statusText: "Created" })
}
if (url === "/favorites") {
const { productId } = config.data
const response = await mockHandlers.addToFavorites(productId)
return Promise.resolve({ data: response.data, status: 201, config, headers: {}, statusText: "Created" })
}
if (url.match(/^\/orders\/\d+\/cancel$/)) {
const orderId = Number.parseInt(url.split("/")[2])
const response = await mockHandlers.cancelOrder(orderId)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
}
if (method === "patch") {
if (url.match(/^\/cart\/items\/\d+$/)) {
const itemId = Number.parseInt(url.split("/")[3])
const { quantity } = config.data
const response = await mockHandlers.updateCartItemQuantity(itemId, quantity)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
}
if (method === "delete") {
if (url.match(/^\/cart\/items\/\d+$/)) {
const itemId = Number.parseInt(url.split("/")[3])
const response = await mockHandlers.removeFromCart(itemId)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
if (url.match(/^\/favorites\/\d+$/)) {
const productId = Number.parseInt(url.split("/")[2])
const response = await mockHandlers.removeFromFavorites(productId)
return Promise.resolve({ data: response.data, status: 200, config, headers: {}, statusText: "OK" })
}
}
// Fallback - endpoint not mocked
return Promise.reject({
response: { status: 404, data: { message: "Endpoint not found" }, config },
})
} catch (error: any) {
return Promise.reject({
response: {
status: 400,
data: { message: error.message },
config,
},
})
}
}
}

View File

@@ -1,95 +0,0 @@
/**
* Mock data for development and testing
*/
export const mockProducts = [
{
id: 1,
name: "Premium Wireless Headphones",
price: 199.99,
category: "electronics",
image: "/wireless-headphones.png",
description: "High-quality sound with noise cancellation",
stock: 50,
},
{
id: 2,
name: "Classic Analog Watch",
price: 89.99,
category: "accessories",
image: "/analog-watch.png",
description: "Timeless design with precision movement",
stock: 30,
},
{
id: 3,
name: "Portable Charger 20000mAh",
price: 49.99,
category: "electronics",
image: "/portable-charger-lifestyle.png",
description: "Fast charging technology with dual ports",
stock: 100,
},
{
id: 4,
name: "Leather Messenger Bag",
price: 129.99,
category: "accessories",
image: "/leather-messenger-bag.png",
description: "Premium leather construction",
stock: 25,
},
{
id: 5,
name: "4K Webcam",
price: 149.99,
category: "electronics",
image: "/4k-webcam.jpg",
description: "Professional quality streaming camera",
stock: 40,
},
{
id: 6,
name: "USB-C Hub 7-in-1",
price: 59.99,
category: "electronics",
image: "/usb-c-hub.jpg",
description: "Multiple ports for connectivity",
stock: 75,
},
]
export const mockCategories = [
{
id: 1,
name: "Electronics",
slug: "electronics",
image: "/electronics-category.png",
},
{
id: 2,
name: "Accessories",
slug: "accessories",
image: "/accessories-category.png",
},
]
export const mockOrders = [
{
id: 101,
status: "delivered" as const,
total: 299.98,
createdAt: new Date("2024-11-01").toISOString(),
},
{
id: 102,
status: "processing" as const,
total: 199.99,
createdAt: new Date("2024-11-05").toISOString(),
},
]
export const mockFavorites = [
{ id: 1, productId: 1, addedAt: new Date().toISOString() },
{ id: 2, productId: 3, addedAt: new Date().toISOString() },
]

View File

@@ -1,181 +0,0 @@
/**
* Mock HTTP handlers for development
* Simulates API responses for TanStack Query testing
*/
import { mockProducts, mockCategories, mockOrders, mockFavorites } from "./data"
interface MockCart {
id: string
items: Array<{ id: number; productId: number; quantity: number; price: number }>
total: number
}
// In-memory storage for development
const mockCart: MockCart = {
id: "cart-1",
items: [],
total: 0,
}
let mockUserFavorites = [...mockFavorites]
/**
* Simulate network delay for realistic testing
*/
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
/**
* Mock API handlers
*/
export const mockHandlers = {
// Products
async getProducts() {
await delay(300)
return { data: mockProducts }
},
async getProduct(id: number) {
await delay(300)
const product = mockProducts.find((p) => p.id === id)
if (!product) throw new Error("Product not found")
return { data: product }
},
// Categories
async getCategories() {
await delay(300)
return { data: mockCategories }
},
async getCategory(slug: string) {
await delay(300)
const category = mockCategories.find((c) => c.slug === slug)
if (!category) throw new Error("Category not found")
const products = mockProducts.filter((p) => p.category === slug)
return { data: { ...category, products } }
},
// Cart operations
async getCart() {
await delay(200)
return { data: mockCart }
},
async addToCart(productId: number, quantity = 1) {
await delay(300)
const product = mockProducts.find((p) => p.id === productId)
if (!product) throw new Error("Product not found")
const existingItem = mockCart.items.find((item) => item.productId === productId)
if (existingItem) {
existingItem.quantity += quantity
} else {
mockCart.items.push({
id: Date.now(),
productId,
quantity,
price: product.price,
})
}
mockCart.total = mockCart.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
return { data: mockCart }
},
async removeFromCart(itemId: number) {
await delay(300)
mockCart.items = mockCart.items.filter((item) => item.id !== itemId)
mockCart.total = mockCart.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
return { data: mockCart }
},
async updateCartItemQuantity(itemId: number, quantity: number) {
await delay(300)
const item = mockCart.items.find((i) => i.id === itemId)
if (!item) throw new Error("Item not found")
item.quantity = Math.max(1, quantity)
mockCart.total = mockCart.items.reduce((sum, i) => sum + i.price * i.quantity, 0)
return { data: mockCart }
},
// Favorites
async getFavorites() {
await delay(200)
return { data: mockUserFavorites }
},
async addToFavorites(productId: number) {
await delay(300)
const product = mockProducts.find((p) => p.id === productId)
if (!product) throw new Error("Product not found")
const exists = mockUserFavorites.find((f) => f.productId === productId)
if (!exists) {
mockUserFavorites.push({
id: Date.now(),
productId,
addedAt: new Date().toISOString(),
})
}
return { data: mockUserFavorites }
},
async removeFromFavorites(productId: number) {
await delay(300)
mockUserFavorites = mockUserFavorites.filter((f) => f.productId !== productId)
return { data: mockUserFavorites }
},
// Orders
async getOrders() {
await delay(300)
return { data: mockOrders }
},
async getOrder(id: number) {
await delay(300)
const order = mockOrders.find((o) => o.id === id)
if (!order) throw new Error("Order not found")
return { data: order }
},
async cancelOrder(orderId: number) {
await delay(300)
const order = mockOrders.find((o) => o.id === orderId)
if (!order) throw new Error("Order not found")
if (order.status !== "processing" && order.status !== "pending") {
throw new Error("Cannot cancel shipped or delivered orders")
}
order.status = "cancelled"
return { data: order }
},
// Search
async search(query: string, filters?: { category?: string; priceFrom?: number; priceTo?: number }) {
await delay(400)
let results = mockProducts
if (query) {
results = results.filter(
(p) =>
p.name.toLowerCase().includes(query.toLowerCase()) ||
p.description.toLowerCase().includes(query.toLowerCase()),
)
}
if (filters?.category) {
results = results.filter((p) => p.category === filters.category)
}
if (filters?.priceFrom) {
results = results.filter((p) => p.price >= filters.priceFrom!)
}
if (filters?.priceTo) {
results = results.filter((p) => p.price <= filters.priceTo!)
}
return { data: { products: results, total: results.length } }
},
}

View File

@@ -1,239 +1,258 @@
/**
* API Response and Entity Type Definitions
* Based on Postman collection structure
*/
// Product Types
export interface ProductMedia {
thumbnail: string
images_400x400: string
images_720x720: string
images_800x800: string
images_1200x1200: string
thumbnail: string;
images_400x400: string;
images_720x720: string;
images_800x800: string;
images_1200x1200: string;
}
export interface ProductProperty {
attribute_id: number
name: string
value: string
attribute_id: number;
name: string;
value: string;
}
export interface ProductReviews {
count: number
rating: string
count: number;
rating: string;
}
export interface Product {
id: number
parent_id: number | null
name: string
slug: string
description: string
sku: string | null
barcode: string
stock: number
price_amount: string
old_price_amount: string | null
backorder: string
weight_value: number | null
weight_unit: string | null
height_value: number | null
height_unit: string | null
media: ProductMedia[]
created_at: string
seo_title: string | null
seo_description: string | null
colour: string | null
size: string | null
available_colors: string[]
available_sizes: string[]
id: number;
parent_id: number | null;
name: string;
slug: string;
description: string;
sku: string | null;
barcode: string;
stock: number;
price_amount: string;
old_price_amount: string | null;
backorder: string;
weight_value: number | null;
weight_unit: string | null;
height_value: number | null;
height_unit: string | null;
media: ProductMedia[];
created_at: string;
seo_title: string | null;
seo_description: string | null;
is_visible: boolean;
colour: string | null;
size: string | null;
available_colors?: string[];
available_sizes?: string[];
brand: {
id: number | null
name: string | null
}
channel: Array<{
id: number
name: string
}>
properties: ProductProperty[]
variations: any[]
reviews: ProductReviews
reviews_resources: any[]
categories: Array<{
id: number
name: string
}>
id: number | null;
name: string | null;
};
channel?: Array<{
id: number;
name: string;
}>;
properties?: ProductProperty[];
variations?: any[];
reviews: ProductReviews;
reviews_resources?: any[];
categories?: Array<{
id: number;
name: string;
}>;
}
// Category Types
export interface Category {
id: number
name: string
slug: string
image: string
parent_id?: number
children?: Category[]
id: number;
name: string;
slug: string;
image: string;
parent_id?: number;
children?: Category[];
}
// Collection Types
export interface Collection {
id: number;
name: string;
slug?: string;
description?: string;
image?: string;
created_at?: string;
}
// Cart Types
export interface CartItem {
id: number
product_id: number
product?: Product
id: number;
product_id: number;
product?: Product;
seller: {
id: number
name: string
}
quantity: number
price: number
total: number
price_formatted?: string
sub_total_formatted?: string
id: number;
name: string;
};
quantity: number;
price: number;
total: number;
price_formatted?: string;
sub_total_formatted?: string;
}
export interface Cart {
id: string
items: CartItem[]
total: number
total_formatted?: string
count?: number
id: string;
items: CartItem[];
total: number;
total_formatted?: string;
count?: number;
}
// Favorites Types
export interface Favorite {
id: number
product_id: number
product?: Product
added_at?: string
id: number;
product_id: number;
product?: Product;
added_at?: string;
}
// Order Types
export interface OrderItem {
id: number
product_id: number
product?: Product
quantity: number
price: number
total: number
id: number;
product_id: number;
product?: Product;
quantity: number;
price: number;
total: number;
}
export interface Order {
id: number
number?: string
status: "pending" | "processing" | "shipped" | "delivered" | "cancelled"
items: OrderItem[]
total: number
total_formatted?: string
created_at: string
updated_at?: string
estimated_delivery?: string
tracking_number?: string
id: number;
number?: string;
status: "pending" | "processing" | "shipped" | "delivered" | "cancelled";
items: OrderItem[];
total: number;
total_formatted?: string;
created_at: string;
updated_at?: string;
estimated_delivery?: string;
tracking_number?: string;
}
// Pagination Types
export interface Pagination {
page: number;
perPage: number;
count: number;
first_page_url?: string;
next_page_url?: string | null;
prev_page_url?: string | null;
current_page?: number;
last_page?: number;
per_page?: number;
total?: number;
}
export interface PaginatedResponse<T> {
data: T[]
pagination: {
current_page: number
last_page: number
per_page: number
total: number
}
message?: string;
data: T[];
pagination: Pagination;
}
// Search Types
export interface SearchFilters {
q?: string
category_id?: number
brand_id?: number
price_from?: number
price_to?: number
page?: number
per_page?: number
q?: string;
category_id?: number;
brand_id?: number;
price_from?: number;
price_to?: number;
page?: number;
per_page?: number;
}
export interface SearchResponse {
products: Product[]
total: number
products: Product[];
total: number;
filters?: {
brands: Array<{ id: number; name: string }>
categories: Array<{ id: number; name: string }>
price_range: { min: number; max: number }
}
brands: Array<{ id: number; name: string }>;
categories: Array<{ id: number; name: string }>;
price_range: { min: number; max: number };
};
}
// Profile Types
export interface UserProfile {
id: number
email: string
phone?: string
first_name?: string
last_name?: string
avatar?: string
created_at: string
id: number;
email: string;
phone?: string;
first_name?: string;
last_name?: string;
avatar?: string;
created_at: string;
}
// Auth Types
export interface AuthResponse {
token: string
user: UserProfile
token: string;
user: UserProfile;
}
// Banner Types
export interface Banner {
id: number
title: string
image: string
url?: string
type?: string
place?: string
id: number;
title: string;
image: string;
thumbnail?: string;
link?: string;
url?: string;
type?: string;
place?: string;
}
// Generic API Error Response
export interface ApiError {
message: string
errors?: Record<string, string[]>
message: string;
errors?: Record<string, string[]>;
}
// Region, Address, PaymentType, and ShippingMethod Types
export interface Region {
id: number
code: string
name: string
id: number;
code: string;
name: string;
}
export interface Address {
id: number
title: string
region_id: number
address: string
phone?: string
is_default?: boolean
id: number;
title: string;
region_id: number;
address: string;
phone?: string;
is_default?: boolean;
}
export interface PaymentTypeOption {
id: number
name: string
code: string
id: number;
name: string;
code: string;
}
export interface ShippingMethod {
id: number
name: string
code: string
id: number;
name: string;
code: string;
}
// Order creation payload type
export interface CreateOrderPayload {
customer_name?: string
customer_phone?: string
customer_address: string
shipping_method: string
payment_type_id: number
delivery_time?: string
delivery_at?: string
region: string
note?: string
customer_name?: string;
customer_phone?: string;
customer_address: string;
shipping_method: string;
payment_type_id: number;
delivery_time?: string;
delivery_at?: string;
region: string;
note?: string;
}