initial commit

This commit is contained in:
2025-09-24 20:32:28 +05:00
commit 1759326253
184 changed files with 23284 additions and 0 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
VITE_API_URL=https://mm.com.tm/api/v1/
VITE_API_TOKEN=hello-mf-s

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

38
eslint.config.js Normal file
View File

@@ -0,0 +1,38 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MM Electronics</title>
</head>
<body>
<div id="root" ></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

8834
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

46
package.json Normal file
View File

@@ -0,0 +1,46 @@
{
"name": "MMElectronics",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^2.5.0",
"antd": "^5.22.7",
"framer-motion": "^12.5.0",
"i18next": "^24.2.1",
"i18next-browser-languagedetector": "^8.0.2",
"imask": "^7.6.1",
"lodash": "^4.17.21",
"lucide-react": "^0.469.0",
"nprogress": "^0.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.4.0",
"react-icons": "^5.4.0",
"react-infinite-scroll-component": "^6.1.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.1.1",
"swiper": "^11.2.0"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.17.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"install": "^0.13.0",
"npm": "^11.0.0",
"sass-embedded": "^1.83.0",
"vite": "^6.0.5"
}
}

17
public/logo.svg Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 171.39 122.42">
<defs>
<style>
.cls-1 {
fill: #d82622;
stroke-width: 0px;
}
</style>
</defs>
<g id="Layer_10" data-name="Layer 10">
<g>
<polygon class="cls-1" points="122.42 0 122.42 32.65 97.93 32.65 97.93 32.65 85.69 44.89 73.45 32.65 48.97 32.65 48.97 0 0 0 0 48.97 32.65 48.97 32.65 73.45 73.45 73.45 73.45 57.13 85.69 69.36 97.93 57.13 97.93 73.45 138.74 73.45 138.74 48.97 171.39 48.97 171.39 0 122.42 0"/>
<polygon class="cls-1" points="97.93 81.61 97.93 81.62 85.69 93.86 73.45 81.61 32.65 81.61 32.65 122.42 73.45 122.42 73.45 106.1 85.69 118.33 97.93 106.1 97.93 122.42 138.74 122.42 138.74 81.61 97.93 81.61"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 827 B

0
src/App.css Normal file
View File

84
src/App.jsx Normal file
View File

@@ -0,0 +1,84 @@
import { useState, useEffect } from "react";
import { BackTop } from "antd";
import Router from "./routes";
import { Provider } from "react-redux";
import store from "./app/store";
import "./i18n/i18n";
import PageLoader from "./components/Loader/pageLoader.jsx";
import ScrollToTop from "./components/ScrollToTop";
import { AuthProvider } from "./context/authContext.jsx";
function App() {
const [isLoading, setIsLoading] = useState(true);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsLoading(false);
}, 1000);
return () => clearTimeout(timer);
}, []);
const handleScroll = () => {
const scrollPosition = window.scrollY;
if (scrollPosition > 200) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<Provider store={store}>
<AuthProvider>
{isLoading ? (
<div className="loading-container">
<PageLoader />
</div>
) : (
<>
<Router />
<ScrollToTop />
{isVisible && (
<BackTop visibilityHeight={500} duration={800}>
<div
style={{
height: 40,
width: 40,
backgroundColor: "#fff",
color: "#fff",
textAlign: "center",
lineHeight: "40px",
borderRadius: "50%",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
right: "0",
}}
>
<svg
style={{ color: "#888888" }}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m12 7l5 5h-3v4h-4v-4H7l5-5m0 15A10 10 0 0 1 2 12A10 10 0 0 1 12 2a10 10 0 0 1 10 10a10 10 0 0 1-10 10m0-2a8 8 0 0 0 8-8a8 8 0 0 0-8-8a8 8 0 0 0-8 8a8 8 0 0 0 8 8Z"
></path>
</svg>
</div>
</BackTop>
)}
</>
)}
</AuthProvider>
</Provider>
);
}
export default App;

100
src/app/api/authApi.js Normal file
View File

@@ -0,0 +1,100 @@
import { baseApi } from "./baseApi";
export const authApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getGuestToken: builder.mutation({
query: () => ({
url: "auth/guest-token",
method: "POST",
headers: {
"Content-Type": "application/json",
},
}),
async onQueryStarted(_, { queryFulfilled }) {
try {
const { data } = await queryFulfilled;
const token = data?.token;
if (token) {
document.cookie = `guestToken=${token}; path=/; secure; SameSite=Strict`;
localStorage.setItem("guestToken", token);
}
} catch (error) {
console.error("Error fetching guest token:", error);
}
},
}),
login: builder.mutation({
query: (credentials) => ({
url: "/auth/login",
method: "POST",
body: credentials,
}),
transformResponse: (response) => {
const token = response?.token || response?.data;
if (token) {
localStorage.removeItem("guestToken");
document.cookie =
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
localStorage.setItem("authToken", token);
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
}
return response;
},
}),
register: builder.mutation({
query: (userData) => ({
url: "/auth/register",
method: "POST",
body: userData,
}),
transformResponse: (response) => {
const token = response?.token || response?.data;
if (token) {
localStorage.removeItem("guestToken");
document.cookie =
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
localStorage.setItem("authToken", token);
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
}
return response;
},
}),
verifyToken: builder.mutation({
query: ({ phone_number, code }) => ({
url: "/auth/verify",
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: { phone_number, code },
}),
async onQueryStarted(_, { queryFulfilled }) {
try {
const { data } = await queryFulfilled;
const token = data.data;
// console.log("Full Response:", data);
// console.log("New Token:", token);
if (token) {
localStorage.removeItem("guestToken");
document.cookie =
"guestToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
localStorage.setItem("authToken", token);
document.cookie = `authToken=${token}; path=/; secure; SameSite=Strict`;
}
} catch (error) {
console.error("Error verifying token:", error);
}
},
}),
}),
});
export const {
useGetGuestTokenMutation,
useLoginMutation,
useRegisterMutation,
useVerifyTokenMutation,
} = authApi;

14
src/app/api/bannersApi.js Normal file
View File

@@ -0,0 +1,14 @@
import { baseApi } from './baseApi';
export const mediaApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getCarousels: builder.query({
query: () => '/media/carousels',
}),
getBanners: builder.query({
query: () => '/media/banners',
}),
}),
});
export const { useGetCarouselsQuery, useGetBannersQuery } = mediaApi;

124
src/app/api/baseApi.js Normal file
View File

