first commit

This commit is contained in:
tusuii
2026-02-19 17:29:27 +05:30
commit 94d0aabec6
125 changed files with 14511 additions and 0 deletions

View File

View File

View File

View File

@@ -0,0 +1,64 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
// import axios from "axios";
import api from "../../app/api";
export const login = createAsyncThunk(
"auth/login",
async (credentials, thunkAPI) => {
try {
const response = await api.post("/auth/login", credentials);
console.log("Login response:", response.data);
const token = response.data?.token || response.data?.data?.token;
const user = response.data?.user || response.data?.data?.user;
if (!token) {
throw new Error("No token returned from server");
}
localStorage.setItem("token", token);
return { user, token };
} catch (error) {
return thunkAPI.rejectWithValue(
error.response?.data?.message || "Login failed"
);
}
}
);
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
loading: false,
error: null,
token: localStorage.getItem("token") || null,
},
reducers: {
logout: (state) => {
state.user = null;
state.token = null;
localStorage.removeItem("token");
},
},
extraReducers: (builder) => {
builder
.addCase(login.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(login.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload.user;
state.token = action.payload.token;
})
.addCase(login.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
},
});
export const { logout } = authSlice.actions;
export default authSlice.reducer;

View File

View File

@@ -0,0 +1,81 @@
// src/features/categories/categoryAPI.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const categoryApi = createApi({
reducerPath: "categoryApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:3000/api",
prepareHeaders: (headers, { getState }) => {
const token = getState().auth?.token;
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
tagTypes: ["Categories"],
endpoints: (builder) => ({
getCategoryTree: builder.query({
query: () => "/admin/tree",
providesTags: ["Categories"],
}),
addCategory: builder.mutation({
query: (body) => ({
url: "/admin/categories",
method: "POST",
body,
}),
invalidatesTags: ["Categories"],
}),
updateCategory: builder.mutation({
query: ({ id, ...body }) => ({
url: `/admin/categories/${id}`,
method: "PUT",
body,
}),
invalidatesTags: ["Categories"],
}),
getCategoryById: builder.query({
query: (id) => `/admin/categories/${id}`,
}),
updateCategoryStatus: builder.mutation({
query: ({ id, isActive }) => ({
url: `/admin/categories/${id}/status`,
method: "PATCH",
body: { isActive },
}),
invalidatesTags: ["Categories"],
}),
deleteCategory: builder.mutation({
query: (id) => ({
url: `/admin/categories/${id}`,
method: "DELETE",
}),
invalidatesTags: ["Categories"],
}),
reorderCategories: builder.mutation({
query: (orders) => ({
url: "/admin/categories/reorder",
method: "PATCH",
body: { orders },
}),
invalidatesTags: ["Categories"],
}),
}),
});
export const {
useGetCategoryTreeQuery,
useAddCategoryMutation,
useUpdateCategoryMutation,
useDeleteCategoryMutation,
useGetCategoryByIdQuery,
useUpdateCategoryStatusMutation,
useReorderCategoriesMutation,
} = categoryApi;
export default categoryApi;

View File

View File

@@ -0,0 +1,53 @@
// src/features/coupons/couponAPI.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const couponApi = createApi({
reducerPath: "couponApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:3000/api",
prepareHeaders: (headers, { getState }) => {
const token = getState().auth?.token;
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
tagTypes: ["Coupons"],
endpoints: (builder) => ({
getCoupons: builder.query({
query: () => "/admin/coupons",
transformResponse: (res) => res.data.coupons,
providesTags: ["Coupons"],
}),
// ✅ GET SINGLE COUPON (NEW)
getCouponById: builder.query({
query: (id) => `/admin/coupons/${id}`,
transformResponse: (res) => res.data, // 🔥 IMPORTANT
providesTags: ["Coupons"],
}),
createCoupon: builder.mutation({
query: (body) => ({
url: "/admin/coupons",
method: "POST",
body,
}),
invalidatesTags: ["Coupons"],
}),
toggleCouponStatus: builder.mutation({
query: (id) => ({
url: `/admin/coupons/${id}/toggle`,
method: "PATCH",
}),
invalidatesTags: ["Coupons"],
}),
}),
});
export const {
useGetCouponsQuery,
useGetCouponByIdQuery,
useCreateCouponMutation,
useToggleCouponStatusMutation,
} = couponApi;

