initial commit
This commit is contained in:
100
src/app/api/authApi.js
Normal file
100
src/app/api/authApi.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const authApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getGuestToken: builder.mutation({
|
||||
query: () => ({
|
||||
url: "auth/guest-token",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}),
|
||||
async onQueryStarted(_, { queryFulfilled }) {
|
||||
try {
|
||||
const { data } = await queryFulfilled;
|
||||
const token = data?.token;
|
||||
|
||||
if (token) {
|
||||
document.cookie = `guestToken=${token}; path=/; secure; SameSite=Strict`;
|
||||
localStorage.setItem("guestToken", token);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching guest token:", error);
|
||||
}
|
||||
},
|
||||
}),
|
||||
login: builder.mutation({
|
||||
query: (credentials) => ({
|
||||
url: "/auth/login",
|
||||
method: "POST",
|
||||
body: credentials,
|
||||
}),
|
||||
transformResponse: (response) => {
|
||||
const token = response?.token || response?.data;
|
||||
if (token) {
|
||||
localStorage.removeItem("guestToken");
|
||||
document.cookie =
|
||||
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
||||
localStorage.setItem("authToken", token);
|
||||
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
|
||||
}
|
||||
return response;
|
||||
},
|
||||
}),
|
||||
|
||||
register: builder.mutation({
|
||||
query: (userData) => ({
|
||||
url: "/auth/register",
|
||||
method: "POST",
|
||||
body: userData,
|
||||
}),
|
||||
transformResponse: (response) => {
|
||||
const token = response?.token || response?.data;
|
||||
if (token) {
|
||||
localStorage.removeItem("guestToken");
|
||||
document.cookie =
|
||||
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
||||
localStorage.setItem("authToken", token);
|
||||
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
|
||||
}
|
||||
return response;
|
||||
},
|
||||
}),
|
||||
verifyToken: builder.mutation({
|
||||
query: ({ phone_number, code }) => ({
|
||||
url: "/auth/verify",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: { phone_number, code },
|
||||
}),
|
||||
async onQueryStarted(_, { queryFulfilled }) {
|
||||
try {
|
||||
const { data } = await queryFulfilled;
|
||||
const token = data.data;
|
||||
// console.log("Full Response:", data);
|
||||
// console.log("New Token:", token);
|
||||
if (token) {
|
||||
localStorage.removeItem("guestToken");
|
||||
document.cookie =
|
||||
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
||||
localStorage.setItem("authToken", token);
|
||||
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error verifying token:", error);
|
||||
}
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetGuestTokenMutation,
|
||||
useLoginMutation,
|
||||
useRegisterMutation,
|
||||
useVerifyTokenMutation,
|
||||
} = authApi;
|
||||
14
src/app/api/bannersApi.js
Normal file
14
src/app/api/bannersApi.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { baseApi } from './baseApi';
|
||||
|
||||
export const mediaApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getCarousels: builder.query({
|
||||
query: () => '/media/carousels',
|
||||
}),
|
||||
getBanners: builder.query({
|
||||
query: () => '/media/banners',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetCarouselsQuery, useGetBannersQuery } = mediaApi;
|
||||
124
src/app/api/baseApi.js
Normal file
124
src/app/api/baseApi.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
||||
import i18n from "../../i18n/i18n"; // Import your i18n configuration
|
||||
|
||||
const getToken = () => {
|
||||
// Önce cookie'lerden token kontrol et
|
||||
const authTokenCookie = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("authToken="));
|
||||
|
||||
const guestTokenCookie = document.cookie
|
||||
.split("; ")
|
||||
.find((row) => row.startsWith("guestToken="));
|
||||
|
||||
// Sonra localStorage'dan kontrol et
|
||||
const authTokenLocal = localStorage.getItem("authToken");
|
||||
const guestTokenLocal = localStorage.getItem("guestToken");
|
||||
|
||||
// Auth token varsa onu döndür (önce cookie sonra localStorage)
|
||||
if (authTokenCookie) return authTokenCookie.split("=")[1];
|
||||
if (authTokenLocal) return authTokenLocal;
|
||||
|
||||
// Guest token varsa onu döndür
|
||||
if (guestTokenCookie) return guestTokenCookie.split("=")[1];
|
||||
if (guestTokenLocal) return guestTokenLocal;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const customBaseQuery = async (args, api, extraOptions) => {
|
||||
const token = getToken();
|
||||
const currentLanguage = i18n.language || "tm";
|
||||
|
||||
// Add language parameter to the URL
|
||||
const urlWithLang =
|
||||
typeof args === "string"
|
||||
? `${args}${args.includes("?") ? "&" : "?"}lang=${currentLanguage}`
|
||||
: {
|
||||
...args,
|
||||
url: `${args.url}${
|
||||
args.url.includes("?") ? "&" : "?"
|
||||
}lang=${currentLanguage}`,
|
||||
};
|
||||
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: import.meta.env.VITE_API_URL || "https://mm.com.tm/api/v1/",
|
||||
prepareHeaders: (headers) => {
|
||||
headers.set("Api-Token", import.meta.env.VITE_API_TOKEN || "hello-mf-s");
|
||||
if (token) {
|
||||
headers.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
|
||||
if (!headers.has("Content-Type")) {
|
||||
headers.set("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
return headers;
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await baseQuery(urlWithLang, api, extraOptions);
|
||||
|
||||
if (result.error && result.error.status === 401) {
|
||||
try {
|
||||
const guestTokenResponse = await fetch(
|
||||
`${
|
||||
import.meta.env.VITE_API_URL || "https://mm.com.tm/api/v1/"
|
||||
}/auth/guest-token`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Api-Token": import.meta.env.VITE_API_TOKEN || "hello-mf-s",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const data = await guestTokenResponse.json();
|
||||
const newGuestToken = data.token || data.data;
|
||||
|
||||
if (newGuestToken) {
|
||||
localStorage.setItem("guestToken", newGuestToken);
|
||||
document.cookie = `guestToken=${newGuestToken}; path=/; secure; SameSite=Strict`;
|
||||
|
||||
const retryResult = await baseQuery(urlWithLang, api, extraOptions);
|
||||
return retryResult;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to get new guest token:", error);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
result.error &&
|
||||
typeof result.error.data === "string" &&
|
||||
result.error.data.includes("<!DOCTYPE html>")
|
||||
) {
|
||||
return {
|
||||
error: {
|
||||
status: result.error.status,
|
||||
data: {
|
||||
message: "Server returned an HTML error page instead of JSON",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (axiosError) {
|
||||
console.error("Query failed:", axiosError);
|
||||
return {
|
||||
error: {
|
||||
status: "CUSTOM_ERROR",
|
||||
error: axiosError.message || "Unknown error occurred",
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const baseApi = createApi({
|
||||
reducerPath: "api",
|
||||
baseQuery: customBaseQuery,
|
||||
endpoints: () => ({}),
|
||||
});
|
||||
60
src/app/api/brandsApi.js
Normal file
60
src/app/api/brandsApi.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const brandsApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getBrands: builder.query({
|
||||
query: (params = {}) => {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params.type) {
|
||||
queryParams.append("type", params.type);
|
||||
}
|
||||
if (params.page) {
|
||||
queryParams.append("page", params.page);
|
||||
}
|
||||
|
||||
if (params.limit) {
|
||||
queryParams.append("limit", params.limit);
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
return `/brands${queryString ? `?${queryString}` : ""}`;
|
||||
},
|
||||
transformResponse: (response) => response.data || response,
|
||||
}),
|
||||
|
||||
getBrandDetails: builder.query({
|
||||
query: (brandId) => `/brands/${brandId}`,
|
||||
transformResponse: (response) => response.data || response,
|
||||
}),
|
||||
|
||||
getBrandProducts: builder.query({
|
||||
query: (params) => {
|
||||
// Handle both string ID and object with pagination params
|
||||
if (typeof params === 'string' || typeof params === 'number') {
|
||||
return `/brands/${params}/products`;
|
||||
}
|
||||
|
||||
// Handle object with pagination
|
||||
const { id, page = 1, limit } = params;
|
||||
let url = `/brands/${id}/products?page=${page}`;
|
||||
|
||||
if (limit) {
|
||||
url += `&limit=${limit}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
},
|
||||
transformResponse: (response) => {
|
||||
return response.data || response;
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetBrandsQuery,
|
||||
useLazyGetBrandsQuery,
|
||||
useGetBrandDetailsQuery,
|
||||
useGetBrandProductsQuery,
|
||||
useLazyGetBrandProductsQuery,
|
||||
} = brandsApi;
|
||||
202
src/app/api/cartApi.js
Normal file
202
src/app/api/cartApi.js
Normal file
@@ -0,0 +1,202 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const cartApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getCart: builder.query({
|
||||
query: () => `/carts`,
|
||||
providesTags: ["cartItems"],
|
||||
pollingInterval: 5000,
|
||||
refetchOnMountOrArgChange: true,
|
||||
refetchOnFocus: false,
|
||||
refetchOnReconnect: true,
|
||||
transformResponse: (response) => {
|
||||
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: [] };
|
||||
},
|
||||
}),
|
||||
|
||||
addToCart: builder.mutation({
|
||||
query: ({ productId, quantity }) => ({
|
||||
url: `/carts`,
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
product_id: productId,
|
||||
product_quantity: quantity,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}),
|
||||
invalidatesTags: ["cartItems"],
|
||||
async onQueryStarted(
|
||||
{ productId, quantity },
|
||||
{ dispatch, queryFulfilled }
|
||||
) {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
} catch {}
|
||||
},
|
||||
transformResponse: (response) => {
|
||||
if (typeof response === "object" && response.data) {
|
||||
return response;
|
||||
}
|
||||
|
||||
if (typeof response === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(response);
|
||||
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" };
|
||||
},
|
||||
}),
|
||||
|
||||
removeFromCart: builder.mutation({
|
||||
query: ({ productId }) => ({
|
||||
url: `/carts`,
|
||||
method: "PATCH",
|
||||
body: new URLSearchParams({ product_id: productId }),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}),
|
||||
invalidatesTags: ["cartItems"],
|
||||
async onQueryStarted({ productId }, { dispatch, queryFulfilled }) {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
} catch {}
|
||||
},
|
||||
transformResponse: (response) => {
|
||||
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 cart response:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
}),
|
||||
|
||||
cleanCart: builder.mutation({
|
||||
query: () => ({
|
||||
url: `/carts`,
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}),
|
||||
invalidatesTags: ["cartItems"],
|
||||
transformResponse: (response) => {
|
||||
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 cart response:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
}),
|
||||
|
||||
updateCartItem: builder.mutation({
|
||||
query: ({ productId, quantity }) => ({
|
||||
url: `/carts`,
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
product_id: productId,
|
||||
product_quantity: quantity,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}),
|
||||
invalidatesTags: ["cartItems"],
|
||||
async onQueryStarted(
|
||||
{ productId, quantity },
|
||||
{ dispatch, queryFulfilled }
|
||||
) {
|
||||
try {
|
||||
await queryFulfilled;
|
||||
} catch {
|
||||
console.error("API update failed, retrying...");
|
||||
}
|
||||
},
|
||||
transformResponse: (response) => {
|
||||
if (typeof response === "object" && response.data) {
|
||||
return response;
|
||||
}
|
||||
|
||||
if (typeof response === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(response);
|
||||
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" };
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetCartQuery,
|
||||
useAddToCartMutation,
|
||||
useRemoveFromCartMutation,
|
||||
useUpdateCartItemMutation,
|
||||
useCleanCartMutation,
|
||||
} = cartApi;
|
||||
110
src/app/api/categories.js
Normal file
110
src/app/api/categories.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const categoriesApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getCategories: builder.query({
|
||||
query: (type = "tree") => `/categories?type=${type}`,
|
||||
}),
|
||||
getCategoryProducts: builder.query({
|
||||
query: ({ categoryId, page = 1, limit }) => {
|
||||
let url = `categories/${categoryId}/products?page=${page}`;
|
||||
if (limit) url += `&limit=${limit}`;
|
||||
return url;
|
||||
},
|
||||
transformResponse: (response) => ({
|
||||
data: response.data || [],
|
||||
pagination: response.pagination || {},
|
||||
}),
|
||||
}),
|
||||
getAllCategoryProducts: builder.query({
|
||||
async queryFn(category, queryApi, extraOptions, baseQuery) {
|
||||
const fetchProducts = async (categoryId) => {
|
||||
const result = await baseQuery(`categories/${categoryId}/products`);
|
||||
return result.data ? result.data.data : [];
|
||||
};
|
||||
|
||||
let allProducts = await fetchProducts(category.id);
|
||||
|
||||
for (const child of category.children) {
|
||||
const childProducts = await fetchProducts(child.id);
|
||||
allProducts = [...allProducts, ...childProducts];
|
||||
}
|
||||
|
||||
return { data: allProducts };
|
||||
},
|
||||
}),
|
||||
|
||||
getAllCategoryProductsPaginated: builder.query({
|
||||
async queryFn(
|
||||
{ category, page = 1, limit = 6 },
|
||||
queryApi,
|
||||
extraOptions,
|
||||
baseQuery
|
||||
) {
|
||||
if (!category) return { data: [] };
|
||||
|
||||
try {
|
||||
const hasMoreByCategory = {};
|
||||
|
||||
const fetchProductsForPage = async (categoryIds, currentPage) => {
|
||||
let allPageProducts = [];
|
||||
const perCategoryLimit = Math.ceil(limit / categoryIds.length);
|
||||
|
||||
for (const categoryId of categoryIds) {
|
||||
const result = await baseQuery(
|
||||
`categories/${categoryId}/products?page=${currentPage}&limit=${perCategoryLimit}`
|
||||
);
|
||||
if (result.data && result.data.data) {
|
||||
allPageProducts = [...allPageProducts, ...result.data.data];
|
||||
|
||||
hasMoreByCategory[categoryId] =
|
||||
!!result.data.pagination.next_page_url;
|
||||
}
|
||||
}
|
||||
|
||||
return allPageProducts;
|
||||
};
|
||||
|
||||
const categoryIds = [category.id];
|
||||
if (category.children && category.children.length > 0) {
|
||||
category.children.forEach((child) => categoryIds.push(child.id));
|
||||
}
|
||||
|
||||
const productsForPage = await fetchProductsForPage(categoryIds, page);
|
||||
|
||||
const hasMorePages = Object.values(hasMoreByCategory).some(
|
||||
(hasMore) => hasMore
|
||||
);
|
||||
|
||||
return {
|
||||
data: {
|
||||
data: productsForPage,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
hasMorePages: hasMorePages,
|
||||
},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
},
|
||||
}),
|
||||
getProductById: builder.query({
|
||||
query: (productId) => `/products/${productId}`,
|
||||
}),
|
||||
getRelatedProducts: builder.query({
|
||||
query: (productId) => `/products/${productId}/related`,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetCategoriesQuery,
|
||||
useGetCategoryProductsQuery,
|
||||
useGetAllCategoryProductsQuery,
|
||||
useGetAllCategoryProductsPaginatedQuery,
|
||||
useLazyGetAllCategoryProductsPaginatedQuery,
|
||||
useGetProductByIdQuery,
|
||||
useGetRelatedProductsQuery,
|
||||
} = categoriesApi;
|
||||
51
src/app/api/collectionsApi.js
Normal file
51
src/app/api/collectionsApi.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const collectionsApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getCollections: builder.query({
|
||||
query: () => `/collections`,
|
||||
}),
|
||||
getCollectionById: builder.query({
|
||||
query: (collectionId) => `/collections/${collectionId}`,
|
||||
}),
|
||||
getCollectionProducts: builder.query({
|
||||
query: (collectionId) => `/collections/${collectionId}/products`,
|
||||
// Ürünleri dönüştürerek boş kontrol edilebilir
|
||||
transformResponse: (response) => {
|
||||
return {
|
||||
data: response.data || [],
|
||||
isEmpty: !response.data || response.data.length === 0,
|
||||
};
|
||||
},
|
||||
}),
|
||||
// Yeni endpoint: Koleksiyonun ürün içerip içermediğini kontrol eder
|
||||
checkCollectionHasProducts: builder.query({
|
||||
query: (collectionId) => `/collections/${collectionId}/products?limit=1`,
|
||||
transformResponse: (response) => {
|
||||
return {
|
||||
hasProducts: response.data && response.data.length > 0,
|
||||
};
|
||||
},
|
||||
}),
|
||||
// Sayfalı koleksiyon ürünleri için endpoint
|
||||
getCollectionProductsPaginated: builder.query({
|
||||
query: ({ collectionId, page = 1, limit = 6 }) =>
|
||||
`/collections/${collectionId}/products?page=${page}${limit ? `&limit=${limit}` : ''}`,
|
||||
transformResponse: (response) => ({
|
||||
data: response.data || [],
|
||||
pagination: response.pagination || {},
|
||||
isEmpty: !response.data || response.data.length === 0,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetCollectionsQuery,
|
||||
useGetCollectionByIdQuery,
|
||||
useGetCollectionProductsQuery,
|
||||
useCheckCollectionHasProductsQuery,
|
||||
useGetCollectionProductsPaginatedQuery,
|
||||
useLazyGetCollectionProductsPaginatedQuery,
|
||||
useLazyCheckCollectionHasProductsQuery,
|
||||
} = collectionsApi;
|
||||
19
src/app/api/contactUs.js
Normal file
19
src/app/api/contactUs.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const contactApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
submitContactMessage: builder.mutation({
|
||||
query: ({ phone, title, content, type }) => ({
|
||||
url: "/forms/contact-us",
|
||||
method: "POST",
|
||||
body: { phone, title, content, type },
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useSubmitContactMessageMutation } = contactApi;
|
||||
97
src/app/api/favoritesApi.js
Normal file
97
src/app/api/favoritesApi.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const favoritesApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getFavorites: builder.query({
|
||||
query: () => `/favorites`,
|
||||
providesTags: ["Favorites"],
|
||||
transformResponse: (response) => {
|
||||
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 [];
|
||||
},
|
||||
}),
|
||||
|
||||
addFavorite: builder.mutation({
|
||||
query: (productId) => ({
|
||||
url: `/favorites`,
|
||||
method: "POST",
|
||||
body: new URLSearchParams({ product_id: productId }),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
invalidatesTags: ["Favorites"],
|
||||
}),
|
||||
transformResponse: (response) => {
|
||||
if (typeof response === "object" && response.data) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
if (typeof response === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(response);
|
||||
return parsed.data || "Added";
|
||||
} catch (error) {
|
||||
if (response.includes("<!doctype html>")) {
|
||||
return "Added";
|
||||
}
|
||||
console.error("Failed to parse add favorite response:", error);
|
||||
return "Added";
|
||||
}
|
||||
}
|
||||
return "Added";
|
||||
},
|
||||
}),
|
||||
|
||||
removeFavorite: builder.mutation({
|
||||
query: (productId) => ({
|
||||
url: `/favorites`,
|
||||
method: "POST",
|
||||
body: new URLSearchParams({
|
||||
product_id: productId,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
invalidatesTags: ["Favorites"],
|
||||
}),
|
||||
transformResponse: (response) => {
|
||||
if (typeof response === "object" && response.data) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
if (typeof response === "string") {
|
||||
try {
|
||||
const parsed = JSON.parse(response);
|
||||
return parsed.data || "Removed";
|
||||
} catch (error) {
|
||||
if (response.includes("<!doctype html>")) {
|
||||
return "Removed";
|
||||
}
|
||||
console.error("Failed to parse remove favorite response:", error);
|
||||
return "Removed";
|
||||
}
|
||||
}
|
||||
return "Removed";
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetFavoritesQuery,
|
||||
useAddFavoriteMutation,
|
||||
useRemoveFavoriteMutation,
|
||||
} = favoritesApi;
|
||||
15
src/app/api/legalPagesApi.js
Normal file
15
src/app/api/legalPagesApi.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const legalPagesApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getLegalPages: builder.query({
|
||||
query: () => `/legal-pages`,
|
||||
}),
|
||||
getLegalPageBySlug: builder.query({
|
||||
query: (slug) => `/legal-pages/${slug}`,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetLegalPagesQuery, useGetLegalPageBySlugQuery } =
|
||||
legalPagesApi;
|
||||
11
src/app/api/locationApi.js
Normal file
11
src/app/api/locationApi.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const locationApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getLocations: builder.query({
|
||||
query: () => "/provinces",
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetLocationsQuery } = locationApi;
|
||||
36
src/app/api/myProfileApi.js
Normal file
36
src/app/api/myProfileApi.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const profileApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getProfile: builder.query({
|
||||
query: () => {
|
||||
const authToken = localStorage.getItem("authToken");
|
||||
return {
|
||||
url: "profile",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
Accept: "application/json",
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
updateProfile: builder.mutation({
|
||||
query: (profileData) => {
|
||||
const authToken = localStorage.getItem("authToken");
|
||||
return {
|
||||
url: "profile",
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: profileData,
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetProfileQuery, useUpdateProfileMutation } = profileApi;
|
||||
81
src/app/api/orderApi.js
Normal file
81
src/app/api/orderApi.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const orderApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getOrders: builder.query({
|
||||
query: () => "/orders",
|
||||
transformResponse: (response) => response.data || [],
|
||||
}),
|
||||
|
||||
getOrderById: builder.query({
|
||||
query: (orderId) => `/orders/${orderId}`,
|
||||
transformResponse: (response) => response.data || null,
|
||||
}),
|
||||
placeOrder: builder.mutation({
|
||||
query: (orderDetails) => ({
|
||||
url: "/orders",
|
||||
method: "POST",
|
||||
body: new URLSearchParams(orderDetails),
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
}),
|
||||
transformResponse: (response, meta, arg) => {
|
||||
console.log({response: response})
|
||||
if (response && response.data) {
|
||||
return response.data;
|
||||
}
|
||||
return "Order placed";
|
||||
},
|
||||
|
||||
transformErrorResponse: (response, meta, arg) => {
|
||||
if (
|
||||
typeof response.data === "string" &&
|
||||
response.data.includes("<!doctype html>")
|
||||
) {
|
||||
return {
|
||||
status: "CUSTOM_ERROR",
|
||||
data: {
|
||||
message:
|
||||
"Server returned HTML instead of expected response format",
|
||||
},
|
||||
};
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
||||
validateStatus: (response, result) => {
|
||||
return (
|
||||
response.status >= 200 && response.status < 300 && !result?.error
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
cancelOrder: builder.mutation({
|
||||
query: (orderId) => ({
|
||||
url: `/orders/${orderId}`,
|
||||
method: "DELETE",
|
||||
}),
|
||||
transformResponse: (response) => response.data || "Order cancelled",
|
||||
}),
|
||||
|
||||
getOrderTimes: builder.query({
|
||||
query: () => "/order-time",
|
||||
transformResponse: (response) => response.data || [],
|
||||
}),
|
||||
|
||||
getOrderPayments: builder.query({
|
||||
query: () => "/order-payments",
|
||||
transformResponse: (response) => response.data || [],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetOrdersQuery,
|
||||
useGetOrderByIdQuery,
|
||||
usePlaceOrderMutation,
|
||||
useCancelOrderMutation,
|
||||
useGetOrderTimesQuery,
|
||||
useGetOrderPaymentsQuery,
|
||||
} = orderApi;
|
||||
22
src/app/api/reviewApi.js
Normal file
22
src/app/api/reviewApi.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const reviewsApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
submitReview: builder.mutation({
|
||||
query: ({ productId, rating, title, source }) => ({
|
||||
url: `/products/${productId}/reviews`,
|
||||
method: "POST",
|
||||
body: { rating: rating, title: title, source: source },
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
getReviewsByProduct: builder.query({
|
||||
query: (productId) => `/reviews?product_id=${productId}`,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useSubmitReviewMutation, useGetReviewsByProductQuery } =
|
||||
reviewsApi;
|
||||
21
src/app/api/searchApi.js
Normal file
21
src/app/api/searchApi.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const searchApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
searchProduct: builder.query({
|
||||
query: (query) => ({
|
||||
url: "/search-product",
|
||||
params: { q: query },
|
||||
}),
|
||||
}),
|
||||
searchProductByBarcode: builder.query({
|
||||
query: (barcode) => ({
|
||||
url: "/search-product-barcode",
|
||||
params: { barcode },
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useSearchProductQuery, useSearchProductByBarcodeQuery } =
|
||||
searchApi;
|
||||
52
src/app/store.js
Normal file
52
src/app/store.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { baseApi } from "./api/baseApi";
|
||||
import { categoriesApi } from "./api/categories";
|
||||
import { searchApi } from "./api/searchApi";
|
||||
import { cartApi } from "./api/cartApi";
|
||||
import { brandsApi } from "./api/brandsApi";
|
||||
import { collectionsApi } from "./api/collectionsApi";
|
||||
import { favoritesApi } from "./api/favoritesApi";
|
||||
import { legalPagesApi } from "./api/legalPagesApi";
|
||||
import { locationApi } from "./api/locationApi";
|
||||
import { orderApi } from "./api/orderApi";
|
||||
import { mediaApi } from "./api/bannersApi";
|
||||
import { reviewsApi } from "./api/reviewApi";
|
||||
import { profileApi } from "./api/myProfileApi";
|
||||
import { contactApi } from "./api/contactUs";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
[baseApi.reducerPath]: baseApi.reducer,
|
||||
[categoriesApi.reducerPath]: categoriesApi.reducer,
|
||||
[searchApi.reducerPath]: searchApi.reducer,
|
||||
[cartApi.reducerPath]: cartApi.reducer,
|
||||
[brandsApi.reducerPath]: brandsApi.reducer,
|
||||
[collectionsApi.reducerPath]: collectionsApi.reducer,
|
||||
[favoritesApi.reducerPath]: favoritesApi.reducer,
|
||||
[legalPagesApi.reducerPath]: legalPagesApi.reducer,
|
||||
[locationApi.reducerPath]: locationApi.reducer,
|
||||
[orderApi.reducerPath]: orderApi.reducer,
|
||||
[mediaApi.reducerPath]: mediaApi.reducer,
|
||||
[reviewsApi.reducerPath]: reviewsApi.reducer,
|
||||
[profileApi.reducerPath]: profileApi.reducer,
|
||||
[contactApi.reducerPath]: contactApi.reducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(
|
||||
baseApi.middleware,
|
||||
categoriesApi.middleware,
|
||||
searchApi.middleware,
|
||||
brandsApi.middleware,
|
||||
collectionsApi.middleware,
|
||||
favoritesApi.middleware,
|
||||
legalPagesApi.middleware,
|
||||
locationApi.middleware,
|
||||
orderApi.middleware,
|
||||
reviewsApi.middleware,
|
||||
mediaApi.middleware,
|
||||
profileApi.middleware,
|
||||
contactApi.middleware,
|
||||
),
|
||||
});
|
||||
|
||||
export default store;
|
||||
Reference in New Issue
Block a user