@@ -0,0 +1,124 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import i18n from "../../i18n/i18n"; // Import your i18n configuration
const getToken = () => {
// Önce cookie'lerden token kontrol et
const authTokenCookie = document.cookie
.split("; ")
.find((row) => row.startsWith("authToken="));
const guestTokenCookie = document.cookie
.split("; ")
.find((row) => row.startsWith("guestToken="));
// Sonra localStorage'dan kontrol et
const authTokenLocal = localStorage.getItem("authToken");
const guestTokenLocal = localStorage.getItem("guestToken");
// Auth token varsa onu döndür (önce cookie sonra localStorage)
if (authTokenCookie) return authTokenCookie.split("=")[1];
if (authTokenLocal) return authTokenLocal;
// Guest token varsa onu döndür
if (guestTokenCookie) return guestTokenCookie.split("=")[1];
if (guestTokenLocal) return guestTokenLocal;
return null;
};
const customBaseQuery = async (args, api, extraOptions) => {
const token = getToken();
const currentLanguage = i18n.language || "tm";
// Add language parameter to the URL
const urlWithLang =
typeof args === "string"
? `${args}${args.includes("?") ? "&" : "?"}lang=${currentLanguage}`
: {
...args,
url: `${args.url}${
args.url.includes("?") ? "&" : "?"
}lang=${currentLanguage}`,
};
const baseQuery = fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL || "https://mm.com.tm/api/v1/",
prepareHeaders: (headers) => {
headers.set("Api-Token", import.meta.env.VITE_API_TOKEN || "hello-mf-s");
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
if (!headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
return headers;
},
});
try {
const result = await baseQuery(urlWithLang, api, extraOptions);
if (result.error && result.error.status === 401) {
try {
const guestTokenResponse = await fetch(
`${
import.meta.env.VITE_API_URL || "https://mm.com.tm/api/v1/"
}/auth/guest-token`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Api-Token": import.meta.env.VITE_API_TOKEN || "hello-mf-s",
},
}
);
const data = await guestTokenResponse.json();
const newGuestToken = data.token || data.data;
if (newGuestToken) {
localStorage.setItem("guestToken", newGuestToken);
document.cookie = `guestToken=${newGuestToken}; path=/; secure; SameSite=Strict`;
const retryResult = await baseQuery(urlWithLang, api, extraOptions);
return retryResult;
}
} catch (error) {
console.error("Failed to get new guest token:", error);
}
}
if (
result.error &&
typeof result.error.data === "string" &&
result.error.data.includes("<!DOCTYPE html>")
) {
return {
error: {
status: result.error.status,
data: {
message: "Server returned an HTML error page instead of JSON",
},
},
};
}
return result;
} catch (axiosError) {
console.error("Query failed:", axiosError);
return {
error: {
status: "CUSTOM_ERROR",
error: axiosError.message || "Unknown error occurred",
},
};
}
};
export const baseApi = createApi({
reducerPath: "api",
baseQuery: customBaseQuery,
endpoints: () => ({}),
});

60
src/app/api/brandsApi.js Normal file
View File

@@ -0,0 +1,60 @@
import { baseApi } from "./baseApi";
export const brandsApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getBrands: builder.query({
query: (params = {}) => {
const queryParams = new URLSearchParams();
if (params.type) {
queryParams.append("type", params.type);
}
if (params.page) {
queryParams.append("page", params.page);
}
if (params.limit) {
queryParams.append("limit", params.limit);
}
const queryString = queryParams.toString();
return `/brands${queryString ? `?${queryString}` : ""}`;
},
transformResponse: (response) => response.data || response,
}),
getBrandDetails: builder.query({
query: (brandId) => `/brands/${brandId}`,
transformResponse: (response) => response.data || response,
}),
getBrandProducts: builder.query({
query: (params) => {
// Handle both string ID and object with pagination params
if (typeof params === 'string' || typeof params === 'number') {
return `/brands/${params}/products`;
}
// Handle object with pagination
const { id, page = 1, limit } = params;
let url = `/brands/${id}/products?page=${page}`;
if (limit) {
url += `&limit=${limit}`;
}
return url;
},
transformResponse: (response) => {
return response.data || response;
},
}),
}),
});
export const {
useGetBrandsQuery,
useLazyGetBrandsQuery,
useGetBrandDetailsQuery,
useGetBrandProductsQuery,
useLazyGetBrandProductsQuery,
} = brandsApi;

202
src/app/api/cartApi.js Normal file
View File

@@ -0,0 +1,202 @@
import { baseApi } from "./baseApi";
export const cartApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getCart: builder.query({
query: () => `/carts`,
providesTags: ["cartItems"],
pollingInterval: 5000,
refetchOnMountOrArgChange: true,
refetchOnFocus: false,
refetchOnReconnect: true,
transformResponse: (response) => {
if (
typeof response === "string" &&
(response.trim().startsWith("<!DOCTYPE") ||
response.trim().startsWith("<html"))
) {
console.error(
"Received HTML response instead of JSON:",
response.substring(0, 100)
);
return {
message: "error",
data: [],
errorDetails:
"Server returned HTML instead of JSON. The server might be down or experiencing issues.",
};
}
if (typeof response === "object") {
if (response.data) {
return response;
}
return { message: "success", data: [] };
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed;
} catch (error) {
console.error("Failed to parse response:", error);
return { message: "error", data: [] };
}
}
return { message: "unknown", data: [] };
},
}),
addToCart: builder.mutation({
query: ({ productId, quantity }) => ({
url: `/carts`,
method: "POST",
body: new URLSearchParams({
product_id: productId,
product_quantity: quantity,
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}),
invalidatesTags: ["cartItems"],
async onQueryStarted(
{ productId, quantity },
{ dispatch, queryFulfilled }
) {
try {
await queryFulfilled;
} catch {}
},
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed;
} catch (error) {
console.error("Failed to parse add to cart response:", error);
return { message: "success", data: "Added to cart" };
}
}
return { message: "success", data: "Added to cart" };
},
}),
removeFromCart: builder.mutation({
query: ({ productId }) => ({
url: `/carts`,
method: "PATCH",
body: new URLSearchParams({ product_id: productId }),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}),
invalidatesTags: ["cartItems"],
async onQueryStarted({ productId }, { dispatch, queryFulfilled }) {
try {
await queryFulfilled;
} catch {}
},
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || [];
} catch (error) {
console.error("Failed to parse cart response:", error);
return [];
}
}
return [];
},
}),
cleanCart: builder.mutation({
query: () => ({
url: `/carts`,
method: "DELETE",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}),
invalidatesTags: ["cartItems"],
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || [];
} catch (error) {
console.error("Failed to parse cart response:", error);
return [];
}
}
return [];
},
}),
updateCartItem: builder.mutation({
query: ({ productId, quantity }) => ({
url: `/carts`,
method: "POST",
body: new URLSearchParams({
product_id: productId,
product_quantity: quantity,
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}),
invalidatesTags: ["cartItems"],
async onQueryStarted(
{ productId, quantity },
{ dispatch, queryFulfilled }
) {
try {
await queryFulfilled;
} catch {
console.error("API update failed, retrying...");
}
},
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed;
} catch (error) {
console.error("Failed to parse update cart response:", error);
return { message: "success", data: "Updated cart" };
}
}
return { message: "success", data: "Updated cart" };
},
}),
}),
});
export const {
useGetCartQuery,
useAddToCartMutation,
useRemoveFromCartMutation,
useUpdateCartItemMutation,
useCleanCartMutation,
} = cartApi;

