added some api

This commit is contained in:
Jelaletdin12
2025-11-13 21:56:30 +05:00
parent fdec9e4b0e
commit 21b9e88c5c
22 changed files with 2268 additions and 930 deletions

View File

@@ -9,7 +9,12 @@ export * from "./useOpenStore"
export * from "./useRegions"
export * from "./useAddresses"
export * from "./usePaymentTypes"
export * from "./useCategories"
export * from "./useMedia"
export * from "./useCollections"

View File

@@ -1,14 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
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,16 +1,61 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { useQuery, useMutation, useQueryClient, UseQueryOptions } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Cart, CartItem } from "@/lib/types/api"
export function useCart() {
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<Cart>("/api/v1/carts")
return response.data
const response = await apiClient.get("/carts")
return transformCartResponse(response.data)
},
staleTime: 0, // Always fetch fresh
refetchInterval: 5000, // Poll every 5 seconds like RTK
refetchOnMount: true,
refetchOnWindowFocus: false,
refetchOnReconnect: true,
staleTime: 0,
retry: 1,
...options,
})
}
@@ -19,17 +64,38 @@ export function useAddToCart() {
return useMutation({
mutationFn: async ({ productId, quantity = 1 }: { productId: number; quantity?: number }) => {
const response = await apiClient.post<Cart>("/api/v1/carts", {
product_id: productId,
quantity,
const params = new URLSearchParams({
product_id: String(productId),
product_quantity: String(quantity),
})
return response.data
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("[v0] Add to cart error:", error.response?.data?.message || error.message)
console.error("Add to cart error:", error.response?.data?.message || error.message)
},
})
}
@@ -38,8 +104,66 @@ export function useRemoveFromCart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (itemId: number) => {
await apiClient.delete(`/api/v1/carts/${itemId}`)
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"] })
@@ -51,15 +175,40 @@ export function useUpdateCartItemQuantity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ itemId, quantity }: { itemId: number; quantity: number }) => {
const response = await apiClient.patch<CartItem>(`/api/v1/carts/${itemId}`, {
quantity,
mutationFn: async ({ productId, quantity }: { productId: number; quantity: number }) => {
const params = new URLSearchParams({
product_id: String(productId),
product_quantity: String(quantity),
})
return response.data
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)
},
})
}
@@ -86,7 +235,36 @@ export function useCreateOrder() {
queryClient.invalidateQueries({ queryKey: ["orders"] })
},
onError: (error: any) => {
console.error("[v0] Create order error:", error.response?.data?.message || error.message)
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

@@ -92,6 +92,8 @@ export function useAllCategoryProducts(
})
}
// Get products from category and children WITH pagination (mimics RTK getAllCategoryProductsPaginated)
export function useAllCategoryProductsPaginated(
category: Category | undefined,
@@ -155,4 +157,5 @@ export function useAllCategoryProductsPaginated(
},
enabled: options?.enabled !== false && !!category,
})
}
}

View File

@@ -1,42 +1,112 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Favorite } from "@/lib/types/api"
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<Favorite[]>("/favorites")
return response.data
const response = await apiClient.get("/favorites");
return transformFavoritesResponse(response.data);
},
staleTime: 1000 * 60 * 5,
retry: 1,
})
});
}
export function useAddToFavorites() {
const queryClient = useQueryClient()
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (productId: number) => {
const response = await apiClient.post<Favorite[]>("/favorites", { product_id: productId })
return response.data
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"] })
queryClient.invalidateQueries({ queryKey: ["favorites"] });
},
})
});
}
export function useRemoveFromFavorites() {
const queryClient = useQueryClient()
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (productId: number) => {
await apiClient.delete(`/favorites/${productId}`)
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"] })
queryClient.invalidateQueries({ queryKey: ["favorites"] });
},
})
});
}

View File

@@ -1,59 +1,165 @@
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Order, PaginatedResponse } from "@/lib/types/api"
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<PaginatedResponse<Order>>("/orders", {
const response = await apiClient.get("/orders", {
params: {
page: options?.page || 1,
per_page: options?.perPage || 20,
},
})
return response.data.data || response.data
});
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<Order>(`/orders/${id}`)
return response.data
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()
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (orderId: number) => {
await apiClient.post(`/orders/${orderId}/cancel`, {})
const response = await apiClient.delete(`/orders/${orderId}`);
return transformOrderActionResponse(response.data, "Order cancelled");
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["orders"] })
onSuccess: (_, orderId) => {
queryClient.invalidateQueries({ queryKey: ["orders"] });
queryClient.invalidateQueries({ queryKey: ["order", orderId] });
},
})
}
export function useCreateOrder() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (orderData: any) => {
const response = await apiClient.post<Order>("/orders", orderData)
return response.data
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["orders"] })
queryClient.invalidateQueries({ queryKey: ["cart"] })
},
})
});
}

View File

@@ -1,50 +1,216 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Product, PaginatedResponse } from "@/lib/types/api"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api";
import type { Review, Product, PaginatedResponse } from "@/lib/types/api";
interface UseProductsOptions {
enabled?: boolean
staleTime?: number
page?: number
perPage?: number
// 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
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, // 5 minutes
staleTime: options?.staleTime ?? 1000 * 60 * 5,
enabled: options?.enabled !== false,
})
});
}
export function useProduct(id: number | string, options?: { enabled?: boolean }) {
// Get single product by ID (for review context)
export function useProduct(
productId: number | string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["product", id],
queryKey: ["product", productId],
queryFn: async () => {
const response = await apiClient.get<Product>(`/products/${id}`)
return response.data
const response = await apiClient.get<Product>(`/products/${productId}`);
return response.data;
},
staleTime: 1000 * 60 * 10, // 10 minutes
enabled: options?.enabled !== false && !!id,
})
enabled: options?.enabled !== false && !!productId,
staleTime: 1000 * 60 * 10,
});
}
export function useProductsBySlug(slug: string, options?: { enabled?: boolean }) {
export function useProductsBySlug(
slug: string,
options?: { enabled?: boolean }
) {
return useQuery({
queryKey: ["products", "slug", slug],
queryFn: async () => {
const response = await apiClient.get<Product>(`/products/${slug}`)
return response.data
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,14 +0,0 @@
import { useQuery } from "@tanstack/react-query"
import { apiClient } from "@/lib/api"
import type { Region } from "@/lib/types/api"
export function useRegions() {
return useQuery({
queryKey: ["regions"],
queryFn: async () => {
const response = await apiClient.get<Region[]>("/api/v1/regions")
return response.data
},
staleTime: 1000 * 60 * 60, // 1 hour
})
}