Initial commit

This commit is contained in:
2025-10-04 18:22:24 +03:00
commit 2852c2c054
291 changed files with 38109 additions and 0 deletions

31
src/redux/api/apiSlice.ts Normal file
View File

@@ -0,0 +1,31 @@
import {
BaseQueryFn,
createApi,
FetchArgs,
} from "@reduxjs/toolkit/query/react";
import { API_BASE_URL } from "utils/constants";
import baseQuery from "./baseQuery";
export type ProServerError = {
data: {
error: string;
description: string;
status: "error";
};
};
export const baseApi = createApi({
reducerPath: "baseApi",
baseQuery: baseQuery({
baseUrl: API_BASE_URL,
}) as BaseQueryFn<string | FetchArgs, unknown, ProServerError, {}>,
tagTypes: [
"Orders",
"Order",
"Menu",
"Restaurant",
],
keepUnusedDataFor: 240, // Keep data for 60 seconds after component unmounts
endpoints: () => ({}),
});
export const dummy = [];

34
src/redux/api/auth.ts Normal file
View File

@@ -0,0 +1,34 @@
import { CONFIRM_OTP_URL, LOGIN_URL, SEND_OTP_URL } from "utils/constants";
import { LoginUserType } from "utils/types/appTypes";
import { baseApi } from "./apiSlice";
export const loginApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
createLogin: builder.mutation({
query: (body: LoginUserType) => ({
url: LOGIN_URL,
method: "POST",
body,
}),
}),
sendOtp: builder.mutation({
query: (body: any) => ({
url: SEND_OTP_URL,
method: "POST",
body,
}),
}),
confirmOtp: builder.mutation({
query: (body: any) => ({
url: CONFIRM_OTP_URL,
method: "POST",
body,
}),
}),
}),
});
export const {
useCreateLoginMutation,
useSendOtpMutation,
useConfirmOtpMutation,
} = loginApi;

View File

@@ -0,0 +1,27 @@
import { fetchBaseQuery, retry } from "@reduxjs/toolkit/query/react";
import { getDefaultLanguage } from "i18n/helper";
import { ACCESS_TOKEN } from "utils/constants";
const baseQuery = ({ baseUrl }: { baseUrl: string }) =>
retry(
fetchBaseQuery({
baseUrl,
validateStatus: (response, result) => {
return response.status?.toString()[0] === "2" && !result.error;
},
prepareHeaders: (headers) => {
const token = localStorage.getItem(ACCESS_TOKEN);
headers.set("Accept", "application/json");
headers.set("Accept-Language", getDefaultLanguage());
if (token) headers.set("authorization", `Bearer ${token}`);
return headers;
},
}),
{
maxRetries: 0, // Set the maximum number of retries to 3
}
);
export default baseQuery;

53
src/redux/api/others.ts Normal file
View File

@@ -0,0 +1,53 @@
import {
CREATE_ORDER_URL,
ORDERS_URL,
PRODUCTS_AND_CATEGORIES_URL,
RESTAURANT_DETAILS_URL,
} from "utils/constants";
import menuParser from "pages/menu/helper";
import { RestaurantDetails } from "utils/types/appTypes";
import { baseApi } from "./apiSlice";
export const branchApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getRestaurantDetails: builder.query<RestaurantDetails, string | void>({
query: (restaurantId: string) =>
`${RESTAURANT_DETAILS_URL}${restaurantId}`,
transformResponse: (response: any) => {
return response.result;
},
}),
getMenu: builder.query<any, string | void>({
query: (restaurantId: string) =>
`${PRODUCTS_AND_CATEGORIES_URL}${restaurantId}`,
transformResponse: (response: any) => {
return menuParser(response);
},
}),
getOrders: builder.query<any, void>({
query: () => ({
url: ORDERS_URL,
method: "POST",
}),
transformResponse: (response: any) => {
return response.result.data.orders;
},
providesTags: ["Orders"],
}),
createOrder: builder.mutation({
query: (body: any) => ({
url: CREATE_ORDER_URL,
method: "POST",
body,
}),
invalidatesTags: ["Orders"],
}),
}),
});
export const {
useGetRestaurantDetailsQuery,
useGetMenuQuery,
useCreateOrderMutation,
useGetOrdersQuery,
} = branchApi;

32
src/redux/api/roles.ts Normal file
View File

@@ -0,0 +1,32 @@
import {
PERMISSIONS_DATA_URL,
ROLES_DATA_URL,
SINGED_USER_INFO_URL
} from "utils/constants";
import { RolesType, UserType } from "utils/types/appTypes";
import { baseApi } from "./apiSlice";
export const roleApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getRoles: builder.query<Array<RolesType>, void>({
query: () => ROLES_DATA_URL,
transformResponse: (response: any) => {
return response.data;
},
}),
getPermissions: builder.query<Array<any>, void>({
query: () => PERMISSIONS_DATA_URL,
transformResponse: (response: any) => {
return response.data;
},
}),
getSignedUserInfo: builder.query<UserType, void>({
query: () => SINGED_USER_INFO_URL,
}),
}),
});
export const {
useGetRolesQuery,
useGetPermissionsQuery,
useGetSignedUserInfoQuery,
} = roleApi;

65
src/redux/hooks.ts Normal file
View File

@@ -0,0 +1,65 @@
import { useDispatch, useSelector } from "react-redux";
import type { TypedUseSelectorHook } from "react-redux";
import { ItemType, OrderType } from "pages/pos/orders/types";
import { TableType } from "pages/pos/tables/types";
import { useEffect, useMemo, useState } from "react";
import { parseTranslations } from "utils/helpers";
import type { AppDispatch, RootState } from "./store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useDebouncedValue = (value: any, delay: number = 500) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler: NodeJS.Timeout = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
export const useExtractTableItems = (table?: TableType) => {
return useMemo(() => {
let items: Array<ItemType> = [];
let quantity: number = 0;
table?.active_reservation?.orders?.forEach((order: OrderType) => {
items = [...items, ...(order?.items || [])];
quantity += Number(order?.quantities) || 0;
});
items = items.filter((i) => i.quantity > 0);
items = [...removeDuplicatesAndIncreaseQuantity(items)];
items = items.map((i: any) => ({ ...i, ...parseTranslations(i.product) }));
return { items, quantity };
}, [table]);
};
export function removeDuplicatesAndIncreaseQuantity(items: any[]) {
const result: any[] = [];
// Group objects by ID
const grouped = items.reduce((acc, item) => {
if (!acc[item.product.id]) {
acc[item.product.id] = { ...item };
} else {
acc[item.product.id].quantity += item.quantity;
acc[item.product.id].total_amount =
acc[item.product.id]?.quantity * acc[item.product.id]?.unit_price;
}
return acc;
}, {});
// Convert grouped objects back to array
Object.values(grouped).forEach((item) => result.push(item));
return result;
}

33
src/redux/store.ts Normal file
View File

@@ -0,0 +1,33 @@
import { Action, configureStore, ThunkAction } from "@reduxjs/toolkit";
import authReducer from "features/auth/authSlice";
import appErrorReducer from "features/error/appErrorSlice";
import localeReducer from "features/locale/localeSlice";
import orderSlice from "features/order/orderSlice";
import themeReducer from "features/theme/themeSlice";
import { baseApi } from "./api/apiSlice";
export const store = configureStore({
reducer: {
auth: authReducer,
theme: themeReducer,
locale: localeReducer,
appError: appErrorReducer,
order: orderSlice,
[baseApi.reducerPath]: baseApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }).concat(
baseApi.middleware
),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;