110
src/app/api/categories.js Normal file
View File

@@ -0,0 +1,110 @@
import { baseApi } from "./baseApi";
export const categoriesApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getCategories: builder.query({
query: (type = "tree") => `/categories?type=${type}`,
}),
getCategoryProducts: builder.query({
query: ({ categoryId, page = 1, limit }) => {
let url = `categories/${categoryId}/products?page=${page}`;
if (limit) url += `&limit=${limit}`;
return url;
},
transformResponse: (response) => ({
data: response.data || [],
pagination: response.pagination || {},
}),
}),
getAllCategoryProducts: builder.query({
async queryFn(category, queryApi, extraOptions, baseQuery) {
const fetchProducts = async (categoryId) => {
const result = await baseQuery(`categories/${categoryId}/products`);
return result.data ? result.data.data : [];
};
let allProducts = await fetchProducts(category.id);
for (const child of category.children) {
const childProducts = await fetchProducts(child.id);
allProducts = [...allProducts, ...childProducts];
}
return { data: allProducts };
},
}),
getAllCategoryProductsPaginated: builder.query({
async queryFn(
{ category, page = 1, limit = 6 },
queryApi,
extraOptions,
baseQuery
) {
if (!category) return { data: [] };
try {
const hasMoreByCategory = {};
const fetchProductsForPage = async (categoryIds, currentPage) => {
let allPageProducts = [];
const perCategoryLimit = Math.ceil(limit / categoryIds.length);
for (const categoryId of categoryIds) {
const result = await baseQuery(
`categories/${categoryId}/products?page=${currentPage}&limit=${perCategoryLimit}`
);
if (result.data && result.data.data) {
allPageProducts = [...allPageProducts, ...result.data.data];
hasMoreByCategory[categoryId] =
!!result.data.pagination.next_page_url;
}
}
return allPageProducts;
};
const categoryIds = [category.id];
if (category.children && category.children.length > 0) {
category.children.forEach((child) => categoryIds.push(child.id));
}
const productsForPage = await fetchProductsForPage(categoryIds, page);
const hasMorePages = Object.values(hasMoreByCategory).some(
(hasMore) => hasMore
);
return {
data: {
data: productsForPage,
pagination: {
currentPage: page,
hasMorePages: hasMorePages,
},
},
};
} catch (error) {
return { error };
}
},
}),
getProductById: builder.query({
query: (productId) => `/products/${productId}`,
}),
getRelatedProducts: builder.query({
query: (productId) => `/products/${productId}/related`,
}),
}),
});
export const {
useGetCategoriesQuery,
useGetCategoryProductsQuery,
useGetAllCategoryProductsQuery,
useGetAllCategoryProductsPaginatedQuery,
useLazyGetAllCategoryProductsPaginatedQuery,
useGetProductByIdQuery,
useGetRelatedProductsQuery,
} = categoriesApi;

View File

@@ -0,0 +1,51 @@
import { baseApi } from "./baseApi";
export const collectionsApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getCollections: builder.query({
query: () => `/collections`,
}),
getCollectionById: builder.query({
query: (collectionId) => `/collections/${collectionId}`,
}),
getCollectionProducts: builder.query({
query: (collectionId) => `/collections/${collectionId}/products`,
// Ürünleri dönüştürerek boş kontrol edilebilir
transformResponse: (response) => {
return {
data: response.data || [],
isEmpty: !response.data || response.data.length === 0,
};
},
}),
// Yeni endpoint: Koleksiyonun ürün içerip içermediğini kontrol eder
checkCollectionHasProducts: builder.query({
query: (collectionId) => `/collections/${collectionId}/products?limit=1`,
transformResponse: (response) => {
return {
hasProducts: response.data && response.data.length > 0,
};
},
}),
// Sayfalı koleksiyon ürünleri için endpoint
getCollectionProductsPaginated: builder.query({
query: ({ collectionId, page = 1, limit = 6 }) =>
`/collections/${collectionId}/products?page=${page}${limit ? `&limit=${limit}` : ''}`,
transformResponse: (response) => ({
data: response.data || [],
pagination: response.pagination || {},
isEmpty: !response.data || response.data.length === 0,
}),
}),
}),
});
export const {
useGetCollectionsQuery,
useGetCollectionByIdQuery,
useGetCollectionProductsQuery,
useCheckCollectionHasProductsQuery,
useGetCollectionProductsPaginatedQuery,
useLazyGetCollectionProductsPaginatedQuery,
useLazyCheckCollectionHasProductsQuery,
} = collectionsApi;

19
src/app/api/contactUs.js Normal file
View File

@@ -0,0 +1,19 @@
import { baseApi } from "./baseApi";
export const contactApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
submitContactMessage: builder.mutation({
query: ({ phone, title, content, type }) => ({
url: "/forms/contact-us",
method: "POST",
body: { phone, title, content, type },
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
}),
}),
}),
});
export const { useSubmitContactMessageMutation } = contactApi;

View File

@@ -0,0 +1,97 @@
import { baseApi } from "./baseApi";
export const favoritesApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getFavorites: builder.query({
query: () => `/favorites`,
providesTags: ["Favorites"],
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || [];
} catch (error) {
console.error("Failed to parse favorites response:", error);
return [];
}
}
return [];
},
}),
addFavorite: builder.mutation({
query: (productId) => ({
url: `/favorites`,
method: "POST",
body: new URLSearchParams({ product_id: productId }),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
invalidatesTags: ["Favorites"],
}),
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || "Added";
} catch (error) {
if (response.includes("<!doctype html>")) {
return "Added";
}
console.error("Failed to parse add favorite response:", error);
return "Added";
}
}
return "Added";
},
}),
removeFavorite: builder.mutation({
query: (productId) => ({
url: `/favorites`,
method: "POST",
body: new URLSearchParams({
product_id: productId,
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
invalidatesTags: ["Favorites"],
}),
transformResponse: (response) => {
if (typeof response === "object" && response.data) {
return response.data;
}
if (typeof response === "string") {
try {
const parsed = JSON.parse(response);
return parsed.data || "Removed";
} catch (error) {
if (response.includes("<!doctype html>")) {
return "Removed";
}
console.error("Failed to parse remove favorite response:", error);
return "Removed";
}
}
return "Removed";
},
}),
}),
});
export const {
useGetFavoritesQuery,
useAddFavoriteMutation,
useRemoveFavoriteMutation,
} = favoritesApi;

View File

@@ -0,0 +1,15 @@
import { baseApi } from "./baseApi";
export const legalPagesApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getLegalPages: builder.query({
query: () => `/legal-pages`,
}),
getLegalPageBySlug: builder.query({
query: (slug) => `/legal-pages/${slug}`,
}),
}),
});
export const { useGetLegalPagesQuery, useGetLegalPageBySlugQuery } =
legalPagesApi;

View File

@@ -0,0 +1,11 @@
import { baseApi } from "./baseApi";
export const locationApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getLocations: builder.query({
query: () => "/provinces",
}),
}),
});
export const { useGetLocationsQuery } = locationApi;