View File

@@ -0,0 +1,123 @@
// import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
// import axios from "../../app/api";
// // ✅ Async thunk to fetch dashboard stats
// export const fetchDashboard = createAsyncThunk(
// "dashboard/fetchDashboard",
// async (_, { rejectWithValue }) => {
// try {
// const res = await axios.get("/admin/dashboard");
// return res.data.data;
// } catch (error) {
// return rejectWithValue(error.response?.data || error.message);
// }
// }
// );
// const dashboardSlice = createSlice({
// name: "dashboard",
// initialState: {
// stats: null,
// loading: false,
// error: null,
// },
// reducers: {},
// extraReducers: (builder) => {
// builder
// .addCase(fetchDashboard.pending, (state) => {
// state.loading = true;
// state.error = null;
// })
// .addCase(fetchDashboard.fulfilled, (state, action) => {
// state.loading = false;
// state.stats = action.payload;
// })
// .addCase(fetchDashboard.rejected, (state, action) => {
// state.loading = false;
// state.error = action.payload;
// });
// },
// });
// export default dashboardSlice.reducer;
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "../../app/api";
/* =====================================================
1⃣ Fetch Main Dashboard Data
Endpoint: GET /admin/dashboard
===================================================== */
export const fetchDashboard = createAsyncThunk(
"dashboard/fetchDashboard",
async (_, { rejectWithValue }) => {
try {
const res = await axios.get("/admin/dashboard");
return res.data.data;
} catch (error) {
return rejectWithValue(error.response?.data?.message || error.message);
}
},
);
/* =====================================================
2⃣ Fetch Coupon Statistics
Endpoint: GET /admin/stats
===================================================== */
export const fetchCouponStats = createAsyncThunk(
"dashboard/fetchCouponStats",
async (_, { rejectWithValue }) => {
try {
const res = await axios.get("/admin/stats");
return res.data.data;
} catch (error) {
return rejectWithValue(error.response?.data?.message || error.message);
}
},
);
/* =====================================================
Dashboard Slice
===================================================== */
const dashboardSlice = createSlice({
name: "dashboard",
initialState: {
stats: null, // data from /admin/dashboard
couponStats: null, // data from /admin/stats
loading: false,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
/* ===== DASHBOARD DATA ===== */
.addCase(fetchDashboard.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchDashboard.fulfilled, (state, action) => {
state.loading = false;
state.stats = action.payload;
})
.addCase(fetchDashboard.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
})
/* ===== COUPON STATS ===== */
.addCase(fetchCouponStats.pending, (state) => {
state.loading = true;
})
.addCase(fetchCouponStats.fulfilled, (state, action) => {
state.loading = false;
state.couponStats = action.payload;
})
.addCase(fetchCouponStats.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
},
});
export default dashboardSlice.reducer;

View File

