add superadmin authentication
This commit is contained in:
parent
a40a1994e5
commit
0656cda2ec
@ -1,19 +1,16 @@
|
|||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import Scrollbars from "react-custom-scrollbars-2";
|
import Scrollbars from "react-custom-scrollbars-2";
|
||||||
// import { useSelector } from "react-redux";
|
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { SidebarData } from "../../core/json/siderbar_data";
|
import { SidebarData } from "../../core/json/siderbar_data";
|
||||||
import HorizontalSidebar from "./horizontalSidebar";
|
|
||||||
import CollapsedSidebar from "./collapsedSidebar";
|
import CollapsedSidebar from "./collapsedSidebar";
|
||||||
|
import HorizontalSidebar from "./horizontalSidebar";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
// const SidebarData = useSelector((state) => state.sidebar_data);
|
const { user } = useSelector((state) => state.auth);
|
||||||
// console.log(sidebarData, "sidebar");
|
|
||||||
|
|
||||||
const Location = useLocation();
|
const Location = useLocation();
|
||||||
|
|
||||||
console.log("Location.pathname", Location.pathname);
|
|
||||||
|
|
||||||
const [subOpen, setSubopen] = useState("");
|
const [subOpen, setSubopen] = useState("");
|
||||||
const [subsidebar, setSubsidebar] = useState("");
|
const [subsidebar, setSubsidebar] = useState("");
|
||||||
|
|
||||||
@ -46,6 +43,12 @@ const Sidebar = () => {
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{mainLabel?.submenuItems?.map((title, i) => {
|
{mainLabel?.submenuItems?.map((title, i) => {
|
||||||
|
if (title?.role) {
|
||||||
|
if (user?.role !== title?.role) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let link_array = [];
|
let link_array = [];
|
||||||
title?.submenuItems?.map((link) => {
|
title?.submenuItems?.map((link) => {
|
||||||
link_array?.push(link?.link);
|
link_array?.push(link?.link);
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export const all_routes = {
|
|||||||
floatinglabel: "/form-floating-labels",
|
floatinglabel: "/form-floating-labels",
|
||||||
formvalidation: "/form-validation",
|
formvalidation: "/form-validation",
|
||||||
select2: "/form-select2",
|
select2: "/form-select2",
|
||||||
|
companylist: "/company-list",
|
||||||
toasts: "/ui-toasts",
|
toasts: "/ui-toasts",
|
||||||
video: "/ui-video",
|
video: "/ui-video",
|
||||||
sweetalerts: "/ui-sweetalerts",
|
sweetalerts: "/ui-sweetalerts",
|
||||||
@ -81,7 +81,7 @@ export const all_routes = {
|
|||||||
typicons: "/icon-typicon",
|
typicons: "/icon-typicon",
|
||||||
flagicons: "/icon-flag",
|
flagicons: "/icon-flag",
|
||||||
ribbon: "/ui-ribbon",
|
ribbon: "/ui-ribbon",
|
||||||
|
paymentmethodlist: "/payment-method-list",
|
||||||
chat: "/chat",
|
chat: "/chat",
|
||||||
videocall: "/video-call",
|
videocall: "/video-call",
|
||||||
audiocall: "/audio-call",
|
audiocall: "/audio-call",
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
import React from "react";
|
import { useSelector } from "react-redux";
|
||||||
import { Route, Routes } from "react-router-dom";
|
import { Outlet, Route, Routes } from "react-router-dom";
|
||||||
import Header from "../InitialPage/Sidebar/Header";
|
import Header from "../InitialPage/Sidebar/Header";
|
||||||
import Sidebar from "../InitialPage/Sidebar/Sidebar";
|
import Sidebar from "../InitialPage/Sidebar/Sidebar";
|
||||||
import { pagesRoute, posRoutes, publicRoutes } from "./router.link";
|
|
||||||
import { Outlet } from "react-router-dom";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import ThemeSettings from "../InitialPage/themeSettings";
|
import ThemeSettings from "../InitialPage/themeSettings";
|
||||||
import ProtectedRoute from "../components/ProtectedRoute";
|
import ProtectedRoute from "../components/ProtectedRoute";
|
||||||
|
import { pagesRoute, posRoutes, publicRoutes } from "./router.link";
|
||||||
// import CollapsedSidebar from "../InitialPage/Sidebar/collapsedSidebar";
|
// import CollapsedSidebar from "../InitialPage/Sidebar/collapsedSidebar";
|
||||||
|
import GuestRoute from "../components/GuestRoute";
|
||||||
import Loader from "../feature-module/loader/loader";
|
import Loader from "../feature-module/loader/loader";
|
||||||
// import HorizontalSidebar from "../InitialPage/Sidebar/horizontalSidebar";
|
// import HorizontalSidebar from "../InitialPage/Sidebar/horizontalSidebar";
|
||||||
//import LoadingSpinner from "../InitialPage/Sidebar/LoadingSpinner";
|
//import LoadingSpinner from "../InitialPage/Sidebar/LoadingSpinner";
|
||||||
|
|
||||||
const AllRoutes = () => {
|
const AllRoutes = () => {
|
||||||
|
const { user } = useSelector((state) => state.auth);
|
||||||
const data = useSelector((state) => state.toggle_header);
|
const data = useSelector((state) => state.toggle_header);
|
||||||
// const layoutStyles = useSelector((state) => state.layoutstyledata);
|
// const layoutStyles = useSelector((state) => state.layoutstyledata);
|
||||||
const HeaderLayout = () => (
|
const HeaderLayout = () => (
|
||||||
@ -57,6 +57,7 @@ const AllRoutes = () => {
|
|||||||
<Route path={route.path} element={route.element} key={id} />
|
<Route path={route.path} element={route.element} key={id} />
|
||||||
))}
|
))}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path={"/"}
|
path={"/"}
|
||||||
element={
|
element={
|
||||||
@ -65,12 +66,20 @@ const AllRoutes = () => {
|
|||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{publicRoutes.map((route, id) => (
|
{publicRoutes.map((route, id) => {
|
||||||
<Route path={route.path} element={route.element} key={id} />
|
if (route?.role && route?.role !== user?.role) return null;
|
||||||
))}
|
return <Route path={route.path} element={route.element} key={id} />;
|
||||||
|
})}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={"/"} element={<Authpages />}>
|
<Route
|
||||||
|
path={"/"}
|
||||||
|
element={
|
||||||
|
<GuestRoute>
|
||||||
|
<Authpages />
|
||||||
|
</GuestRoute>
|
||||||
|
}
|
||||||
|
>
|
||||||
{pagesRoute.map((route, id) => (
|
{pagesRoute.map((route, id) => (
|
||||||
<Route path={route.path} element={route.element} key={id} />
|
<Route path={route.path} element={route.element} key={id} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -204,6 +204,9 @@ import AddWeddingGuest from "../feature-module/inventory/addWeddingGuest";
|
|||||||
import ProductList2 from "../feature-module/inventory/productlist2";
|
import ProductList2 from "../feature-module/inventory/productlist2";
|
||||||
import ProductList3 from "../feature-module/inventory/productlist3";
|
import ProductList3 from "../feature-module/inventory/productlist3";
|
||||||
import { all_routes } from "./all_routes";
|
import { all_routes } from "./all_routes";
|
||||||
|
import PaymentMethodList from "../feature-module/FinanceAccounts/paymentmethodlist";
|
||||||
|
import CompanyList from "../feature-module/superadmin/companylist";
|
||||||
|
|
||||||
export const publicRoutes = [
|
export const publicRoutes = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -1488,7 +1491,23 @@ export const publicRoutes = [
|
|||||||
element: <Navigate to="/" />,
|
element: <Navigate to="/" />,
|
||||||
route: Route,
|
route: Route,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 120,
|
||||||
|
path: routes.paymentmethodlist,
|
||||||
|
name: "paymendMethodList",
|
||||||
|
element: <PaymentMethodList /> ,
|
||||||
|
route: Route,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
path: routes.companylist,
|
||||||
|
name: "companies",
|
||||||
|
element: <CompanyList />,
|
||||||
|
route: Route,
|
||||||
|
role: 'superadmin'
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const posRoutes = [
|
export const posRoutes = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
15
src/components/GuestRoute.jsx
Normal file
15
src/components/GuestRoute.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const GuestRoute = ({ children }) => {
|
||||||
|
const authState = useSelector((state) => state.auth);
|
||||||
|
const isAuthenticated = authState?.isAuthenticated || authState?.token;
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
return <Navigate to="/" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GuestRoute;
|
||||||
@ -1,17 +1,12 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Navigate } from 'react-router-dom';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
const ProtectedRoute = ({ children }) => {
|
const ProtectedRoute = ({ children }) => {
|
||||||
// Check if user is authenticated using Redux state
|
// Check if user is authenticated using Redux state
|
||||||
const authState = useSelector((state) => state.auth);
|
const authState = useSelector((state) => state.auth);
|
||||||
const isAuthenticated = authState?.isAuthenticated || authState?.token;
|
const isAuthenticated = authState?.isAuthenticated || authState?.token;
|
||||||
|
|
||||||
// Fallback to localStorage check
|
|
||||||
const localStorageAuth = localStorage.getItem('authToken') || localStorage.getItem('user');
|
|
||||||
const isUserAuthenticated = isAuthenticated || localStorageAuth;
|
|
||||||
|
|
||||||
if (!isUserAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
// Redirect to login page if not authenticated
|
// Redirect to login page if not authenticated
|
||||||
return <Navigate to="/signin" replace />;
|
return <Navigate to="/signin" replace />;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
132
src/core/redux/actions/organizationActions.js
Normal file
132
src/core/redux/actions/organizationActions.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { organizationsApi } from '../../../services/organizationsApi';
|
||||||
|
|
||||||
|
// Action Types
|
||||||
|
export const ORGANIZATION_ACTIONS = {
|
||||||
|
FETCH_ORGANIZATIONS_REQUEST: 'FETCH_ORGANIZATIONS_REQUEST',
|
||||||
|
FETCH_ORGANIZATIONS_SUCCESS: 'FETCH_ORGANIZATIONS_SUCCESS',
|
||||||
|
FETCH_ORGANIZATIONS_FAILURE: 'FETCH_ORGANIZATIONS_FAILURE',
|
||||||
|
|
||||||
|
FETCH_ORGANIZATION_REQUEST: 'FETCH_ORGANIZATION_REQUEST',
|
||||||
|
FETCH_ORGANIZATION_SUCCESS: 'FETCH_ORGANIZATION_SUCCESS',
|
||||||
|
FETCH_ORGANIZATION_FAILURE: 'FETCH_ORGANIZATION_FAILURE',
|
||||||
|
|
||||||
|
CREATE_ORGANIZATION_REQUEST: 'CREATE_ORGANIZATION_REQUEST',
|
||||||
|
CREATE_ORGANIZATION_SUCCESS: 'CREATE_ORGANIZATION_SUCCESS',
|
||||||
|
CREATE_ORGANIZATION_FAILURE: 'CREATE_ORGANIZATION_FAILURE',
|
||||||
|
|
||||||
|
UPDATE_ORGANIZATION_REQUEST: 'UPDATE_ORGANIZATION_REQUEST',
|
||||||
|
UPDATE_ORGANIZATION_SUCCESS: 'UPDATE_ORGANIZATION_SUCCESS',
|
||||||
|
UPDATE_ORGANIZATION_FAILURE: 'UPDATE_ORGANIZATION_FAILURE',
|
||||||
|
|
||||||
|
DELETE_ORGANIZATION_REQUEST: 'DELETE_ORGANIZATION_REQUEST',
|
||||||
|
DELETE_ORGANIZATION_SUCCESS: 'DELETE_ORGANIZATION_SUCCESS',
|
||||||
|
DELETE_ORGANIZATION_FAILURE: 'DELETE_ORGANIZATION_FAILURE',
|
||||||
|
|
||||||
|
CLEAR_ORGANIZATION_ERROR: 'CLEAR_ORGANIZATION_ERROR',
|
||||||
|
CLEAR_CURRENT_ORGANIZATION: 'CLEAR_CURRENT_ORGANIZATION',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Action Creators
|
||||||
|
|
||||||
|
export const fetchOrganizations = (params = {}) => async (dispatch) => {
|
||||||
|
dispatch({ type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_REQUEST });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await organizationsApi.getAllOrganizations(params);
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message || 'Failed to fetch organizations',
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchOrganization = (id) => async (dispatch) => {
|
||||||
|
dispatch({ type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_REQUEST });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await organizationsApi.getOrganizationById(id);
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message || 'Failed to fetch organization',
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createOrganization = (organizationData) => async (dispatch) => {
|
||||||
|
dispatch({ type: ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_REQUEST });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await organizationsApi.createOrganization(organizationData);
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message || 'Failed to create organization',
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateOrganization = (id, organizationData) => async (dispatch) => {
|
||||||
|
dispatch({ type: ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_REQUEST });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await organizationsApi.updateOrganization(id, organizationData);
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_SUCCESS,
|
||||||
|
payload: { id, data },
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message || 'Failed to update organization',
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteOrganization = (id) => async (dispatch) => {
|
||||||
|
dispatch({ type: ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_REQUEST });
|
||||||
|
|
||||||
|
try {
|
||||||
|
await organizationsApi.deleteOrganization(id);
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_SUCCESS,
|
||||||
|
payload: id,
|
||||||
|
});
|
||||||
|
return id;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message || 'Failed to delete organization',
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearOrganizationError = () => ({
|
||||||
|
type: ORGANIZATION_ACTIONS.CLEAR_ORGANIZATION_ERROR,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const clearCurrentOrganization = () => ({
|
||||||
|
type: ORGANIZATION_ACTIONS.CLEAR_CURRENT_ORGANIZATION,
|
||||||
|
});
|
||||||
126
src/core/redux/actions/paymentMethodActions.js
Normal file
126
src/core/redux/actions/paymentMethodActions.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { paymentMethodsApi } from '../../../services/paymentMethodsApi';
|
||||||
|
|
||||||
|
export const PAYMENT_METHOD_ACTIONS = {
|
||||||
|
FETCH_PAYMENT_METHODS_REQUEST: 'FETCH_PAYMENT_METHODS_REQUEST',
|
||||||
|
FETCH_PAYMENT_METHODS_SUCCESS: 'FETCH_PAYMENT_METHODS_SUCCESS',
|
||||||
|
FETCH_PAYMENT_METHODS_FAILURE: 'FETCH_PAYMENT_METHODS_FAILURE',
|
||||||
|
|
||||||
|
FETCH_PAYMENT_METHOD_REQUEST: 'FETCH_PAYMENT_METHOD_REQUEST',
|
||||||
|
FETCH_PAYMENT_METHOD_SUCCESS: 'FETCH_PAYMENT_METHOD_SUCCESS',
|
||||||
|
FETCH_PAYMENT_METHOD_FAILURE: 'FETCH_PAYMENT_METHOD_FAILURE',
|
||||||
|
|
||||||
|
CREATE_PAYMENT_METHOD_REQUEST: 'CREATE_PAYMENT_METHOD_REQUEST',
|
||||||
|
CREATE_PAYMENT_METHOD_SUCCESS: 'CREATE_PAYMENT_METHOD_SUCCESS',
|
||||||
|
CREATE_PAYMENT_METHOD_FAILURE: 'CREATE_PAYMENT_METHOD_FAILURE',
|
||||||
|
|
||||||
|
UPDATE_PAYMENT_METHOD_REQUEST: 'UPDATE_PAYMENT_METHOD_REQUEST',
|
||||||
|
UPDATE_PAYMENT_METHOD_SUCCESS: 'UPDATE_PAYMENT_METHOD_SUCCESS',
|
||||||
|
UPDATE_PAYMENT_METHOD_FAILURE: 'UPDATE_PAYMENT_METHOD_FAILURE',
|
||||||
|
|
||||||
|
DELETE_PAYMENT_METHOD_REQUEST: 'DELETE_PAYMENT_METHOD_REQUEST',
|
||||||
|
DELETE_PAYMENT_METHOD_SUCCESS: 'DELETE_PAYMENT_METHOD_SUCCESS',
|
||||||
|
DELETE_PAYMENT_METHOD_FAILURE: 'DELETE_PAYMENT_METHOD_FAILURE',
|
||||||
|
|
||||||
|
CLEAR_PAYMENT_METHOD_ERROR: 'CLEAR_PAYMENT_METHOD_ERROR',
|
||||||
|
CLEAR_CURRENT_PAYMENT_METHOD: 'CLEAR_CURRENT_PAYMENT_METHOD',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Action Creators
|
||||||
|
|
||||||
|
export const fetchPaymentMethods = (params = {}) => async (dispatch) => {
|
||||||
|
dispatch({ type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_REQUEST });
|
||||||
|
try {
|
||||||
|
const data = await paymentMethodsApi.getAll(params);
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPaymentMethod = (id) => async (dispatch) => {
|
||||||
|
dispatch({ type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_REQUEST });
|
||||||
|
try {
|
||||||
|
const data = await paymentMethodsApi.getById(id);
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPaymentMethod = (formData) => async (dispatch) => {
|
||||||
|
dispatch({ type: PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_REQUEST });
|
||||||
|
try {
|
||||||
|
const data = await paymentMethodsApi.create(formData);
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_SUCCESS,
|
||||||
|
payload: data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updatePaymentMethod = (id, formData) => async (dispatch) => {
|
||||||
|
dispatch({ type: PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_REQUEST });
|
||||||
|
try {
|
||||||
|
const data = await paymentMethodsApi.update(id, formData);
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_SUCCESS,
|
||||||
|
payload: { id, data },
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePaymentMethod = (id) => async (dispatch) => {
|
||||||
|
dispatch({ type: PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_REQUEST });
|
||||||
|
try {
|
||||||
|
await paymentMethodsApi.remove(id);
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_SUCCESS,
|
||||||
|
payload: id,
|
||||||
|
});
|
||||||
|
return id;
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_FAILURE,
|
||||||
|
payload: error.response?.data?.message || error.message,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearPaymentMethodError = () => ({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.CLEAR_PAYMENT_METHOD_ERROR,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const clearCurrentPaymentMethod = () => ({
|
||||||
|
type: PAYMENT_METHOD_ACTIONS.CLEAR_CURRENT_PAYMENT_METHOD,
|
||||||
|
});
|
||||||
@ -4,6 +4,8 @@ import productReducer from './reducers/productReducer';
|
|||||||
import authReducer from './reducers/authReducer';
|
import authReducer from './reducers/authReducer';
|
||||||
import categoryReducer from './reducers/categoryReducer';
|
import categoryReducer from './reducers/categoryReducer';
|
||||||
import orderReducer from './reducers/orderReducer';
|
import orderReducer from './reducers/orderReducer';
|
||||||
|
import paymentMethodReducer from './reducers/paymentMethodReducer';
|
||||||
|
import organizationReducer from './reducers/organizationReducer';
|
||||||
|
|
||||||
// Legacy reducer for existing functionality
|
// Legacy reducer for existing functionality
|
||||||
const legacyReducer = (state = initialState, action) => {
|
const legacyReducer = (state = initialState, action) => {
|
||||||
@ -79,6 +81,8 @@ const rootReducer = combineReducers({
|
|||||||
auth: authReducer,
|
auth: authReducer,
|
||||||
categories: categoryReducer,
|
categories: categoryReducer,
|
||||||
orders: orderReducer,
|
orders: orderReducer,
|
||||||
|
paymentMethods: paymentMethodReducer,
|
||||||
|
organizations: organizationReducer
|
||||||
});
|
});
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
|||||||
212
src/core/redux/reducers/organizationReducer.js
Normal file
212
src/core/redux/reducers/organizationReducer.js
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import { ORGANIZATION_ACTIONS } from '../actions/organizationActions';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
// Organizations list
|
||||||
|
organizations: [],
|
||||||
|
totalOrganizations: 0,
|
||||||
|
currentPage: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
hasPrevious: false,
|
||||||
|
hasNext: false,
|
||||||
|
|
||||||
|
// Current organization (for edit/view)
|
||||||
|
currentOrganization: null,
|
||||||
|
|
||||||
|
// Search results
|
||||||
|
searchResults: [],
|
||||||
|
searchQuery: '',
|
||||||
|
|
||||||
|
// Loading states
|
||||||
|
loading: false,
|
||||||
|
organizationLoading: false,
|
||||||
|
searchLoading: false,
|
||||||
|
|
||||||
|
// Error states
|
||||||
|
error: null,
|
||||||
|
organizationError: null,
|
||||||
|
searchError: null,
|
||||||
|
|
||||||
|
// Operation states
|
||||||
|
creating: false,
|
||||||
|
updating: false,
|
||||||
|
deleting: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const organizationReducer = (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
// Fetch Organizations
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_SUCCESS: {
|
||||||
|
const { organizations, total_count, page, total_pages, limit } = action.payload.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
organizations: organizations,
|
||||||
|
totalOrganizations: total_count || organizations.length,
|
||||||
|
currentPage: page || 1,
|
||||||
|
totalPages: total_pages || 1,
|
||||||
|
pageSize: limit || 10,
|
||||||
|
hasPrevious: false,
|
||||||
|
hasNext: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATIONS_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
error: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch Single Organization
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
organizationLoading: true,
|
||||||
|
organizationError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
organizationLoading: false,
|
||||||
|
currentOrganization: action.payload.data,
|
||||||
|
organizationError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.FETCH_ORGANIZATION_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
organizationLoading: false,
|
||||||
|
organizationError: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Organization
|
||||||
|
case ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
creating: true,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
creating: false,
|
||||||
|
organizations: [action.payload.data, ...state.organizations],
|
||||||
|
totalOrganizations: state.totalOrganizations + 1,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.CREATE_ORGANIZATION_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
creating: false,
|
||||||
|
error: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update Organization
|
||||||
|
case ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: true,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: false,
|
||||||
|
organizations: state.organizations.map(org =>
|
||||||
|
org.id === action.payload.data.id ? action.payload.data : org
|
||||||
|
),
|
||||||
|
currentOrganization: action.payload.data,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.UPDATE_ORGANIZATION_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: false,
|
||||||
|
error: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete Organization
|
||||||
|
case ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deleting: true,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deleting: false,
|
||||||
|
organizations: state.organizations.filter(org => org.id !== action.payload),
|
||||||
|
totalOrganizations: state.totalOrganizations - 1,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.DELETE_ORGANIZATION_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deleting: false,
|
||||||
|
error: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Search Organizations
|
||||||
|
case ORGANIZATION_ACTIONS.SEARCH_ORGANIZATIONS_REQUEST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
searchLoading: true,
|
||||||
|
searchError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.SEARCH_ORGANIZATIONS_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
searchLoading: false,
|
||||||
|
searchResults: action.payload.data || action.payload,
|
||||||
|
searchQuery: action.payload.query || '',
|
||||||
|
searchError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.SEARCH_ORGANIZATIONS_FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
searchLoading: false,
|
||||||
|
searchError: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear States
|
||||||
|
case ORGANIZATION_ACTIONS.CLEAR_ORGANIZATION_ERROR:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
error: null,
|
||||||
|
organizationError: null,
|
||||||
|
searchError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case ORGANIZATION_ACTIONS.CLEAR_CURRENT_ORGANIZATION:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentOrganization: null,
|
||||||
|
organizationError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default organizationReducer;
|
||||||
106
src/core/redux/reducers/paymentMethodReducer.js
Normal file
106
src/core/redux/reducers/paymentMethodReducer.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { PAYMENT_METHOD_ACTIONS } from '../actions/paymentMethodActions';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
paymentMethods: [],
|
||||||
|
totalPaymentMethods: 0,
|
||||||
|
currentPage: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
|
||||||
|
currentPaymentMethod: null,
|
||||||
|
|
||||||
|
loading: false,
|
||||||
|
creating: false,
|
||||||
|
updating: false,
|
||||||
|
deleting: false,
|
||||||
|
|
||||||
|
error: null,
|
||||||
|
detailError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const paymentMethodReducer = (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_REQUEST:
|
||||||
|
return { ...state, loading: true, error: null };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_SUCCESS: {
|
||||||
|
const { payment_methods, total_count, page, total_pages, limit } = action.payload.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
paymentMethods: payment_methods,
|
||||||
|
totalPaymentMethods: total_count || payment_methods.length,
|
||||||
|
currentPage: page || 1,
|
||||||
|
totalPages: total_pages || 1,
|
||||||
|
pageSize: limit || 10,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHODS_FAILURE:
|
||||||
|
return { ...state, loading: false, error: action.payload };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_REQUEST:
|
||||||
|
return { ...state, detailLoading: true, detailError: null };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_SUCCESS:
|
||||||
|
return { ...state, detailLoading: false, currentPaymentMethod: action.payload.data };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.FETCH_PAYMENT_METHOD_FAILURE:
|
||||||
|
return { ...state, detailLoading: false, detailError: action.payload };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_REQUEST:
|
||||||
|
return { ...state, creating: true };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
creating: false,
|
||||||
|
paymentMethods: [action.payload.data, ...state.paymentMethods],
|
||||||
|
totalPaymentMethods: state.totalPaymentMethods + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.CREATE_PAYMENT_METHOD_FAILURE:
|
||||||
|
return { ...state, creating: false, error: action.payload };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_REQUEST:
|
||||||
|
return { ...state, updating: true };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: false,
|
||||||
|
paymentMethods: state.paymentMethods.map((item) =>
|
||||||
|
item.id === action.payload.data.id ? action.payload.data : item
|
||||||
|
),
|
||||||
|
currentPaymentMethod: action.payload.data,
|
||||||
|
};
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.UPDATE_PAYMENT_METHOD_FAILURE:
|
||||||
|
return { ...state, updating: false, error: action.payload };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_REQUEST:
|
||||||
|
return { ...state, deleting: true };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
deleting: false,
|
||||||
|
paymentMethods: state.paymentMethods.filter((item) => item.id !== action.payload),
|
||||||
|
totalPaymentMethods: state.totalPaymentMethods - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.DELETE_PAYMENT_METHOD_FAILURE:
|
||||||
|
return { ...state, deleting: false, error: action.payload };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.CLEAR_PAYMENT_METHOD_ERROR:
|
||||||
|
return { ...state, error: null, detailError: null };
|
||||||
|
|
||||||
|
case PAYMENT_METHOD_ACTIONS.CLEAR_CURRENT_PAYMENT_METHOD:
|
||||||
|
return { ...state, currentPaymentMethod: null };
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default paymentMethodReducer;
|
||||||
387
src/feature-module/FinanceAccounts/paymentmethodlist.jsx
Normal file
387
src/feature-module/FinanceAccounts/paymentmethodlist.jsx
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
import { Select, Tag } from "antd";
|
||||||
|
import {
|
||||||
|
ChevronUp,
|
||||||
|
PlusCircle,
|
||||||
|
RotateCcw,
|
||||||
|
Trash2,
|
||||||
|
} from "feather-icons-react/build/IconComponents";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import CustomPagination from "../../components/CustomPagination";
|
||||||
|
import ImageWithBasePath from "../../core/img/imagewithbasebath";
|
||||||
|
import AddCategoryList from "../../core/modals/inventory/addcategorylist";
|
||||||
|
import EditCategoryList from "../../core/modals/inventory/editcategorylist";
|
||||||
|
import Table from "../../core/pagination/datatable";
|
||||||
|
import { setToogleHeader } from "../../core/redux/action";
|
||||||
|
import { deletePaymentMethod, fetchPaymentMethod, fetchPaymentMethods } from "../../core/redux/actions/paymentMethodActions";
|
||||||
|
import { formatDate } from "../../utils/date";
|
||||||
|
|
||||||
|
const PaymentMethodList = () => {
|
||||||
|
const {
|
||||||
|
paymentMethods: apiPaymentMethods,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
totalPaymentMethods,
|
||||||
|
totalPages,
|
||||||
|
pageSize: reduxPageSize,
|
||||||
|
currentPage: reduxCurrentPage,
|
||||||
|
} = useSelector((state) => state.paymentMethods);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const data = useSelector((state) => state.toggle_header);
|
||||||
|
const dataSource = apiPaymentMethods?.length > 0 ? apiPaymentMethods : [];
|
||||||
|
|
||||||
|
const [currentPage, setCurrentPage] = useState(reduxCurrentPage || 1);
|
||||||
|
const [pageSize, setPageSize] = useState(reduxPageSize || 10);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPaymentMethods = async () => {
|
||||||
|
try {
|
||||||
|
const searchParams = {
|
||||||
|
page: currentPage,
|
||||||
|
limit: pageSize,
|
||||||
|
search: debouncedSearchTerm || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove empty parameters
|
||||||
|
const cleanParams = Object.fromEntries(
|
||||||
|
Object.entries(searchParams).filter(([, value]) => value !== "")
|
||||||
|
);
|
||||||
|
|
||||||
|
await dispatch(fetchPaymentMethods(cleanParams));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load categories", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPaymentMethods();
|
||||||
|
}, [dispatch, currentPage, pageSize, debouncedSearchTerm]);
|
||||||
|
|
||||||
|
// Debounce search term
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setDebouncedSearchTerm(searchTerm);
|
||||||
|
}, 500); // 500ms delay
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [searchTerm]);
|
||||||
|
|
||||||
|
// Handle pagination
|
||||||
|
const handlePageChange = (page) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle page size change
|
||||||
|
const handlePageSizeChange = (newPageSize) => {
|
||||||
|
setPageSize(newPageSize);
|
||||||
|
setCurrentPage(1); // Reset to first page when changing page size
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setSearchTerm(value);
|
||||||
|
// Reset to first page when searching
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate pagination info
|
||||||
|
const totalRecords = totalPaymentMethods || dataSource.length;
|
||||||
|
const calculatedTotalPages = Math.ceil(totalRecords / pageSize);
|
||||||
|
const actualTotalPages = totalPages || calculatedTotalPages;
|
||||||
|
|
||||||
|
const handleDeletePaymentMethod = async (paymentMethodId) => {
|
||||||
|
try {
|
||||||
|
await dispatch(deletePaymentMethod(paymentMethodId));
|
||||||
|
// Show success message
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Deleted!",
|
||||||
|
text: "Payment Method has been deleted successfully.",
|
||||||
|
icon: "success",
|
||||||
|
className: "btn btn-success",
|
||||||
|
customClass: {
|
||||||
|
confirmButton: "btn btn-success",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to delete payment method:", error);
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Error!",
|
||||||
|
text: "Failed to delete payment method. Please try again.",
|
||||||
|
icon: "error",
|
||||||
|
className: "btn btn-danger",
|
||||||
|
customClass: {
|
||||||
|
confirmButton: "btn btn-danger",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTooltip = (props) => (
|
||||||
|
<Tooltip id="pdf-tooltip" {...props}>
|
||||||
|
Pdf
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderExcelTooltip = (props) => (
|
||||||
|
<Tooltip id="excel-tooltip" {...props}>
|
||||||
|
Excel
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderPrinterTooltip = (props) => (
|
||||||
|
<Tooltip id="printer-tooltip" {...props}>
|
||||||
|
Printer
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderRefreshTooltip = (props) => (
|
||||||
|
<Tooltip id="refresh-tooltip" {...props}>
|
||||||
|
Refresh
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderCollapseTooltip = (props) => (
|
||||||
|
<Tooltip id="refresh-tooltip" {...props}>
|
||||||
|
Collapse
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
const dateOptions = [
|
||||||
|
{ label: "Sort By: Last 7 Days", value: "last7days" },
|
||||||
|
{ label: "Sort By: Last Month", value: "lastmonth" },
|
||||||
|
{ label: "Sort By: Ascending", value: "ascending" },
|
||||||
|
{ label: "Sort By: Descending", value: "descending" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Payment Method",
|
||||||
|
dataIndex: "paymentmethod",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record.name}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.name.length - b.name.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Type",
|
||||||
|
dataIndex: "type",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record?.type}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.type.length - b.type.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Created On",
|
||||||
|
dataIndex: "createdon",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{formatDate(record.created_at)}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.created_at.length - b.created_at.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Status",
|
||||||
|
dataIndex: "status",
|
||||||
|
render: (_, record) => (
|
||||||
|
<Tag color="#87d068">{record.is_active ? "Active" : "Inactive"}</Tag>
|
||||||
|
),
|
||||||
|
sorter: (a, b) => a.status.length - b.status.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Actions",
|
||||||
|
dataIndex: "actions",
|
||||||
|
key: "actions",
|
||||||
|
render: (_, record) => (
|
||||||
|
<td className="action-table-data">
|
||||||
|
<div className="edit-delete-action">
|
||||||
|
<Link
|
||||||
|
className="me-2 p-2"
|
||||||
|
to="#"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#edit-category"
|
||||||
|
onClick={() => dispatch(fetchPaymentMethod(record.id))}
|
||||||
|
>
|
||||||
|
<i data-feather="edit" className="feather-edit"></i>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="confirm-text p-2"
|
||||||
|
to="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "You won't be able to revert this!",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#00ff00",
|
||||||
|
confirmButtonText: "Yes, delete it!",
|
||||||
|
cancelButtonColor: "#ff0000",
|
||||||
|
cancelButtonText: "Cancel",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
handleDeletePaymentMethod(record.id || record.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="feather-trash-2" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="page-wrapper">
|
||||||
|
<div className="content">
|
||||||
|
<div className="page-header">
|
||||||
|
<div className="add-item d-flex">
|
||||||
|
<div className="page-title">
|
||||||
|
<h4>Payment Method</h4>
|
||||||
|
<h6>Manage your payment methods</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className="table-top-head">
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderTooltip}>
|
||||||
|
<Link>
|
||||||
|
<ImageWithBasePath
|
||||||
|
src="assets/img/icons/pdf.svg"
|
||||||
|
alt="img"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderExcelTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<ImageWithBasePath
|
||||||
|
src="assets/img/icons/excel.svg"
|
||||||
|
alt="img"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderPrinterTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<i data-feather="printer" className="feather-printer" />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderRefreshTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<RotateCcw />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderCollapseTooltip}>
|
||||||
|
<Link
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-placement="top"
|
||||||
|
id="collapse-header"
|
||||||
|
className={data ? "active" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(setToogleHeader(!data));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChevronUp />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div className="page-btn">
|
||||||
|
<Link
|
||||||
|
to="#"
|
||||||
|
className="btn btn-added"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#add-category"
|
||||||
|
>
|
||||||
|
<PlusCircle className="me-2" />
|
||||||
|
Add New Category
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* /product list */}
|
||||||
|
<div className="card table-list-card">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="table-top">
|
||||||
|
<div className="search-set">
|
||||||
|
<div className="search-input">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
className="form-control form-control-sm formsearch"
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
<Link to className="btn btn-searchset">
|
||||||
|
<i data-feather="search" className="feather-search" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
style={{ height: 36 }}
|
||||||
|
defaultValue={dateOptions[0]?.value}
|
||||||
|
options={dateOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="table-responsive">
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-center p-4">
|
||||||
|
<div className="spinner-border text-primary" role="status">
|
||||||
|
<span className="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
<p className="mt-2">Loading categories...</p>
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="alert alert-danger" role="alert">
|
||||||
|
<strong>Error:</strong> {error}
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-danger ms-2"
|
||||||
|
onClick={() => dispatch(fetchPaymentMethods())}
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Table columns={columns} dataSource={dataSource} />
|
||||||
|
|
||||||
|
<CustomPagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
totalCount={totalRecords}
|
||||||
|
totalPages={actualTotalPages}
|
||||||
|
loading={loading}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onPageSizeChange={handlePageSizeChange}
|
||||||
|
pageSizeOptions={[10, 20, 50, 100]}
|
||||||
|
showInfo={true}
|
||||||
|
showPageSizeSelector={true}
|
||||||
|
compact={false}
|
||||||
|
className="paymentmethod-list-pagination"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* /category list */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AddCategoryList />
|
||||||
|
<EditCategoryList />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentMethodList;
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
Plus,
|
Plus,
|
||||||
PlusCircle,
|
PlusCircle,
|
||||||
Trash2,
|
Trash2,
|
||||||
X
|
X,
|
||||||
} from "feather-icons-react/build/IconComponents";
|
} from "feather-icons-react/build/IconComponents";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||||
@ -180,12 +180,19 @@ const AddProduct = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Prepare the data for submission
|
const isAllEmpty = variants.every(
|
||||||
|
(item) =>
|
||||||
|
item.name === "" && item.price_modifier === 0 && item.cost === 0
|
||||||
|
);
|
||||||
|
|
||||||
const productData = {
|
const productData = {
|
||||||
...formData,
|
...formData,
|
||||||
variants
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!isAllEmpty) {
|
||||||
|
productData.variants = variants;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove empty values
|
// Remove empty values
|
||||||
const cleanData = Object.fromEntries(
|
const cleanData = Object.fromEntries(
|
||||||
Object.entries(productData).filter(([, value]) => {
|
Object.entries(productData).filter(([, value]) => {
|
||||||
@ -222,7 +229,7 @@ const AddProduct = () => {
|
|||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: "error",
|
icon: "error",
|
||||||
title: "Error!",
|
title: "Error!",
|
||||||
text: error.message || "Failed to create product. Please try again.",
|
text: error?.response?.data?.errors[0].cause || "Failed to create product. Please try again.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -254,7 +261,7 @@ const AddProduct = () => {
|
|||||||
|
|
||||||
const handleChangeVariant = (index, field, value) => {
|
const handleChangeVariant = (index, field, value) => {
|
||||||
const newVariants = [...variants];
|
const newVariants = [...variants];
|
||||||
if (['price_modifier', 'cost'].includes(field)) value = Number(value);
|
if (["price_modifier", "cost"].includes(field)) value = Number(value);
|
||||||
newVariants[index][field] = value;
|
newVariants[index][field] = value;
|
||||||
setVariants(newVariants);
|
setVariants(newVariants);
|
||||||
};
|
};
|
||||||
@ -683,6 +690,7 @@ const AddProduct = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="tab-content" id="pills-tabContent">
|
<div className="tab-content" id="pills-tabContent">
|
||||||
<div
|
<div
|
||||||
className="tab-pane fade show active"
|
className="tab-pane fade show active"
|
||||||
@ -872,7 +880,10 @@ const AddProduct = () => {
|
|||||||
className="btn btn-primary mt-2"
|
className="btn btn-primary mt-2"
|
||||||
onClick={addVariant}
|
onClick={addVariant}
|
||||||
>
|
>
|
||||||
<Plus data-feather="plus" className="me-1 icon-small" />
|
<Plus
|
||||||
|
data-feather="plus"
|
||||||
|
className="me-1 icon-small"
|
||||||
|
/>
|
||||||
Add Variant
|
Add Variant
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -188,12 +188,19 @@ const EditProduct = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Prepare the data for submission
|
const isAllEmpty = variants.every(
|
||||||
|
(item) =>
|
||||||
|
item.name === "" && item.price_modifier === 0 && item.cost === 0
|
||||||
|
);
|
||||||
|
|
||||||
const productData = {
|
const productData = {
|
||||||
...formData,
|
...formData,
|
||||||
variants,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!isAllEmpty) {
|
||||||
|
productData.variants = variants;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove empty values
|
// Remove empty values
|
||||||
const cleanData = Object.fromEntries(
|
const cleanData = Object.fromEntries(
|
||||||
Object.entries(productData).filter(([, value]) => {
|
Object.entries(productData).filter(([, value]) => {
|
||||||
|
|||||||
@ -10,29 +10,28 @@ const Signin = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
// const dispatch = useDispatch();
|
// const dispatch = useDispatch();
|
||||||
const authState = useSelector((state) => state.auth);
|
const authState = useSelector((state) => state.auth);
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
email: '',
|
email: "",
|
||||||
password: ''
|
password: "",
|
||||||
});
|
});
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
[e.target.name]: e.target.value
|
[e.target.name]: e.target.value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setError('');
|
setError("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await authApi.login(formData);
|
authApi.login(formData).then(() => navigate(route.dashboard));
|
||||||
navigate(route.dashboard);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error.message || 'Login failed');
|
setError(error.message || "Login failed");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,12 +62,12 @@ const Signin = () => {
|
|||||||
<div className="form-login mb-3">
|
<div className="form-login mb-3">
|
||||||
<label className="form-label">Email Address</label>
|
<label className="form-label">Email Address</label>
|
||||||
<div className="form-addons">
|
<div className="form-addons">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<ImageWithBasePath
|
<ImageWithBasePath
|
||||||
@ -110,12 +109,12 @@ const Signin = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-login">
|
<div className="form-login">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-login"
|
className="btn btn-login"
|
||||||
disabled={authState.loading}
|
disabled={authState.loading}
|
||||||
>
|
>
|
||||||
{authState.loading ? 'Signing In...' : 'Sign In'}
|
{authState.loading ? "Signing In..." : "Sign In"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="signinform">
|
<div className="signinform">
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DatePicker, Space, Select as AntSelect } from "antd";
|
import { Select as AntSelect, DatePicker, Space } from "antd";
|
||||||
import {
|
import {
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
PlusCircle,
|
PlusCircle,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
fetchOrders,
|
fetchOrders,
|
||||||
} from "../../core/redux/actions/orderActions";
|
} from "../../core/redux/actions/orderActions";
|
||||||
import { formatRupiah } from "../../utils/currency";
|
import { formatRupiah } from "../../utils/currency";
|
||||||
import { formatDate } from "../../utils/date";
|
import { formatDate, formatInputDate } from "../../utils/date";
|
||||||
|
|
||||||
const SalesList = () => {
|
const SalesList = () => {
|
||||||
const {
|
const {
|
||||||
@ -35,25 +35,41 @@ const SalesList = () => {
|
|||||||
|
|
||||||
const dataSource = apiOrders?.length > 0 ? apiOrders : [];
|
const dataSource = apiOrders?.length > 0 ? apiOrders : [];
|
||||||
|
|
||||||
const [currentPage, setCurrentPage] = useState(reduxCurrentPage || 1);
|
const [params, setParams] = useState({
|
||||||
const [pageSize, setPageSize] = useState(reduxPageSize || 10);
|
page: reduxCurrentPage || 1,
|
||||||
|
limit: reduxPageSize || 10,
|
||||||
|
status: null,
|
||||||
|
date_from: null,
|
||||||
|
date_to: null,
|
||||||
|
});
|
||||||
|
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||||
const [orderStatus, setOrderStatus] = useState(null);
|
|
||||||
|
const [selectedOrder, setSelectedOrder] = useState(null);
|
||||||
|
|
||||||
|
const handleSetParams = (key, value) => {
|
||||||
|
setParams({
|
||||||
|
...params,
|
||||||
|
[key]: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadOrders = async () => {
|
const loadOrders = async () => {
|
||||||
try {
|
try {
|
||||||
const searchParams = {
|
const receivedParams = {
|
||||||
page: currentPage,
|
page: params.page,
|
||||||
limit: pageSize,
|
limit: params.limit,
|
||||||
search: debouncedSearchTerm || "",
|
search: debouncedSearchTerm || "",
|
||||||
status: orderStatus,
|
status: params.status,
|
||||||
|
date_from: params.date_from,
|
||||||
|
date_to: params.date_to,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove empty parameters
|
// Remove empty parameters
|
||||||
const cleanParams = Object.fromEntries(
|
const cleanParams = Object.fromEntries(
|
||||||
Object.entries(searchParams).filter(([, value]) => value !== "")
|
Object.entries(receivedParams).filter(([, value]) => value !== "")
|
||||||
);
|
);
|
||||||
|
|
||||||
await dispatch(fetchOrders(cleanParams));
|
await dispatch(fetchOrders(cleanParams));
|
||||||
@ -63,7 +79,7 @@ const SalesList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadOrders();
|
loadOrders();
|
||||||
}, [dispatch, currentPage, pageSize, debouncedSearchTerm, orderStatus]);
|
}, [dispatch, params, debouncedSearchTerm]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@ -78,30 +94,23 @@ const SalesList = () => {
|
|||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setSearchTerm(value);
|
setSearchTerm(value);
|
||||||
// Reset to first page when searching
|
// Reset to first page when searching
|
||||||
setCurrentPage(1);
|
handleSetParams("page", 1);
|
||||||
};
|
|
||||||
|
|
||||||
const handleFilterStatus = (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setOrderStatus(value);
|
|
||||||
|
|
||||||
setCurrentPage(1);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle pagination
|
// Handle pagination
|
||||||
const handlePageChange = (page) => {
|
const handlePageChange = (page) => {
|
||||||
setCurrentPage(page);
|
handleSetParams("page", page);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle page size change
|
// Handle page size change
|
||||||
const handlePageSizeChange = (newPageSize) => {
|
const handlePageSizeChange = (newPageSize) => {
|
||||||
setPageSize(newPageSize);
|
handleSetParams("limit", newPageSize);
|
||||||
setCurrentPage(1); // Reset to first page when changing page size
|
handleSetParams("page", 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate pagination info
|
// Calculate pagination info
|
||||||
const totalRecords = totalOrders || dataSource.length;
|
const totalRecords = totalOrders || dataSource.length;
|
||||||
const calculatedTotalPages = Math.ceil(totalRecords / pageSize);
|
const calculatedTotalPages = Math.ceil(totalRecords / params.limit);
|
||||||
const actualTotalPages = totalPages || calculatedTotalPages;
|
const actualTotalPages = totalPages || calculatedTotalPages;
|
||||||
|
|
||||||
// Clear error when component unmounts
|
// Clear error when component unmounts
|
||||||
@ -141,13 +150,6 @@ const SalesList = () => {
|
|||||||
cancelled: "danger",
|
cancelled: "danger",
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ label: "Sort By: Last 7 Days", value: "last7days" },
|
|
||||||
{ label: "Sort By: Last Month", value: "lastmonth" },
|
|
||||||
{ label: "Sort By: Ascending", value: "ascending" },
|
|
||||||
{ label: "Sort By: Descending", value: "descending" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||||
const handleDateChange = (date) => {
|
const handleDateChange = (date) => {
|
||||||
setSelectedDate(date);
|
setSelectedDate(date);
|
||||||
@ -272,23 +274,48 @@ const SalesList = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Space warp>
|
<Space warp align="center">
|
||||||
<AntSelect
|
<AntSelect
|
||||||
style={{ height: 36, width: 120 }}
|
style={{ height: 36, width: 120 }}
|
||||||
placeholder={"Status"}
|
placeholder={"Status"}
|
||||||
options={paymentStatus}
|
options={paymentStatus}
|
||||||
value={
|
value={
|
||||||
paymentStatus.find(
|
paymentStatus.find(
|
||||||
(option) => option.value === orderStatus
|
(option) => option.value === params.status
|
||||||
) || null
|
) || null
|
||||||
}
|
}
|
||||||
onChange={handleFilterStatus}
|
onChange={(selectedOption) =>
|
||||||
|
handleSetParams("status", selectedOption)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AntSelect
|
<DatePicker
|
||||||
|
selected={params.date_from}
|
||||||
|
onChange={(date) =>
|
||||||
|
date
|
||||||
|
? handleSetParams("date_from", formatInputDate(date))
|
||||||
|
: handleSetParams("date_from", "")
|
||||||
|
}
|
||||||
|
height={120}
|
||||||
|
type="date"
|
||||||
|
className="datetimepicker w-100"
|
||||||
|
dateFormat="dd-MM-yyyy"
|
||||||
|
placeholder="From Date"
|
||||||
|
style={{ height: 36 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DatePicker
|
||||||
|
selected={params.date_to}
|
||||||
|
onChange={(date) =>
|
||||||
|
date
|
||||||
|
? handleSetParams("date_to", formatInputDate(date))
|
||||||
|
: handleSetParams("date_to", "")
|
||||||
|
}
|
||||||
|
type="date"
|
||||||
|
className="datetimepicker w-100"
|
||||||
|
dateFormat="dd-MM-yyyy"
|
||||||
|
placeholder="To Date"
|
||||||
style={{ height: 36 }}
|
style={{ height: 36 }}
|
||||||
defaultValue={options[0]?.value}
|
|
||||||
options={options}
|
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
@ -376,12 +403,13 @@ const SalesList = () => {
|
|||||||
className="dropdown-item"
|
className="dropdown-item"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#sales-details-new"
|
data-bs-target="#sales-details-new"
|
||||||
|
onClick={() => setSelectedOrder(item)}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
data-feather="eye"
|
data-feather="eye"
|
||||||
className="info-img"
|
className="info-img"
|
||||||
/>
|
/>
|
||||||
Sale Detail
|
Sales Detail
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@ -455,8 +483,8 @@ const SalesList = () => {
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<CustomPagination
|
<CustomPagination
|
||||||
currentPage={currentPage}
|
currentPage={params.page}
|
||||||
pageSize={pageSize}
|
pageSize={params.limit}
|
||||||
totalCount={totalRecords}
|
totalCount={totalRecords}
|
||||||
totalPages={actualTotalPages}
|
totalPages={actualTotalPages}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@ -687,178 +715,135 @@ const SalesList = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* /add popup */}
|
{/* /add popup */}
|
||||||
|
|
||||||
{/* details popup */}
|
{/* details popup */}
|
||||||
<div className="modal fade" id="sales-details-new">
|
<div className="modal fade" id="sales-details-new">
|
||||||
<div className="modal-dialog sales-details-modal">
|
<div className="modal-dialog sales-details-modal">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
<div className="page-wrapper details-blk">
|
<div className="page-wrapper details-blk">
|
||||||
<div className="content p-4">
|
{setSelectedOrder && (
|
||||||
<div className="d-flex justify-content-between align-items-center mb-4 modal-header">
|
<div className="content p-4">
|
||||||
<h4 className="fw-bold">Sales Detail</h4>
|
<div className="d-flex justify-content-between align-items-center mb-4 modal-header">
|
||||||
<button className="btn btn-dark" data-bs-dismiss="modal">
|
<h4 className="fw-bold">Sales Detail</h4>
|
||||||
<i className="fa fa-arrow-left me-2"></i>Back to Sales
|
<button className="btn btn-dark" data-bs-dismiss="modal">
|
||||||
</button>
|
<i className="fa fa-arrow-left me-2"></i>Back to Sales
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<div className="row g-4 mb-4">
|
|
||||||
<div className="col-md-4">
|
|
||||||
<h6 className="fw-bold text-muted">Customer Info</h6>
|
|
||||||
<p className="mb-0">Carl Evans</p>
|
|
||||||
<small className="text-muted d-block">
|
|
||||||
3103 Trainer Avenue Peoria, IL 61602
|
|
||||||
</small>
|
|
||||||
<small className="text-muted d-block">
|
|
||||||
Email: carlevans241@example.com
|
|
||||||
</small>
|
|
||||||
<small className="text-muted d-block">
|
|
||||||
Phone: +1 987 471 6589
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">
|
|
||||||
<h6 className="fw-bold text-muted">Company Info</h6>
|
<div className="row g-4 mb-4">
|
||||||
<p className="mb-0">DGT</p>
|
<div className="col-md-4">
|
||||||
<small className="text-muted d-block">
|
<h6 className="fw-bold text-muted">Customer Info</h6>
|
||||||
2077 Chicago Avenue Orosi, CA 93647
|
<p className="mb-0">
|
||||||
</small>
|
{selectedOrder?.metadata?.customer_name}
|
||||||
<small className="text-muted d-block">
|
</p>
|
||||||
Email: admin@example.com
|
<small className="text-muted d-block">
|
||||||
</small>
|
3103 Trainer Avenue Peoria, IL 61602
|
||||||
<small className="text-muted d-block">
|
</small>
|
||||||
Phone: +1 893 174 0385
|
<small className="text-muted d-block">
|
||||||
</small>
|
Email: carlevans241@example.com
|
||||||
|
</small>
|
||||||
|
<small className="text-muted d-block">
|
||||||
|
Phone: +1 987 471 6589
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<h6 className="fw-bold text-muted">Company Info</h6>
|
||||||
|
<p className="mb-0">DGT</p>
|
||||||
|
<small className="text-muted d-block">
|
||||||
|
2077 Chicago Avenue Orosi, CA 93647
|
||||||
|
</small>
|
||||||
|
<small className="text-muted d-block">
|
||||||
|
Email: admin@example.com
|
||||||
|
</small>
|
||||||
|
<small className="text-muted d-block">
|
||||||
|
Phone: +1 893 174 0385
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<h6 className="fw-bold text-muted">Invoice Info</h6>
|
||||||
|
<small className="d-block">
|
||||||
|
Reference:{" "}
|
||||||
|
<span className="text-warning fw-semibold">
|
||||||
|
{selectedOrder?.order_number}
|
||||||
|
</span>
|
||||||
|
</small>
|
||||||
|
<small className="d-block">
|
||||||
|
Date: {formatDate(selectedOrder?.created_at)}
|
||||||
|
</small>
|
||||||
|
<small className="d-block">
|
||||||
|
Status:{" "}
|
||||||
|
<span
|
||||||
|
className={`badge text-bg-${
|
||||||
|
badgeColors[selectedOrder?.status]
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{selectedOrder?.status}
|
||||||
|
</span>
|
||||||
|
</small>
|
||||||
|
<small className="d-block">
|
||||||
|
Payment Status:{" "}
|
||||||
|
<span className="badge bg-light-success text-success">
|
||||||
|
Paid
|
||||||
|
</span>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">
|
|
||||||
<h6 className="fw-bold text-muted">Invoice Info</h6>
|
|
||||||
<small className="d-block">
|
|
||||||
Reference:{" "}
|
|
||||||
<span className="text-warning fw-semibold">
|
|
||||||
#SL0101
|
|
||||||
</span>
|
|
||||||
</small>
|
|
||||||
<small className="d-block">Date: Dec 24, 2024</small>
|
|
||||||
<small className="d-block">
|
|
||||||
Status:{" "}
|
|
||||||
<span className="badge bg-success">Completed</span>
|
|
||||||
</small>
|
|
||||||
<small className="d-block">
|
|
||||||
Payment Status:{" "}
|
|
||||||
<span className="badge bg-light-success text-success">
|
|
||||||
Paid
|
|
||||||
</span>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 className="fw-bold mb-3">Order Summary</h5>
|
<h5 className="fw-bold mb-3">Order Summary</h5>
|
||||||
|
|
||||||
<div className="table-responsive mb-4">
|
<div className="table-responsive mb-4">
|
||||||
<table className="table table-bordered">
|
|
||||||
<thead className="thead-light text-dark">
|
|
||||||
<tr>
|
|
||||||
<th>Product</th>
|
|
||||||
<th>Purchase Price($)</th>
|
|
||||||
<th>Discount($)</th>
|
|
||||||
<th>Tax(%)</th>
|
|
||||||
<th>Tax Amount($)</th>
|
|
||||||
<th>Unit Cost($)</th>
|
|
||||||
<th>Total Cost(%)</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img
|
|
||||||
src="/assets/img/products/stock-img-02.png"
|
|
||||||
alt="Nike Jordan"
|
|
||||||
width="30"
|
|
||||||
className="me-2"
|
|
||||||
/>{" "}
|
|
||||||
Nike Jordan
|
|
||||||
</td>
|
|
||||||
<td>2000</td>
|
|
||||||
<td>500</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>1500</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img
|
|
||||||
src="/assets/img/products/stock-img-03.png"
|
|
||||||
alt="Apple Watch"
|
|
||||||
width="30"
|
|
||||||
className="me-2"
|
|
||||||
/>{" "}
|
|
||||||
Apple Series 5 Watch
|
|
||||||
</td>
|
|
||||||
<td>3000</td>
|
|
||||||
<td>400</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>1700</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img
|
|
||||||
src="/assets/img/products/stock-img-05.png"
|
|
||||||
alt="Lobar Handy"
|
|
||||||
width="30"
|
|
||||||
className="me-2"
|
|
||||||
/>{" "}
|
|
||||||
Lobar Handy
|
|
||||||
</td>
|
|
||||||
<td>2500</td>
|
|
||||||
<td>500</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>0.00</td>
|
|
||||||
<td>2000</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="row justify-content-end">
|
|
||||||
<div className="col-md-6">
|
|
||||||
<table className="table table-bordered">
|
<table className="table table-bordered">
|
||||||
|
<thead className="thead-light text-dark">
|
||||||
|
<tr>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Variant</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>Unit Price</th>
|
||||||
|
<th>Total Price</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
{selectedOrder?.order_items?.map((item, index) => (
|
||||||
<td>Order Tax</td>
|
<tr key={index}>
|
||||||
<td className="text-end">$ 0.00</td>
|
<td>{item?.product_name}</td>
|
||||||
</tr>
|
<td>{item?.product_variant_name ?? "-"}</td>
|
||||||
<tr>
|
<td>{item?.status}</td>
|
||||||
<td>Discount</td>
|
<td>{item?.quantity}</td>
|
||||||
<td className="text-end">$ 0.00</td>
|
<td>{formatRupiah(item?.unit_price)}</td>
|
||||||
</tr>
|
<td>{formatRupiah(item?.total_price)}</td>
|
||||||
<tr className="fw-bold">
|
</tr>
|
||||||
<td>Grand Total</td>
|
))}
|
||||||
<td className="text-end">$ 5200.00</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Paid</td>
|
|
||||||
<td className="text-end">$ 5200.00</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Due</td>
|
|
||||||
<td className="text-end">$ 0.00</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="d-flex justify-content-end modal-footer">
|
<div className="row justify-content-end pb-3">
|
||||||
<button
|
<div className="col-md-6">
|
||||||
className="btn btn-outline-primary me-2"
|
<table className="table table-bordered">
|
||||||
data-bs-dismiss="modal"
|
<tbody>
|
||||||
>
|
<tr>
|
||||||
Cancel
|
<td>Order Tax</td>
|
||||||
</button>
|
<td className="text-end">{formatRupiah(selectedOrder?.tax_amount)}</td>
|
||||||
<button className="btn btn-primary">Submit</button>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Discount</td>
|
||||||
|
<td className="text-end">{formatRupiah(selectedOrder?.discount_amount)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className="fw-bold">
|
||||||
|
<td>Grand Total</td>
|
||||||
|
<td className="text-end">{formatRupiah(selectedOrder?.total_amount)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Sub Total</td>
|
||||||
|
<td className="text-end">{formatRupiah(selectedOrder?.subtotal)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -894,7 +879,7 @@ const SalesList = () => {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Product</th>
|
<th>Product</th>
|
||||||
<th>Qty</th>
|
<th>Qty</th>
|
||||||
<th>Purchase Price($)</th>
|
<th>Purchase Price</th>
|
||||||
<th>Discount($)</th>
|
<th>Discount($)</th>
|
||||||
<th>Tax(%)</th>
|
<th>Tax(%)</th>
|
||||||
<th>Tax Amount($)</th>
|
<th>Tax Amount($)</th>
|
||||||
|
|||||||
396
src/feature-module/superadmin/companylist.jsx
Normal file
396
src/feature-module/superadmin/companylist.jsx
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
import { Select } from "antd";
|
||||||
|
import {
|
||||||
|
ChevronUp,
|
||||||
|
PlusCircle,
|
||||||
|
RotateCcw,
|
||||||
|
Trash2,
|
||||||
|
} from "feather-icons-react/build/IconComponents";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import withReactContent from "sweetalert2-react-content";
|
||||||
|
import CustomPagination from "../../components/CustomPagination";
|
||||||
|
import ImageWithBasePath from "../../core/img/imagewithbasebath";
|
||||||
|
import AddCategoryList from "../../core/modals/inventory/addcategorylist";
|
||||||
|
import EditCategoryList from "../../core/modals/inventory/editcategorylist";
|
||||||
|
import Table from "../../core/pagination/datatable";
|
||||||
|
import { setToogleHeader } from "../../core/redux/action";
|
||||||
|
import { fetchOrganizations } from "../../core/redux/actions/organizationActions";
|
||||||
|
import { deletePaymentMethod, fetchPaymentMethod, fetchPaymentMethods } from "../../core/redux/actions/paymentMethodActions";
|
||||||
|
import { formatDate } from "../../utils/date";
|
||||||
|
|
||||||
|
const CompanyList = () => {
|
||||||
|
const {
|
||||||
|
organizations: apiPaymentMethods,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
totalPaymentMethods,
|
||||||
|
totalPages,
|
||||||
|
pageSize: reduxPageSize,
|
||||||
|
currentPage: reduxCurrentPage,
|
||||||
|
} = useSelector((state) => state.organizations);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const data = useSelector((state) => state.toggle_header);
|
||||||
|
const dataSource = apiPaymentMethods?.length > 0 ? apiPaymentMethods : [];
|
||||||
|
|
||||||
|
const [currentPage, setCurrentPage] = useState(reduxCurrentPage || 1);
|
||||||
|
const [pageSize, setPageSize] = useState(reduxPageSize || 10);
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
|
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPaymentMethods = async () => {
|
||||||
|
try {
|
||||||
|
const searchParams = {
|
||||||
|
page: currentPage,
|
||||||
|
limit: pageSize,
|
||||||
|
search: debouncedSearchTerm || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove empty parameters
|
||||||
|
const cleanParams = Object.fromEntries(
|
||||||
|
Object.entries(searchParams).filter(([, value]) => value !== "")
|
||||||
|
);
|
||||||
|
|
||||||
|
await dispatch(fetchOrganizations(cleanParams));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load categories", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPaymentMethods();
|
||||||
|
}, [dispatch, currentPage, pageSize, debouncedSearchTerm]);
|
||||||
|
|
||||||
|
// Debounce search term
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setDebouncedSearchTerm(searchTerm);
|
||||||
|
}, 500); // 500ms delay
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [searchTerm]);
|
||||||
|
|
||||||
|
// Handle pagination
|
||||||
|
const handlePageChange = (page) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle page size change
|
||||||
|
const handlePageSizeChange = (newPageSize) => {
|
||||||
|
setPageSize(newPageSize);
|
||||||
|
setCurrentPage(1); // Reset to first page when changing page size
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setSearchTerm(value);
|
||||||
|
// Reset to first page when searching
|
||||||
|
setCurrentPage(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate pagination info
|
||||||
|
const totalRecords = totalPaymentMethods || dataSource.length;
|
||||||
|
const calculatedTotalPages = Math.ceil(totalRecords / pageSize);
|
||||||
|
const actualTotalPages = totalPages || calculatedTotalPages;
|
||||||
|
|
||||||
|
const handleDeletePaymentMethod = async (paymentMethodId) => {
|
||||||
|
try {
|
||||||
|
await dispatch(deletePaymentMethod(paymentMethodId));
|
||||||
|
// Show success message
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Deleted!",
|
||||||
|
text: "Payment Method has been deleted successfully.",
|
||||||
|
icon: "success",
|
||||||
|
className: "btn btn-success",
|
||||||
|
customClass: {
|
||||||
|
confirmButton: "btn btn-success",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to delete payment method:", error);
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Error!",
|
||||||
|
text: "Failed to delete payment method. Please try again.",
|
||||||
|
icon: "error",
|
||||||
|
className: "btn btn-danger",
|
||||||
|
customClass: {
|
||||||
|
confirmButton: "btn btn-danger",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTooltip = (props) => (
|
||||||
|
<Tooltip id="pdf-tooltip" {...props}>
|
||||||
|
Pdf
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderExcelTooltip = (props) => (
|
||||||
|
<Tooltip id="excel-tooltip" {...props}>
|
||||||
|
Excel
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderPrinterTooltip = (props) => (
|
||||||
|
<Tooltip id="printer-tooltip" {...props}>
|
||||||
|
Printer
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderRefreshTooltip = (props) => (
|
||||||
|
<Tooltip id="refresh-tooltip" {...props}>
|
||||||
|
Refresh
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
const renderCollapseTooltip = (props) => (
|
||||||
|
<Tooltip id="refresh-tooltip" {...props}>
|
||||||
|
Collapse
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
const dateOptions = [
|
||||||
|
{ label: "Sort By: Last 7 Days", value: "last7days" },
|
||||||
|
{ label: "Sort By: Last Month", value: "lastmonth" },
|
||||||
|
{ label: "Sort By: Ascending", value: "ascending" },
|
||||||
|
{ label: "Sort By: Descending", value: "descending" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "Company Name",
|
||||||
|
dataIndex: "companyname",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record.name}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.name.length - b.name.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Email",
|
||||||
|
dataIndex: "email",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record?.email ?? '-'}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.email.length - b.email.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Phone Number",
|
||||||
|
dataIndex: "phonenumber",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record?.phone_number ?? '-'}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.phone_number.length - b.phone_number.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Plan",
|
||||||
|
dataIndex: "plan",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{record?.plan_type ?? '-'}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.plan_type.length - b.plan_type.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Created On",
|
||||||
|
dataIndex: "createdon",
|
||||||
|
render: (_, record) => {
|
||||||
|
return <span>{formatDate(record.created_at)}</span>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.created_at.length - b.created_at.length,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Actions",
|
||||||
|
dataIndex: "actions",
|
||||||
|
key: "actions",
|
||||||
|
render: (_, record) => (
|
||||||
|
<td className="action-table-data">
|
||||||
|
<div className="edit-delete-action">
|
||||||
|
<Link
|
||||||
|
className="me-2 p-2"
|
||||||
|
to="#"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#edit-category"
|
||||||
|
onClick={() => dispatch(fetchPaymentMethod(record.id))}
|
||||||
|
>
|
||||||
|
<i data-feather="edit" className="feather-edit"></i>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="confirm-text p-2"
|
||||||
|
to="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
MySwal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "You won't be able to revert this!",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#00ff00",
|
||||||
|
confirmButtonText: "Yes, delete it!",
|
||||||
|
cancelButtonColor: "#ff0000",
|
||||||
|
cancelButtonText: "Cancel",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
handleDeletePaymentMethod(record.id || record.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="feather-trash-2" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="page-wrapper">
|
||||||
|
<div className="content">
|
||||||
|
<div className="page-header">
|
||||||
|
<div className="add-item d-flex">
|
||||||
|
<div className="page-title">
|
||||||
|
<h4>Companies</h4>
|
||||||
|
<h6>Manage your companies</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className="table-top-head">
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderTooltip}>
|
||||||
|
<Link>
|
||||||
|
<ImageWithBasePath
|
||||||
|
src="assets/img/icons/pdf.svg"
|
||||||
|
alt="img"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderExcelTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<ImageWithBasePath
|
||||||
|
src="assets/img/icons/excel.svg"
|
||||||
|
alt="img"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderPrinterTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<i data-feather="printer" className="feather-printer" />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderRefreshTooltip}>
|
||||||
|
<Link data-bs-toggle="tooltip" data-bs-placement="top">
|
||||||
|
<RotateCcw />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<OverlayTrigger placement="top" overlay={renderCollapseTooltip}>
|
||||||
|
<Link
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-placement="top"
|
||||||
|
id="collapse-header"
|
||||||
|
className={data ? "active" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(setToogleHeader(!data));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChevronUp />
|
||||||
|
</Link>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div className="page-btn">
|
||||||
|
<Link
|
||||||
|
to="#"
|
||||||
|
className="btn btn-added"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#add-category"
|
||||||
|
>
|
||||||
|
<PlusCircle className="me-2" />
|
||||||
|
Add New Category
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* /product list */}
|
||||||
|
<div className="card table-list-card">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="table-top">
|
||||||
|
<div className="search-set">
|
||||||
|
<div className="search-input">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
className="form-control form-control-sm formsearch"
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
<Link to className="btn btn-searchset">
|
||||||
|
<i data-feather="search" className="feather-search" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
style={{ height: 36 }}
|
||||||
|
defaultValue={dateOptions[0]?.value}
|
||||||
|
options={dateOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="table-responsive">
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-center p-4">
|
||||||
|
<div className="spinner-border text-primary" role="status">
|
||||||
|
<span className="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
<p className="mt-2">Loading categories...</p>
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="alert alert-danger" role="alert">
|
||||||
|
<strong>Error:</strong> {error}
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-danger ms-2"
|
||||||
|
onClick={() => dispatch(fetchPaymentMethods())}
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Table columns={columns} dataSource={dataSource} />
|
||||||
|
|
||||||
|
<CustomPagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
totalCount={totalRecords}
|
||||||
|
totalPages={actualTotalPages}
|
||||||
|
loading={loading}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onPageSizeChange={handlePageSizeChange}
|
||||||
|
pageSizeOptions={[10, 20, 50, 100]}
|
||||||
|
showInfo={true}
|
||||||
|
showPageSizeSelector={true}
|
||||||
|
compact={false}
|
||||||
|
className="paymentmethod-list-pagination"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* /category list */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AddCategoryList />
|
||||||
|
<EditCategoryList />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CompanyList;
|
||||||
@ -5,7 +5,6 @@ const ENDPOINTS = {
|
|||||||
CATEGORIES: 'categories',
|
CATEGORIES: 'categories',
|
||||||
CATEGORY_BY_ID: (id) => `categories/${id}`,
|
CATEGORY_BY_ID: (id) => `categories/${id}`,
|
||||||
CATEGORY_PRODUCTS: (id) => `categories/${id}/products`,
|
CATEGORY_PRODUCTS: (id) => `categories/${id}/products`,
|
||||||
SEARCH: 'categories/search',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Categories API service
|
// Categories API service
|
||||||
@ -65,19 +64,6 @@ export const categoriesApi = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Search categories
|
|
||||||
searchCategories: async (query, params = {}) => {
|
|
||||||
try {
|
|
||||||
const response = await api.get(ENDPOINTS.SEARCH, {
|
|
||||||
params: { q: query, ...params }
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error searching categories:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Get products by category
|
// Get products by category
|
||||||
getCategoryProducts: async (id, params = {}) => {
|
getCategoryProducts: async (id, params = {}) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
91
src/services/organizationsApi.js
Normal file
91
src/services/organizationsApi.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import api from './api';
|
||||||
|
|
||||||
|
const ENDPOINTS = {
|
||||||
|
ORGANIZATIONS: 'organizations',
|
||||||
|
ORGANIZATION_BY_ID: (id) => `organizations/${id}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const organizationsApi = {
|
||||||
|
// Get all organizations
|
||||||
|
getAllOrganizations: async (params = {}) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(ENDPOINTS.ORGANIZATIONS, { params });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching organizations:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get organization by ID
|
||||||
|
getOrganizationById: async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(ENDPOINTS.ORGANIZATION_BY_ID(id));
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching organization ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create organization
|
||||||
|
createOrganization: async (data) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(ENDPOINTS.ORGANIZATIONS, data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating organization:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Update organization
|
||||||
|
updateOrganization: async (id, data) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(ENDPOINTS.ORGANIZATION_BY_ID(id), data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error updating organization ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Delete organization
|
||||||
|
deleteOrganization: async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(ENDPOINTS.ORGANIZATION_BY_ID(id));
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error deleting organization ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bulk update
|
||||||
|
bulkUpdate: async (organizations) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(`${ENDPOINTS.ORGANIZATIONS}/bulk`, {
|
||||||
|
organizations,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error bulk updating organizations:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bulk delete
|
||||||
|
bulkDelete: async (ids) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`${ENDPOINTS.ORGANIZATIONS}/bulk`, {
|
||||||
|
data: { ids },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error bulk deleting organizations:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default organizationsApi;
|
||||||
91
src/services/paymentMethodsApi.js
Normal file
91
src/services/paymentMethodsApi.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import api from './api';
|
||||||
|
|
||||||
|
const ENDPOINTS = {
|
||||||
|
PAYMENT_METHODS: 'payment-methods',
|
||||||
|
PAYMENT_METHOD_BY_ID: (id) => `payment-methods/${id}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paymentMethodsApi = {
|
||||||
|
// Get all payment methods
|
||||||
|
getAll: async (params = {}) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(ENDPOINTS.PAYMENT_METHODS, { params });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching payment methods:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get payment method by ID
|
||||||
|
getById: async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await api.get(ENDPOINTS.PAYMENT_METHOD_BY_ID(id));
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching payment method ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create new payment method
|
||||||
|
create: async (data) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post(ENDPOINTS.PAYMENT_METHODS, data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating payment method:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Update payment method
|
||||||
|
update: async (id, data) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(ENDPOINTS.PAYMENT_METHOD_BY_ID(id), data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error updating payment method ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Delete payment method
|
||||||
|
remove: async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(ENDPOINTS.PAYMENT_METHOD_BY_ID(id));
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error deleting payment method ${id}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bulk update
|
||||||
|
bulkUpdate: async (methods) => {
|
||||||
|
try {
|
||||||
|
const response = await api.put(`${ENDPOINTS.PAYMENT_METHODS}/bulk`, {
|
||||||
|
methods,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error bulk updating payment methods:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bulk delete
|
||||||
|
bulkDelete: async (ids) => {
|
||||||
|
try {
|
||||||
|
const response = await api.delete(`${ENDPOINTS.PAYMENT_METHODS}/bulk`, {
|
||||||
|
data: { ids },
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error bulk deleting payment methods:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default paymentMethodsApi;
|
||||||
@ -10,4 +10,8 @@ const formatDate = (isoDate) => {
|
|||||||
return formatted
|
return formatted
|
||||||
};
|
};
|
||||||
|
|
||||||
export { formatDate };
|
const formatInputDate = (date) => {
|
||||||
|
return new Date(date).toLocaleDateString("en-CA");
|
||||||
|
};
|
||||||
|
|
||||||
|
export { formatDate, formatInputDate };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user