View File

@@ -0,0 +1,36 @@
import { baseApi } from "./baseApi";
export const profileApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getProfile: builder.query({
query: () => {
const authToken = localStorage.getItem("authToken");
return {
url: "profile",
method: "GET",
headers: {
Authorization: `Bearer ${authToken}`,
Accept: "application/json",
},
};
},
}),
updateProfile: builder.mutation({
query: (profileData) => {
const authToken = localStorage.getItem("authToken");
return {
url: "profile",
method: "POST",
headers: {
Authorization: `Bearer ${authToken}`,
Accept: "application/json",
"Content-Type": "application/json",
},
body: profileData,
};
},
}),
}),
});
export const { useGetProfileQuery, useUpdateProfileMutation } = profileApi;

81
src/app/api/orderApi.js Normal file
View File

@@ -0,0 +1,81 @@
import { baseApi } from "./baseApi";
export const orderApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getOrders: builder.query({
query: () => "/orders",
transformResponse: (response) => response.data || [],
}),
getOrderById: builder.query({
query: (orderId) => `/orders/${orderId}`,
transformResponse: (response) => response.data || null,
}),
placeOrder: builder.mutation({
query: (orderDetails) => ({
url: "/orders",
method: "POST",
body: new URLSearchParams(orderDetails),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}),
transformResponse: (response, meta, arg) => {
console.log({response: response})
if (response && response.data) {
return response.data;
}
return "Order placed";
},
transformErrorResponse: (response, meta, arg) => {
if (
typeof response.data === "string" &&
response.data.includes("<!doctype html>")
) {
return {
status: "CUSTOM_ERROR",
data: {
message:
"Server returned HTML instead of expected response format",
},
};
}
return response;
},
validateStatus: (response, result) => {
return (
response.status >= 200 && response.status < 300 && !result?.error
);
},
}),
cancelOrder: builder.mutation({
query: (orderId) => ({
url: `/orders/${orderId}`,
method: "DELETE",
}),
transformResponse: (response) => response.data || "Order cancelled",
}),
getOrderTimes: builder.query({
query: () => "/order-time",
transformResponse: (response) => response.data || [],
}),
getOrderPayments: builder.query({
query: () => "/order-payments",
transformResponse: (response) => response.data || [],
}),
}),
});
export const {
useGetOrdersQuery,
useGetOrderByIdQuery,
usePlaceOrderMutation,
useCancelOrderMutation,
useGetOrderTimesQuery,
useGetOrderPaymentsQuery,
} = orderApi;

22
src/app/api/reviewApi.js Normal file
View File

@@ -0,0 +1,22 @@
import { baseApi } from "./baseApi";
export const reviewsApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
submitReview: builder.mutation({
query: ({ productId, rating, title, source }) => ({
url: `/products/${productId}/reviews`,
method: "POST",
body: { rating: rating, title: title, source: source },
headers: {
"Content-Type": "application/json",
},
}),
}),
getReviewsByProduct: builder.query({
query: (productId) => `/reviews?product_id=${productId}`,
}),
}),
});
export const { useSubmitReviewMutation, useGetReviewsByProductQuery } =
reviewsApi;

21
src/app/api/searchApi.js Normal file
View File

@@ -0,0 +1,21 @@
import { baseApi } from "./baseApi";
export const searchApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
searchProduct: builder.query({
query: (query) => ({
url: "/search-product",
params: { q: query },
}),
}),
searchProductByBarcode: builder.query({
query: (barcode) => ({
url: "/search-product-barcode",
params: { barcode },
}),
}),
}),
});
export const { useSearchProductQuery, useSearchProductByBarcodeQuery } =
searchApi;

52
src/app/store.js Normal file
View File