@@ -0,0 +1,107 @@
// src/features/orders/orderApi.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const orderApi = createApi({
reducerPath: "orderApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL,
prepareHeaders: (headers, { getState }) => {
const token = getState().auth?.token;
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
endpoints: (builder) => ({
getOrders: builder.query({
query: ({ page = 1, limit = 10 }) =>
`/admin/orders?page=${page}&limit=${limit}`,
providesTags: ["Orders"],
}),
// ✅ ADD THIS
getOrderById: builder.query({
query: (id) => `/admin/orders/${id}`,
providesTags: ["Orders"],
}),
// =====================================================
// UPDATE ORDER STATUS (Delivery Admin)
// Used in: Order Details Page (Update to SHIPPED / DELIVERED etc.)
// =====================================================
updateOrderStatus: builder.mutation({
query: ({ id, status, trackingNumber }) => ({
url: `/delivery/admin/${id}/status`,
method: "PUT",
body: {
status,
trackingNumber,
},
}),
invalidatesTags: ["Orders"], // better
}),
getOrderHistory: builder.query({
query: (id) => `/admin/${id}/history`,
}),
// REQUESTED APPROVED REJECTEDCOMPLETED
// =====================================================
// RETURN REQUESTS (Admin - Pending / Approved / Rejected)
// Used in: Admin → Returns → Requests List
// Status: REQUESTED | APPROVED | REJECTED
// =====================================================
getReturnRequests: builder.query({
query: ({ page = 1, limit = 10 }) =>
`/orders/admin/returns?page=${page}&limit=${limit}`,
providesTags: ["Returns"],
}),
// =====================================================
// RETURN HISTORY (Admin - Completed / All Return Records)
// Used in: Admin → Returns → History Page
// =====================================================
getReturnedOrders: builder.query({
query: ({ page = 1, limit = 10 }) =>
`/orders/admin/returns/list?page=${page}&limit=${limit}`,
providesTags: ["Returns"],
}),
// =====================================================
// RETURN DETAILS (Single Return Order)
// Used in: Admin → Returns → View Details Page
// URL: /returns/:id
// =====================================================
getReturnRequestById: builder.query({
query: (id) => `/orders/admin/returns/${id}`,
providesTags: (result, error, id) => [{ type: "Returns", id }],
}),
// =====================================================
// UPDATE RETURN STATUS
// Used in: ReturnDetails page (Approve / Reject buttons)
// action = "APPROVE" | "REJECT"
// =====================================================
updateReturnStatus: builder.mutation({
query: ({ id, action }) => ({
url: `/${id}/return/status`,
method: "PUT",
body: { action }, // action = "APPROVE" | "REJECT"
}),
invalidatesTags: ["Returns"],
}),
}),
});
export const {
useGetOrdersQuery,
useGetOrderByIdQuery,
useGetReturnRequestsQuery,
useGetReturnedOrdersQuery,
useGetReturnRequestByIdQuery,
useUpdateReturnStatusMutation,
useUpdateOrderStatusMutation,
useGetOrderHistoryQuery,
} = orderApi;

View File

View File

@@ -0,0 +1,23 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
const productApi = createApi({
reducerPath: "productApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:5000/api",
prepareHeaders: (headers, { getState }) => {
const token = getState().auth?.token;
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
tagTypes: ["Products"],
endpoints: (builder) => ({
getProducts: builder.query({
query: ({ page = 1, limit = 10 } = {}) => `/admin/products?page=${page}&limit=${limit}`,
providesTags: ["Products"],
}),
}),
});
export const { useGetProductsQuery } = productApi;
export default productApi;

View File

View File

View File

@@ -0,0 +1,21 @@
// src/features/reports/customersAPI.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const customersApi = createApi({
reducerPath: "customersApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:5000/api",
prepareHeaders: (headers) => {
const token = localStorage.getItem("token");
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
endpoints: (builder) => ({
getCustomersReport: builder.query({
query: () => "/admin/reports/customers",
}),
}),
});
export const { useGetCustomersReportQuery } = customersApi;

View File

@@ -0,0 +1,21 @@
// src/features/reports/inventoryAPI.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const inventoryApi = createApi({
reducerPath: "inventoryApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:5000/api",
prepareHeaders: (headers) => {
const token = localStorage.getItem("token");
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
endpoints: (builder) => ({
getInventoryStats: builder.query({
query: () => "/admin/reports/inventory",
}),
}),
});
export const { useGetInventoryStatsQuery } = inventoryApi;

View File

@@ -0,0 +1,22 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const ordersApi = createApi({
reducerPath: "ordersApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:5000/api",
prepareHeaders: (headers) => {
const token = localStorage.getItem("token");
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
return headers;
},
}),
endpoints: (builder) => ({
getOrdersReport: builder.query({
query: () => "/admin/reports/orders",
}),
}),
});
export const { useGetOrdersReportQuery } = ordersApi;

View File

@@ -0,0 +1,20 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const salesApi = createApi({
reducerPath: "salesApi",
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "http://localhost:5000/api",
prepareHeaders: (headers) => {
const token = localStorage.getItem("token");
if (token) headers.set("Authorization", `Bearer ${token}`);
return headers;
},
}),
endpoints: (builder) => ({
getSalesReport: builder.query({
query: () => "/admin/reports/sales",
}),
}),
});
export const { useGetSalesReportQuery } = salesApi;

View File

View File