@@ -0,0 +1,52 @@
import { configureStore } from "@reduxjs/toolkit";
import { baseApi } from "./api/baseApi";
import { categoriesApi } from "./api/categories";
import { searchApi } from "./api/searchApi";
import { cartApi } from "./api/cartApi";
import { brandsApi } from "./api/brandsApi";
import { collectionsApi } from "./api/collectionsApi";
import { favoritesApi } from "./api/favoritesApi";
import { legalPagesApi } from "./api/legalPagesApi";
import { locationApi } from "./api/locationApi";
import { orderApi } from "./api/orderApi";
import { mediaApi } from "./api/bannersApi";
import { reviewsApi } from "./api/reviewApi";
import { profileApi } from "./api/myProfileApi";
import { contactApi } from "./api/contactUs";
const store = configureStore({
reducer: {
[baseApi.reducerPath]: baseApi.reducer,
[categoriesApi.reducerPath]: categoriesApi.reducer,
[searchApi.reducerPath]: searchApi.reducer,
[cartApi.reducerPath]: cartApi.reducer,
[brandsApi.reducerPath]: brandsApi.reducer,
[collectionsApi.reducerPath]: collectionsApi.reducer,
[favoritesApi.reducerPath]: favoritesApi.reducer,
[legalPagesApi.reducerPath]: legalPagesApi.reducer,
[locationApi.reducerPath]: locationApi.reducer,
[orderApi.reducerPath]: orderApi.reducer,
[mediaApi.reducerPath]: mediaApi.reducer,
[reviewsApi.reducerPath]: reviewsApi.reducer,
[profileApi.reducerPath]: profileApi.reducer,
[contactApi.reducerPath]: contactApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(
baseApi.middleware,
categoriesApi.middleware,
searchApi.middleware,
brandsApi.middleware,
collectionsApi.middleware,
favoritesApi.middleware,
legalPagesApi.middleware,
locationApi.middleware,
orderApi.middleware,
reviewsApi.middleware,
mediaApi.middleware,
profileApi.middleware,
contactApi.middleware,
),
});
export default store;

BIN
src/assets/apk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/appstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/brands/5401.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
src/assets/brands/5417.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/brands/5420.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/brands/5421.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
src/assets/brands/5514.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/assets/brands/5522.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/assets/brands/5529.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/brands/5705.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/brands/5710.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
src/assets/brands/5715.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/assets/brands/5720.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
src/assets/brands/5735.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/assets/brands/5745.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
src/assets/brands/5792.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
src/assets/brands/5800.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
src/assets/brands/6500.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
src/assets/cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/en.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-3"><path d="M1 13L8.61452 18.5563C9.42553 19.1479 10.5632 19.1479 11.3855 18.5563L19 13" stroke="white" stroke-linecap="round"></path><path d="M1 11L8.61452 16.5563C9.42553 17.1479 10.5632 17.1479 11.3855 16.5563L19 11" stroke="white" stroke-linecap="round"></path><path d="M11.1809 14.4984L11.1809 14.4984L11.1776 14.5008C10.4942 15.0041 9.51502 15.0046 8.83071 14.5094L0.763017 8.66938C0.762807 8.66923 0.762596 8.66908 0.762386 8.66892C0.428113 8.42444 0.428163 7.96739 0.762536 7.72297C0.762696 7.72286 0.762857 7.72274 0.763017 7.72262L8.82806 1.87339C8.82854 1.87305 8.82902 1.87271 8.8295 1.87236C9.52822 1.37467 10.4981 1.37738 11.181 1.87149L19.2487 7.71152C19.2489 7.71168 19.2491 7.71185 19.2493 7.71202C19.5835 7.95646 19.5835 8.41334 19.2493 8.65777C19.2491 8.65794 19.2489 8.65811 19.2487 8.65828L11.1809 14.4984Z" stroke="white"></path></svg>

After

Width:  |  Height:  |  Size: 939 B

View File

@@ -0,0 +1 @@
<svg class="icon-basket w-5" viewBox="0 0 19 16" fill="none" xmlns="http://www.w3.org/2000/svg" data-v-bf54e7ab=""><path class="cls-1" d="M16.5 15.355C17.3284 15.355 18 14.6834 18 13.855C18 13.0266 17.3284 12.355 16.5 12.355C15.6716 12.355 15 13.0266 15 13.855C15 14.6834 15.6716 15.355 16.5 15.355Z" data-v-bf54e7ab=""></path><path class="cls-1" d="M5.625 15.355C6.45342 15.355 7.12499 14.6834 7.12499 13.855C7.12499 13.0266 6.45342 12.355 5.625 12.355C4.79657 12.355 4.125 13.0266 4.125 13.855C4.125 14.6834 4.79657 15.355 5.625 15.355Z" data-v-bf54e7ab=""></path><path class="cls-1" d="M17.6249 10.48H5.91411L6.16423 10.0742C6.27148 9.9002 6.30298 9.68983 6.25161 9.49183L6.00748 8.55208L16.8761 7.98733C17.2882 7.96633 17.6249 7.61121 17.6249 7.19871V2.22997C17.6249 1.81748 17.2874 1.47998 16.875 1.47998H4.16961L4.02299 0.916354C3.98122 0.755602 3.88727 0.613263 3.75587 0.511665C3.62448 0.410066 3.46308 0.354957 3.29699 0.35498H0.749998C0.551086 0.35498 0.360321 0.433998 0.219669 0.57465C0.0790174 0.715302 0 0.906066 0 1.10498C0 1.30389 0.0790174 1.49465 0.219669 1.63531C0.360321 1.77596 0.551086 1.85498 0.749998 1.85498H2.71724L4.71974 9.55933L3.93224 10.8362C3.86219 10.9498 3.82376 11.0801 3.82093 11.2135C3.81809 11.3469 3.85096 11.4787 3.91611 11.5952C3.98101 11.7118 4.07589 11.809 4.19094 11.8766C4.30599 11.9443 4.43702 11.9799 4.57049 11.9799H17.6249C17.8239 11.9799 18.0146 11.9009 18.1553 11.7603C18.2959 11.6196 18.3749 11.4289 18.3749 11.2299C18.3749 11.031 18.2959 10.8403 18.1553 10.6996C18.0146 10.559 17.8239 10.48 17.6249 10.48Z" data-v-bf54e7ab=""></path><path class="cls-2" d="M16.5 15.355C17.3284 15.355 18 14.6834 18 13.855C18 13.0266 17.3284 12.355 16.5 12.355C15.6716 12.355 15 13.0266 15 13.855C15 14.6834 15.6716 15.355 16.5 15.355Z" data-v-bf54e7ab=""></path><path class="cls-2" d="M5.625 15.355C6.45342 15.355 7.12499 14.6834 7.12499 13.855C7.12499 13.0266 6.45342 12.355 5.625 12.355C4.79657 12.355 4.125 13.0266 4.125 13.855C4.125 14.6834 4.79657 15.355 5.625 15.355Z" data-v-bf54e7ab=""></path><path class="cls-2" d="M17.6249 10.48H5.91411L6.16423 10.0742C6.27148 9.9002 6.30298 9.68983 6.25161 9.49183L6.00748 8.55208L16.8761 7.98733C17.2882 7.96633 17.6249 7.61121 17.6249 7.19871V2.22997C17.6249 1.81748 17.2874 1.47998 16.875 1.47998H4.16961L4.02299 0.916354C3.98122 0.755602 3.88727 0.613263 3.75587 0.511665C3.62448 0.410066 3.46308 0.354957 3.29699 0.35498H0.749998C0.551086 0.35498 0.360321 0.433998 0.219669 0.57465C0.0790174 0.715302 0 0.906066 0 1.10498C0 1.30389 0.0790174 1.49465 0.219669 1.63531C0.360321 1.77596 0.551086 1.85498 0.749998 1.85498H2.71724L4.71974 9.55933L3.93224 10.8362C3.86219 10.9498 3.82376 11.0801 3.82093 11.2135C3.81809 11.3469 3.85096 11.4787 3.91611 11.5952C3.98101 11.7118 4.07589 11.809 4.19094 11.8766C4.30599 11.9443 4.43702 11.9799 4.57049 11.9799H17.6249C17.8239 11.9799 18.0146 11.9009 18.1553 11.7603C18.2959 11.6196 18.3749 11.4289 18.3749 11.2299C18.3749 11.031 18.2959 10.8403 18.1553 10.6996C18.0146 10.559 17.8239 10.48 17.6249 10.48Z" data-v-bf54e7ab=""></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1 @@
<svg class="icon-category h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212.6 212.6" data-v-c2fd1ba4=""><path class="cls-1" d="M77.4,112.51H42.61a22.79,22.79,0,0,0-22.76,22.76v34.79a22.79,22.79,0,0,0,22.76,22.77H77.4a22.79,22.79,0,0,0,22.77-22.77V135.27A22.79,22.79,0,0,0,77.4,112.51ZM88,170.06A10.56,10.56,0,0,1,77.4,180.61H42.61a10.56,10.56,0,0,1-10.54-10.55V135.27a10.55,10.55,0,0,1,10.54-10.54H77.4A10.56,10.56,0,0,1,88,135.27Z" data-v-c2fd1ba4=""></path><path class="cls-1" d="M77.4,19.77H42.61A22.79,22.79,0,0,0,19.85,42.54V77.32a22.79,22.79,0,0,0,22.76,22.77H77.4a22.79,22.79,0,0,0,22.77-22.77V42.54A22.79,22.79,0,0,0,77.4,19.77ZM88,77.32A10.56,10.56,0,0,1,77.4,87.87H42.61A10.56,10.56,0,0,1,32.07,77.32V42.54A10.56,10.56,0,0,1,42.61,32H77.4A10.56,10.56,0,0,1,88,42.54Z" data-v-c2fd1ba4=""></path><path class="cls-1" d="M170,112.51H135.2a22.79,22.79,0,0,0-22.77,22.76v34.79a22.79,22.79,0,0,0,22.77,22.77H170a22.79,22.79,0,0,0,22.77-22.77V135.27A22.79,22.79,0,0,0,170,112.51Zm10.55,57.55A10.56,10.56,0,0,1,170,180.61H135.2a10.56,10.56,0,0,1-10.55-10.55V135.27a10.56,10.56,0,0,1,10.55-10.54H170a10.56,10.56,0,0,1,10.55,10.54Z" data-v-c2fd1ba4=""></path><path class="cls-1" d="M170,19.77H135.2a22.79,22.79,0,0,0-22.77,22.77V77.32a22.79,22.79,0,0,0,22.77,22.77H170a22.79,22.79,0,0,0,22.77-22.77V42.54A22.79,22.79,0,0,0,170,19.77Zm10.55,57.55A10.56,10.56,0,0,1,170,87.87H135.2a10.56,10.56,0,0,1-10.55-10.55V42.54A10.56,10.56,0,0,1,135.2,32H170a10.56,10.56,0,0,1,10.55,10.55Z" data-v-c2fd1ba4=""></path><rect class="cls-2" x="19.83" y="112.51" width="80.34" height="80.34" rx="22.77" data-v-c2fd1ba4=""></rect><rect class="cls-2" x="19.83" y="19.75" width="80.34" height="80.34" rx="22.77" data-v-c2fd1ba4=""></rect><rect class="cls-2" x="112.43" y="112.51" width="80.34" height="80.34" rx="22.77" data-v-c2fd1ba4=""></rect><rect class="cls-2" x="112.43" y="19.75" width="80.34" height="80.34" rx="22.77" data-v-c2fd1ba4=""></rect></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212 212" class="h-4.5"><path id="path" d="M 94.43 101.71 C 104.598 101.712 114.471 98.26 122.422 91.922 C 130.373 85.584 135.939 76.729 138.203 66.816 C 140.467 56.903 139.297 46.51 134.887 37.348 C 130.476 28.187 123.081 20.79 113.921 16.377 C 104.76 11.965 94.367 10.793 84.454 13.055 C 74.541 15.317 65.684 20.881 59.344 28.83 C 53.005 36.78 49.55 46.652 49.55 56.82 C 49.563 68.715 54.299 80.132 62.709 88.544 C 71.119 96.956 82.535 101.694 94.43 101.71 Z M 94.43 25.26 C 101.579 25.258 108.52 27.684 114.111 32.14 C 119.701 36.596 123.615 42.821 125.207 49.79 C 126.799 56.759 125.978 64.067 122.877 70.508 C 119.777 76.95 114.578 82.15 108.137 85.253 C 101.697 88.355 94.39 89.179 87.42 87.589 C 80.45 85.999 74.224 82.087 69.766 76.498 C 65.309 70.91 62.88 63.969 62.88 56.82 C 62.891 48.458 66.22 40.433 72.132 34.519 C 78.044 28.605 86.068 25.273 94.43 25.26 Z" fill="currentColor"></path><path id="path_1" d="M 94.43 118.88 C 42.93 118.88 17.86 139.88 7.51 152.41 C 2.684 158.273 0.046 165.636 0.05 173.23 L 0.05 181.32 C 0.055 186.448 2.097 191.37 5.723 194.997 C 9.35 198.623 14.272 200.665 19.4 200.67 L 169.47 200.67 C 174.598 200.665 179.52 198.623 183.147 194.997 C 186.773 191.37 188.815 186.448 188.82 181.32 L 188.82 173.23 C 188.824 165.636 186.186 158.273 181.36 152.41 C 171 139.89 145.94 118.88 94.43 118.88 Z M 175.49 181.32 C 175.49 182.911 174.857 184.438 173.733 185.563 C 172.608 186.687 171.081 187.32 169.49 187.32 L 19.4 187.32 C 17.809 187.32 16.282 186.687 15.157 185.563 C 14.033 184.438 13.4 182.911 13.4 181.32 L 13.4 173.23 C 13.392 168.738 14.948 164.381 17.8 160.91 C 26.67 150.19 48.41 132.21 94.45 132.21 C 140.49 132.21 162.24 150.21 171.11 160.91 C 173.957 164.383 175.513 168.739 175.51 173.23 Z" fill="currentColor"></path><path id="path_2" d="M 205.88 48 L 194.16 48 L 194.16 36.27 C 194.216 34.827 193.802 33.405 192.981 32.218 C 192.16 31.03 190.975 30.141 189.605 29.684 C 188.236 29.228 186.754 29.228 185.385 29.684 C 184.015 30.141 182.83 31.03 182.009 32.218 C 181.188 33.405 180.774 34.827 180.83 36.27 L 180.83 48 L 169.11 48 C 167.388 48.067 165.756 48.799 164.561 50.041 C 163.366 51.283 162.698 52.941 162.698 54.665 C 162.698 56.389 163.366 58.047 164.561 59.289 C 165.756 60.531 167.388 61.263 169.11 61.33 L 180.83 61.33 L 180.83 73 C 180.774 74.443 181.188 75.865 182.009 77.052 C 182.83 78.24 184.015 79.129 185.385 79.586 C 186.754 80.042 188.236 80.042 189.605 79.586 C 190.975 79.129 192.16 78.24 192.981 77.052 C 193.802 75.865 194.216 74.443 194.16 73 L 194.16 61.32 L 205.88 61.32 C 207.323 61.376 208.745 60.962 209.932 60.141 C 211.12 59.32 212.009 58.135 212.466 56.765 C 212.922 55.396 212.922 53.914 212.466 52.545 C 212.009 51.175 211.12 49.99 209.932 49.169 C 208.745 48.348 207.323 47.934 205.88 47.99 Z" fill="currentColor"></path></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1 @@
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.35 28.35" class="h-5.5" data-v-5c1608dd=""><path class="cls-1" d="M24.4,16a2.37,2.37,0,0,0-.94-.59V14.22h0v-2.7h0V9a2.27,2.27,0,0,0-.29-1.11L21.72,5.33a2.27,2.27,0,0,0-2-1.15H11A2.27,2.27,0,0,0,9,5.33L7.52,7.91A2.38,2.38,0,0,0,7.23,9v2.5h0v2h0v.81H4.68a1.46,1.46,0,0,0-1.45,1.46v6.86a1.45,1.45,0,0,0,1.45,1.45H7.2a1.46,1.46,0,0,0,1.28-.78h1.68a2.69,2.69,0,0,1,.57.06l3.55.71a4.09,4.09,0,0,0,.85.09,4.24,4.24,0,0,0,.94-.11l4.64-1,3.65-3.5a2.43,2.43,0,0,0,0-3.49Zm-7.17-1.14-.06,0-3.89-.63a4.34,4.34,0,0,0-2.88.55l-1.75.8V11.52a1,1,0,0,1,1-1H21a1,1,0,0,1,1,1v2h0v1.87a2.45,2.45,0,0,0-.94.5L19,17.68c0-.11,0-.23,0-.36A2.64,2.64,0,0,0,17.23,14.89ZM20.49,6,21.93,8.6A.78.78,0,0,1,22,9v.29a2.51,2.51,0,0,0-1-.23h-5V5.59h3.7A.86.86,0,0,1,20.49,6ZM8.76,8.6,10.2,6A.86.86,0,0,1,11,5.59h3.69V9.08h-5a2.51,2.51,0,0,0-1,.23V9A.78.78,0,0,1,8.76,8.6Zm-1.53,14s0,0,0,0H4.68a0,0,0,0,1,0,0V15.78a0,0,0,0,1,0,0H7.2s0,0,0,0v6.86ZM23.39,18.5,20,21.73l-4.26,1a2.85,2.85,0,0,1-1.2,0L11,22a4.8,4.8,0,0,0-.85-.08H8.65V17.14L11,16.06l.08,0a2.87,2.87,0,0,1,2-.38l3.75.6a1.21,1.21,0,0,1,.76,1.08c0,.44,0,1.18-1.77,1.18H14.54a.7.7,0,0,0-.7.71.7.7,0,0,0,.7.7h4L22,17A1,1,0,0,1,23.4,17a1,1,0,0,1,.3.74A1,1,0,0,1,23.39,18.5Z" data-v-5c1608dd=""></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212 212" class="h-5"><path id="path" d="M 78.76 21.76 L 45.92 21.76 C 39.359 21.765 33.062 24.376 28.421 29.014 C 23.781 33.653 21.168 39.949 21.16 46.51 L 21.16 68.36 C 21.227 70.082 21.959 71.714 23.201 72.909 C 24.443 74.104 26.101 74.772 27.825 74.772 C 29.549 74.772 31.207 74.104 32.449 72.909 C 33.691 71.714 34.423 70.082 34.49 68.36 L 34.49 46.51 C 34.493 43.481 35.699 40.575 37.841 38.434 C 39.984 36.294 42.891 35.09 45.92 35.09 L 78.76 35.09 C 80.482 35.023 82.114 34.291 83.309 33.049 C 84.504 31.807 85.172 30.149 85.172 28.425 C 85.172 26.701 84.504 25.043 83.309 23.801 C 82.114 22.559 80.482 21.827 78.76 21.76 Z" fill="currentColor"></path><path id="path_1" d="M 78.76 177.59 L 45.92 177.59 C 42.891 177.587 39.983 176.381 37.841 174.239 C 35.699 172.097 34.493 169.189 34.49 166.16 L 34.49 144.31 C 34.423 142.588 33.691 140.956 32.449 139.761 C 31.207 138.566 29.549 137.898 27.825 137.898 C 26.101 137.898 24.443 138.566 23.201 139.761 C 21.959 140.956 21.227 142.588 21.16 144.31 L 21.16 166.16 C 21.168 172.721 23.781 179.017 28.421 183.656 C 33.062 188.294 39.359 190.905 45.92 190.91 L 78.76 190.91 C 80.526 190.91 82.221 190.208 83.469 188.959 C 84.718 187.711 85.42 186.016 85.42 184.25 C 85.42 182.484 84.718 180.789 83.469 179.541 C 82.221 178.292 80.526 177.59 78.76 177.59 Z" fill="currentColor"></path><path id="path_2" d="M 21.16 106.3 C 21.155 107.474 21.46 108.629 22.044 109.648 C 22.628 110.666 23.47 111.513 24.486 112.101 C 25.502 112.69 26.656 113 27.83 113 L 60.24 113 L 50.43 122.81 C 49.429 123.803 48.769 125.089 48.545 126.481 C 48.321 127.874 48.545 129.301 49.183 130.559 C 49.822 131.816 50.844 132.838 52.1 133.478 C 53.357 134.118 54.784 134.343 56.177 134.121 C 57.569 133.898 58.856 133.24 59.85 132.24 L 81 111 C 81.828 110.174 82.425 109.144 82.728 108.015 C 83.031 106.885 83.031 105.695 82.728 104.565 C 82.425 103.436 81.828 102.406 81 101.58 L 59.85 80.4 C 58.599 79.158 56.904 78.462 55.141 78.465 C 53.378 78.469 51.686 79.172 50.44 80.42 C 49.194 81.667 48.492 83.36 48.49 85.123 C 48.489 86.886 49.187 88.58 50.43 89.83 L 60.24 99.64 L 27.83 99.64 C 26.063 99.64 24.367 100.342 23.117 101.59 C 21.867 102.838 21.163 104.533 21.16 106.3 Z" fill="currentColor"></path><path id="path_3" d="M 173.13 24.34 L 130.67 12.89 C 125.763 11.565 120.564 11.784 115.785 13.516 C 111.006 15.249 106.875 18.412 103.957 22.574 C 101.038 26.735 99.471 31.697 99.47 36.78 L 99.47 175.78 C 99.475 182.331 102.077 188.621 106.702 193.261 C 111.328 197.901 117.609 200.524 124.16 200.55 C 126.359 200.546 128.547 200.254 130.67 199.68 L 173.13 188.23 C 178.38 186.807 183.019 183.694 186.327 179.376 C 189.635 175.058 191.432 169.77 191.44 164.33 L 191.44 48.24 C 191.434 42.8 189.637 37.51 186.329 33.192 C 183.02 28.874 178.381 25.762 173.13 24.34 Z M 178.13 164.34 C 178.117 166.845 177.284 169.277 175.758 171.264 C 174.232 173.25 172.097 174.682 169.68 175.34 L 127.2 186.85 C 124.937 187.461 122.54 187.362 120.336 186.565 C 118.131 185.769 116.224 184.313 114.875 182.396 C 113.525 180.48 112.797 178.194 112.79 175.85 L 112.79 36.85 C 112.793 33.826 113.994 30.923 116.13 28.781 C 118.266 26.64 121.166 25.431 124.19 25.42 C 125.203 25.421 126.212 25.555 127.19 25.82 L 169.66 37.2 C 172.077 37.858 174.212 39.29 175.738 41.276 C 177.264 43.263 178.097 45.695 178.11 48.2 Z" fill="currentColor"></path><path id="path_4" d="M 135.41 88.1 C 133.644 88.1 131.949 88.802 130.701 90.051 C 129.452 91.299 128.75 92.994 128.75 94.76 L 128.75 117.84 C 128.817 119.562 129.549 121.194 130.791 122.389 C 132.033 123.584 133.691 124.252 135.415 124.252 C 137.139 124.252 138.797 123.584 140.039 122.389 C 141.281 121.194 142.013 119.562 142.08 117.84 L 142.08 94.76 C 142.077 92.993 141.373 91.298 140.123 90.05 C 138.873 88.802 137.177 88.1 135.41 88.1 Z" fill="currentColor"></path></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 13 18" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-3"><path d="M0.718983 5.9688L5.57061 1.11719H5.57452L5.72096 0.970741C6.01398 0.677722 6.49037 0.677722 6.78339 0.970741L11.7834 5.97074C11.9993 6.18669 12.0624 6.50623 11.9449 6.78816C11.8266 7.07225 11.5544 7.25391 11.2502 7.25391H1.25022C0.94906 7.25391 0.673532 7.07141 0.55551 6.78816C0.43925 6.50914 0.502634 6.18818 0.718162 5.96963C0.718436 5.96936 0.718709 5.96908 0.718983 5.9688ZM5.72096 17.0332L0.720962 12.0332C0.505016 11.8172 0.441944 11.4977 0.559416 11.2157C0.677785 10.9317 0.949955 10.75 1.25413 10.75H11.2502C11.5514 10.75 11.8269 10.9325 11.9449 11.2157C12.0612 11.4947 11.9978 11.8156 11.7824 12.0342C11.7821 12.0345 11.7818 12.0348 11.7815 12.0351L6.78339 17.0332C6.49037 17.3262 6.01398 17.3262 5.72096 17.0332Z" stroke="white"></path></svg>

After

Width:  |  Height:  |  Size: 846 B

BIN
src/assets/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/assets/logo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
src/assets/logoFooter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/assets/order.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
src/assets/order.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/assets/playstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

1
src/assets/react.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/assets/ru.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

BIN
src/assets/slider/temp1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
src/assets/slider/temp2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
src/assets/slider/temp3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
src/assets/slider/temp4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
src/assets/slider/temp5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
src/assets/slider/temp6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
src/assets/slider/temp7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
src/assets/slider/temp8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
src/assets/temp1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/temp2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/temp3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
src/assets/tm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/assets/track.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
src/assets/wishlist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,93 @@
.addressSelectContainer {
:global(.ant-btn) {
background-color: #fff;
color: #000 !important;
border-color: #9ca3af !important;
width: 100%;
border-radius: 4px !important;
height: 37.6px !important;
font-size: 14px;
display: flex;
justify-content: flex-start;
padding: 10px;
box-shadow: none;
&:active {
background: #fff !important;
}
&:hover {
background: #ffffff !important;
}
}
}
.customClearIcon {
width: 20px;
height: 20px;
cursor: pointer;
opacity: 1 !important;
}
.optionList {
max-height: 250px;
overflow-y: auto;
}
.optionItem {
padding: 5px 10px;
cursor: pointer;
border-bottom: 1px solid #e8e8e8;
&:hover {
background-color: #f5f5f5;
}
}
.selectedAddress {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
:global {
.ant-select {
&.ant-select-focused {
.ant-select-selector {
border-color: #2563eb !important;
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1) !important;
}
}
.ant-select-selector {
height: 37.6px !important;
padding: 10px !important;
border-color: #9ca3af !important;
border-radius: 4px !important;
.ant-select-selection-search-input {
height: 37.6px !important;
}
.ant-select-selection-item {
height: 28px !important;
}
}
}
}
.addressSelect {
:global(.ant-select-clear) {
display: flex;
align-items: center;
width: 20px;
height: 20px;
opacity: 1 !important;
background: none;
color: #9ca3af !important;
&:hover {
opacity: 1 !important;
color: #9ca3af !important;
}
}
:global(.ant-select-arrow) {
height: 20px !important;
}
}

View File

@@ -0,0 +1,111 @@
import React, { useState } from "react";
import { Select, Modal, Button } from "antd";
import { X } from "lucide-react";
import styles from "./AddressSelect.module.scss";
const { Option } = Select;
const AddressSelect = ({
selectedAddress,
handleAddressSelect,
handleClearAddress,
deviceType,
locations,
}) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleClear = (e) => {
e.stopPropagation();
handleClearAddress();
};
const handleCancel = () => {
setIsModalVisible(false);
};
return (
<div className={styles.addressSelectContainer}>
{deviceType === "mobile" ? (
<>
<Button type="primary" onClick={showModal}>
{selectedAddress ? (
<div className={styles.selectedAddress}>
{selectedAddress}
<X className={styles.customClearIcon} onClick={handleClear} />
</div>
) : (
"Sargyt Salgynyz Saýlaň"
)}
</Button>
<Modal
title="Salgynyz Saýlaň"
open={isModalVisible}
onCancel={handleCancel}
closeIcon={<X />}
footer={null}
>
<ul className={styles.optionList}>
{Array.isArray(locations) && locations.length > 0 ? (
locations.map((location) => (
<li
key={location.id}
onClick={() => {
handleAddressSelect(location.name);
setIsModalVisible(false);
}}
className={styles.optionItem}
>
{location.name}
</li>
))
) : (
<li>Loading...</li>
)}
</ul>
</Modal>
</>
) : (
<Select
showSearch
placeholder="Salgynyz saýlaň"
onChange={handleAddressSelect}
value={selectedAddress}
style={{ width: "100%" }}
allowClear={{
clearIcon: <X className={styles.customClearIcon} />,
}}
onClear={handleClearAddress}
className={styles.addressSelect}
showArrow={!selectedAddress}
loading={!locations}
dropdownRender={(menu) => (
<div
style={{
maxHeight: "150px",
fontSize: "16px",
}}
>
{menu}
</div>
)}
>
{Array.isArray(locations) && locations.length > 0 ? (
locations.map((location) => (
<Option key={location.id} value={location.name}>
{location.name}
</Option>
))
) : (
<Option disabled></Option>
)}
</Select>
)}
</div>
);
};
export default AddressSelect;

View File

@@ -0,0 +1,109 @@
.carouselContainer {
width: 100%;
max-width: 1366px;
padding: 0 0.75rem 0 0.75rem;
display: flex;
gap: 10px;
box-sizing: border-box;
aspect-ratio: 16/6;
@media screen and (max-width: 1024px) {
aspect-ratio: 16/7;
}
@media screen and (max-width: 768px) {
padding: 0;
}
}
.mainSlider {
flex: 3.28;
height: 100%;
border-radius: 10px;
img {
width: 100%;
height: 100%;
object-fit: fill;
}
}
.thumbSlider {
flex: 0.72;
overflow-y: auto;
height: 100%;
scroll-behavior: smooth;
@media screen and (max-width: 767px) {
display: none;
}
:global(.swiper) {
height: 100%;
// max-height: 100%;
}
:global(.swiper-wrapper) {
height: auto;
display: flex;
flex-direction: column;
}
:global(.swiper-slide) {
height: 100% !important;
width: 100% !important;
margin-bottom: 8px;
}
&::-webkit-scrollbar {
width: 7px;
}
&::-webkit-scrollbar-track {
background: #e5e7eb;
}
&::-webkit-scrollbar-thumb {
background: #d1d5db;
border-radius: 3px;
}
.thumbWrapper {
position: relative;
height: 100%;
img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
cursor: pointer;
}
.progressBarImg {
position: absolute;
bottom: 0;
left: 0;
height: 5px;
width: 100%;
background: rgba(57, 47, 47, 0.5);
animation: progress 3s linear;
height: 100%;
border-radius: 10px;
}
.progressBar {
position: absolute;
bottom: 0;
left: 0;
height: 5px;
width: 100%;
background: rgba(207, 12, 12, 0.5);
animation: progress 3s linear infinite;
border-end-end-radius: 10px;
border-end-start-radius: 10px;
}
}
}
@keyframes progress {
from {
width: 0;
}
to {
width: 100%;
}
}

Some files were not shown because too many files have changed in this diff Show More