├── .gitattributes ├── src ├── pages │ ├── user │ │ ├── payment │ │ │ └── index.tsx │ │ ├── profile │ │ │ ├── components │ │ │ │ ├── style.scss │ │ │ │ └── button-menu.tsx │ │ │ ├── order │ │ │ │ ├── list.tsx │ │ │ │ └── single.tsx │ │ │ ├── index.tsx │ │ │ ├── home.tsx │ │ │ ├── addresses │ │ │ │ ├── add.tsx │ │ │ │ ├── list.tsx │ │ │ │ └── edit.tsx │ │ │ └── edit.tsx │ │ ├── categories │ │ │ └── list.tsx │ │ ├── product │ │ │ └── list.tsx │ │ ├── cart │ │ │ └── index.tsx │ │ └── checkout │ │ │ └── index.tsx │ ├── admin │ │ ├── orders │ │ │ ├── list.tsx │ │ │ └── single.tsx │ │ ├── index.tsx │ │ ├── product │ │ │ ├── list.tsx │ │ │ └── add.tsx │ │ ├── slider │ │ │ ├── list.tsx │ │ │ └── add.tsx │ │ └── categories │ │ │ ├── add.tsx │ │ │ ├── list.tsx │ │ │ └── edit.tsx │ ├── bot │ │ ├── index.tsx │ │ ├── components │ │ │ └── button-menu.tsx │ │ ├── masters │ │ │ ├── list.tsx │ │ │ ├── add.tsx │ │ │ └── edit.tsx │ │ └── setting │ │ │ └── index.tsx │ ├── home.tsx │ └── index.tsx ├── vite-env.d.ts ├── assets │ ├── images │ │ ├── chad.jpg │ │ └── logo.svg │ └── styles │ │ ├── counter.scss │ │ ├── app.scss │ │ └── index.css ├── components │ ├── product │ │ ├── index.tsx │ │ ├── card.tsx │ │ ├── item.tsx │ │ └── list.tsx │ ├── skeleton │ │ ├── product-card.tsx │ │ ├── user-single-product.tsx │ │ └── products.tsx │ ├── container │ │ └── index.tsx │ └── discount │ │ └── index.tsx ├── hooks │ ├── useTelegram.ts │ ├── useIsReadyTelegram.ts │ └── useTelegramUser.ts ├── framework │ ├── api │ │ ├── utils │ │ │ ├── api-endpoints.ts │ │ │ └── api-config.ts │ │ ├── master │ │ │ ├── add.ts │ │ │ ├── get.ts │ │ │ ├── update.ts │ │ │ ├── delete.ts │ │ │ └── get-by-id.ts │ │ ├── orders │ │ │ ├── get.ts │ │ │ ├── update.ts │ │ │ ├── get-by-user.ts │ │ │ ├── getById.ts │ │ │ └── add.ts │ │ ├── slider │ │ │ ├── add.ts │ │ │ ├── get.ts │ │ │ └── delete.ts │ │ ├── product │ │ │ ├── add.ts │ │ │ ├── update.ts │ │ │ ├── delete.ts │ │ │ ├── get-by-id.ts │ │ │ └── get.ts │ │ ├── discount │ │ │ ├── add.ts │ │ │ ├── delete.ts │ │ │ └── update.ts │ │ ├── cart │ │ │ ├── clear.ts │ │ │ ├── add.ts │ │ │ ├── get.ts │ │ │ └── delete.ts │ │ ├── bot-setting │ │ │ ├── update.ts │ │ │ └── get.ts │ │ ├── user-information │ │ │ ├── update.ts │ │ │ └── get.ts │ │ ├── photos-upload │ │ │ ├── add-master.ts │ │ │ ├── add.ts │ │ │ └── add-slider.ts │ │ ├── receipt-photos │ │ │ └── add.ts │ │ ├── categories │ │ │ ├── delete.ts │ │ │ ├── add.ts │ │ │ ├── update.ts │ │ │ └── get.ts │ │ └── address │ │ │ ├── delete.ts │ │ │ ├── add.ts │ │ │ ├── update.ts │ │ │ └── get.ts │ └── types.ts ├── router │ ├── index.tsx │ └── routes.tsx ├── main.tsx ├── helpers │ ├── getFileBase64.tsx │ └── get-order-status.ts ├── containers │ ├── 404.tsx │ ├── hero-slider.tsx │ ├── product-news.tsx │ ├── order │ │ ├── single.tsx │ │ ├── components │ │ │ ├── order-list.tsx │ │ │ ├── customer-detail.tsx │ │ │ └── order-setting.tsx │ │ ├── list-admin.tsx │ │ └── list.tsx │ └── boxes.tsx ├── layouts │ ├── header.tsx │ └── main.tsx └── App.tsx ├── public ├── hotdog.png ├── images │ ├── slide-1.jpg │ ├── slide-2.jpg │ └── slide-3.jpg ├── fonts │ ├── digi-sarvenaz.ttf │ ├── digi-sarvenaz.woff │ ├── IRANSansWeb(FaNum).eot │ ├── IRANSansWeb(FaNum).ttf │ ├── IRANSansWeb(FaNum).woff │ └── IRANSansWeb(FaNum).woff2 └── vite.svg ├── postcss.config.cjs ├── screenshots ├── app-preview.png └── extentions.png ├── .stylelintrc.cjs ├── tailwind.config.cjs ├── tsconfig.node.json ├── .prettierrc.cjs ├── .gitignore ├── vite.config.ts ├── .vscode ├── extensions.json └── settings.json ├── .editorconfig ├── index.html ├── tsconfig.json ├── .drone.yml ├── .eslintrc.cjs ├── package.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /src/pages/user/payment/index.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/hotdog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/hotdog.png -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /public/images/slide-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/images/slide-1.jpg -------------------------------------------------------------------------------- /public/images/slide-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/images/slide-2.jpg -------------------------------------------------------------------------------- /public/images/slide-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/images/slide-3.jpg -------------------------------------------------------------------------------- /screenshots/app-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/screenshots/app-preview.png -------------------------------------------------------------------------------- /screenshots/extentions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/screenshots/extentions.png -------------------------------------------------------------------------------- /src/assets/images/chad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/src/assets/images/chad.jpg -------------------------------------------------------------------------------- /public/fonts/digi-sarvenaz.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/digi-sarvenaz.ttf -------------------------------------------------------------------------------- /public/fonts/digi-sarvenaz.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/digi-sarvenaz.woff -------------------------------------------------------------------------------- /public/fonts/IRANSansWeb(FaNum).eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/IRANSansWeb(FaNum).eot -------------------------------------------------------------------------------- /public/fonts/IRANSansWeb(FaNum).ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/IRANSansWeb(FaNum).ttf -------------------------------------------------------------------------------- /public/fonts/IRANSansWeb(FaNum).woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/IRANSansWeb(FaNum).woff -------------------------------------------------------------------------------- /public/fonts/IRANSansWeb(FaNum).woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mojtaba1180/telegram-web-app-shop/HEAD/public/fonts/IRANSansWeb(FaNum).woff2 -------------------------------------------------------------------------------- /src/pages/user/profile/components/style.scss: -------------------------------------------------------------------------------- 1 | .profile-menu { 2 | a.active { 3 | @apply bg-[var(--tg-theme-bg-color)]; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/components/product/index.tsx: -------------------------------------------------------------------------------- 1 | import card from "./card"; 2 | import item from "./item"; 3 | import list from "./list"; 4 | 5 | export { card, item, list }; 6 | -------------------------------------------------------------------------------- /.stylelintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('stylelint').Config} */ 2 | 3 | module.exports = { 4 | extends: ["stylelint-config-clean-order", "stylelint-config-prettier"] 5 | }; 6 | -------------------------------------------------------------------------------- /src/hooks/useTelegram.ts: -------------------------------------------------------------------------------- 1 | const useTelegram = () => { 2 | if (window.Telegram) { 3 | return window.Telegram.WebApp; 4 | } 5 | return null; 6 | }; 7 | export default useTelegram; 8 | -------------------------------------------------------------------------------- /src/hooks/useIsReadyTelegram.ts: -------------------------------------------------------------------------------- 1 | const useIsReadyTelegram = () => { 2 | if (window.Telegram) { 3 | return true; 4 | } 5 | return false; 6 | }; 7 | 8 | export default useIsReadyTelegram; 9 | -------------------------------------------------------------------------------- /src/framework/api/utils/api-endpoints.ts: -------------------------------------------------------------------------------- 1 | export const API_ENDPOINTS = { 2 | admin: { 3 | LAST_MOVIE: "/poster/by/filtres/0/0/created/0" 4 | }, 5 | user: { 6 | movies: "/movies/get" 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/pages/admin/orders/list.tsx: -------------------------------------------------------------------------------- 1 | import OrderListAdmin from "@containers/order/list-admin"; 2 | 3 | function AdminOrderList() { 4 | return ; 5 | } 6 | 7 | export default AdminOrderList; 8 | -------------------------------------------------------------------------------- /src/pages/user/profile/order/list.tsx: -------------------------------------------------------------------------------- 1 | import OrderList from "@containers/order/list"; 2 | 3 | function UserOrderList() { 4 | return ; 5 | } 6 | 7 | export default UserOrderList; 8 | -------------------------------------------------------------------------------- /src/assets/styles/counter.scss: -------------------------------------------------------------------------------- 1 | .counter { 2 | display: flex; 3 | flex-direction: row; 4 | gap: 4vmin; 5 | align-items: center; 6 | justify-content: center; 7 | 8 | margin-top: 12px; 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/admin/orders/single.tsx: -------------------------------------------------------------------------------- 1 | import OrdersSingle from "@containers/order/single"; 2 | 3 | function AdminOrdersSingle() { 4 | return ; 5 | } 6 | 7 | export default AdminOrdersSingle; 8 | -------------------------------------------------------------------------------- /src/pages/user/profile/order/single.tsx: -------------------------------------------------------------------------------- 1 | import OrdersSingle from "@containers/order/single"; 2 | 3 | function UserOrdersSingle() { 4 | return ; 5 | } 6 | export default UserOrdersSingle; 7 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | module.exports = { 4 | content: ["./src/**/*.{ts,tsx}", "./index.html"], 5 | theme: { 6 | extend: {} 7 | }, 8 | plugins: [] 9 | }; 10 | -------------------------------------------------------------------------------- /src/router/index.tsx: -------------------------------------------------------------------------------- 1 | import { RouterProvider } from "react-router-dom"; 2 | 3 | import { routes } from "./routes"; 4 | 5 | function Router() { 6 | return ; 7 | } 8 | 9 | export default Router; 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | 3 | module.exports = { 4 | printWidth: 80, 5 | tabWidth: 2, 6 | singleQuote: false, 7 | trailingComma: "none", 8 | bracketSpacing: true, 9 | jsxBracketSameLine: true, 10 | semi: true, 11 | useTabs: false, 12 | bracketSameLine: false 13 | }; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | .env 24 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import "@style/index.css"; 2 | 3 | import { createRoot } from "react-dom/client"; 4 | 5 | import App from "./App"; 6 | import { TelegramType } from "./types"; 7 | 8 | declare global { 9 | interface Window { 10 | Telegram: TelegramType; 11 | } 12 | } 13 | 14 | createRoot(document.getElementById("root") as HTMLElement).render(); 15 | -------------------------------------------------------------------------------- /src/components/skeleton/product-card.tsx: -------------------------------------------------------------------------------- 1 | function ProductCardSkeleton({ delay }: { delay: number }) { 2 | return ( 3 |
7 | ); 8 | } 9 | 10 | export default ProductCardSkeleton; 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('vite').UserConfig} */ 2 | 3 | import react from "@vitejs/plugin-react"; 4 | import { defineConfig } from "vite"; 5 | import tsconfigPaths from "vite-tsconfig-paths"; 6 | 7 | export default defineConfig({ 8 | plugins: [react(), tsconfigPaths()], 9 | server: { 10 | open: false, 11 | port: 3000 12 | }, 13 | build: { 14 | minify: "terser" 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "stylelint.vscode-stylelint", 6 | "editorconfig.editorconfig", 7 | "orta.vscode-jest", 8 | "styled-components.vscode-styled-components", 9 | "xabikos.ReactSnippets", 10 | "bradlc.vscode-tailwindcss", 11 | "formulahendry.auto-rename-tag", 12 | "miguelsolorio.fluent-icons", 13 | "orta.vscode-twoslash-queries" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/framework/api/master/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostMaster } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddMaster = () => 9 | useMutation({ 10 | mutationKey: ["add-master"], 11 | mutationFn: (props: TypePostMaster) => Api.post("/master", props) 12 | }); 13 | 14 | export default useAddMaster; 15 | -------------------------------------------------------------------------------- /src/framework/api/orders/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | import { TypeOrders } from "@framework/types"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const fetch = async ({ queryKey }: any) => { 8 | const [_key] = queryKey; 9 | const { data } = await Api.get("/orders"); 10 | return data as TypeOrders; 11 | }; 12 | 13 | export const useGetOrders = () => useQuery(["orders"], fetch); 14 | -------------------------------------------------------------------------------- /src/framework/api/slider/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostSlider } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddSlider = () => 9 | useMutation({ 10 | mutationKey: ["add-slider"], 11 | mutationFn: (props: TypePostSlider) => Api.post("/main_slider", props) 12 | }); 13 | 14 | export default useAddSlider; 15 | -------------------------------------------------------------------------------- /src/framework/api/product/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPost } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddProduct = () => 9 | useMutation({ 10 | mutationKey: ["add-Product"], 11 | mutationFn: (props: TypeProductPost) => Api.post("/products", props) 12 | }); 13 | 14 | export default useAddProduct; 15 | -------------------------------------------------------------------------------- /src/pages/bot/index.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router"; 2 | 3 | import UserProfileButtonMenu from "./components/button-menu"; 4 | 5 | function BotPanel() { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 | {/* */} 15 |
16 | ); 17 | } 18 | 19 | export default BotPanel; 20 | -------------------------------------------------------------------------------- /src/framework/api/discount/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostDiscount } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddDiscounts = () => 9 | useMutation({ 10 | mutationKey: ["add-discount"], 11 | mutationFn: (props: TypePostDiscount) => Api.post("/discounts", props) 12 | }); 13 | 14 | export default useAddDiscounts; 15 | -------------------------------------------------------------------------------- /src/framework/api/cart/clear.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeClearCart } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useClearCart = () => 9 | useMutation({ 10 | mutationKey: ["clear-cart"], 11 | mutationFn: ({ user_id }: TypeClearCart) => 12 | Api.delete(`/carts/${user_id}/clear`) 13 | }); 14 | 15 | export default useClearCart; 16 | -------------------------------------------------------------------------------- /src/framework/api/bot-setting/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeUpdateBotSetting } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateBotSetting = () => 9 | useMutation({ 10 | mutationKey: ["edit-bot-setting"], 11 | mutationFn: (props: TypeUpdateBotSetting) => Api.put("/", props) 12 | }); 13 | 14 | export default useUpdateBotSetting; 15 | -------------------------------------------------------------------------------- /src/framework/api/bot-setting/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | import { TypeBotSetting } from "@framework/types"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const fetch = async ({ queryKey }: any) => { 8 | const [_key] = queryKey; 9 | const { data } = await Api.get("/"); 10 | return data.botData as TypeBotSetting | null; 11 | }; 12 | export const useGetBotSetting = () => 13 | useQuery(["bot-setting"], fetch); 14 | -------------------------------------------------------------------------------- /src/pages/user/profile/index.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router"; 2 | 3 | import UserProfileButtonMenu from "./components/button-menu"; 4 | 5 | function UserProfile() { 6 | return ( 7 |
8 | {/*
*/} 9 | 10 | {/*
*/} 11 |
12 | 13 |
14 | {/* */} 15 |
16 | ); 17 | } 18 | 19 | export default UserProfile; 20 | -------------------------------------------------------------------------------- /src/framework/api/master/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeMasters } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const fetch = async ({ queryKey }: any) => { 9 | const [_key] = queryKey; 10 | const { data } = await Api.get("/master"); 11 | return data.masters as TypeMasters[]; 12 | }; 13 | 14 | export const useGetMasters = () => useQuery(["masters"], fetch); 15 | -------------------------------------------------------------------------------- /src/framework/api/slider/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeSlider } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const fetch = async ({ queryKey }: any) => { 9 | const [_key] = queryKey; 10 | const { data } = await Api.get("/main_slider"); 11 | return data.sliders as TypeSlider[]; 12 | }; 13 | 14 | export const useGetSliders = () => useQuery(["masters"], fetch); 15 | -------------------------------------------------------------------------------- /src/framework/api/master/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostMaster } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateMaster = ({ master_id }: { master_id: string }) => 9 | useMutation({ 10 | mutationKey: ["update-master"], 11 | mutationFn: (props: TypePostMaster) => 12 | Api.put(`/master/${master_id}`, props) 13 | }); 14 | 15 | export default useUpdateMaster; 16 | -------------------------------------------------------------------------------- /src/framework/api/user-information/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostUserInfo } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateUser = ({ user_id }: { user_id: string | number }) => 9 | useMutation({ 10 | mutationKey: ["update-user"], 11 | mutationFn: (props: TypePostUserInfo) => Api.put(`/users/${user_id}`, props) 12 | }); 13 | 14 | export default useUpdateUser; 15 | -------------------------------------------------------------------------------- /src/framework/api/photos-upload/add-master.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPhotos } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddMasterImage = () => 9 | useMutation({ 10 | mutationKey: ["add-Product-image"], 11 | mutationFn: ({ photo_base64 }: TypeProductPhotos) => 12 | Api.post("/master/photo", { photo_base64 }) 13 | }); 14 | 15 | export default useAddMasterImage; 16 | -------------------------------------------------------------------------------- /src/framework/api/photos-upload/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPhotos } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddProductImage = () => 9 | useMutation({ 10 | mutationKey: ["add-Product-image"], 11 | mutationFn: ({ photo_base64 }: TypeProductPhotos) => 12 | Api.post("/product_photos", { photo_base64 }) 13 | }); 14 | 15 | export default useAddProductImage; 16 | -------------------------------------------------------------------------------- /src/framework/api/receipt-photos/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPhotos } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddReceiptPhotos = () => 9 | useMutation({ 10 | mutationKey: ["add-receipt-photos"], 11 | mutationFn: ({ photo_base64 }: TypeProductPhotos) => 12 | Api.post("/receipt_photos", { photo_base64 }) 13 | }); 14 | 15 | export default useAddReceiptPhotos; 16 | -------------------------------------------------------------------------------- /src/helpers/getFileBase64.tsx: -------------------------------------------------------------------------------- 1 | export function getFileBase64(file: any) { 2 | return new Promise((resolve, reject) => { 3 | const reader = new FileReader(); 4 | const str = JSON.stringify(file); 5 | const bytes = new TextEncoder().encode(str); 6 | const blob = new Blob([bytes], { 7 | type: "application/json;charset=utf-8" 8 | }); 9 | reader.readAsDataURL(blob); 10 | reader.onload = () => { 11 | resolve(reader.result); 12 | }; 13 | reader.onerror = (error) => { 14 | reject(error); 15 | }; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/framework/api/cart/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeAddToCart } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddToCart = () => 9 | useMutation({ 10 | mutationKey: ["add-to-cart"], 11 | mutationFn: ({ user_id, cart_items }: TypeAddToCart) => 12 | Api.put("/carts", { 13 | user_id, 14 | cart_items 15 | }) 16 | }); 17 | 18 | export default useAddToCart; 19 | -------------------------------------------------------------------------------- /src/framework/api/orders/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeUpdateOrder } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateOrder = ({ order_id }: { order_id: string }) => 9 | useMutation({ 10 | mutationKey: ["update-order"], 11 | mutationFn: (props: TypeUpdateOrder) => 12 | Api.put(`/orders/${order_id}`, props) 13 | }); 14 | 15 | export default useUpdateOrder; 16 | -------------------------------------------------------------------------------- /src/framework/api/utils/api-config.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | const { VITE_API_URL, VITE_API_VERSION, VITE_SHOP_NAME } = import.meta.env 4 | 5 | const Api = axios.create({ 6 | baseURL: `${VITE_API_URL}/api/${VITE_API_VERSION}/${VITE_SHOP_NAME}`, 7 | headers: { 8 | Accept: "*/*" 9 | } 10 | }) 11 | 12 | Api.interceptors.request.use( 13 | (config) => config, 14 | (err) => Promise.reject(err) 15 | ) 16 | 17 | Api.interceptors.response.use( 18 | (response) => response, 19 | (err) => Promise.reject(err) 20 | ) 21 | 22 | export default Api 23 | -------------------------------------------------------------------------------- /src/framework/api/photos-upload/add-slider.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPhotos } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddSliderImage = () => 9 | useMutation({ 10 | mutationKey: ["add-main_slider-image"], 11 | mutationFn: ({ photo_base64 }: TypeProductPhotos) => 12 | Api.post("/main_slider/photo", { photo_base64 }) 13 | }); 14 | 15 | export default useAddSliderImage; 16 | -------------------------------------------------------------------------------- /src/framework/api/product/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeProductPost } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateProduct = ({ product_id }: { product_id: string | number }) => 9 | useMutation({ 10 | mutationKey: ["update-Product"], 11 | mutationFn: (props: TypeProductPost) => 12 | Api.put(`/products/${product_id}`, props) 13 | }); 14 | 15 | export default useUpdateProduct; 16 | -------------------------------------------------------------------------------- /src/framework/api/cart/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeCarts } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const fetch = async ({ queryKey }: any) => { 9 | const [_key, user_id] = queryKey; 10 | const { data } = await Api.get(`/carts/${user_id}`); 11 | return data.cart as TypeCarts | null; 12 | }; 13 | 14 | export const useGetCarts = (user_id: string) => 15 | useQuery(["carts", user_id], fetch); 16 | -------------------------------------------------------------------------------- /src/framework/api/categories/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeDeleteCategories } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useDeleteCategories = () => 9 | useMutation({ 10 | mutationKey: ["delete-category"], 11 | mutationFn: ({ user_id, category_id }: TypeDeleteCategories) => 12 | Api.delete(`/categories/${category_id}?user_id=${user_id}`) 13 | }); 14 | 15 | export default useDeleteCategories; 16 | -------------------------------------------------------------------------------- /src/assets/styles/app.scss: -------------------------------------------------------------------------------- 1 | .app { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | 6 | // min-height: 100vh; 7 | 8 | color: white; 9 | text-align: center; 10 | } 11 | .app-logo { 12 | pointer-events: none; 13 | scale: 1.3; 14 | animation: app-logo-spin infinite 20s linear; 15 | 16 | @keyframes app-logo-spin { 17 | from { 18 | transform: rotate(0deg); 19 | } 20 | 21 | to { 22 | transform: rotate(360deg); 23 | } 24 | } 25 | } 26 | 27 | .links { 28 | a { 29 | border-bottom: #fff solid 1px; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/framework/api/discount/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { useMutation } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const useDeleteDiscount = () => 8 | useMutation({ 9 | mutationKey: ["delete-discount"], 10 | mutationFn: ({ 11 | discount_id, 12 | user_id 13 | }: { 14 | discount_id: string; 15 | user_id: string; 16 | }) => Api.delete(`/discounts/${discount_id}?user_id=${user_id}`) 17 | }); 18 | 19 | export default useDeleteDiscount; 20 | -------------------------------------------------------------------------------- /src/framework/api/discount/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeUpdateDiscount } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useUpdateDiscount = ({ discount_id }: { discount_id: string | number }) => 9 | useMutation({ 10 | mutationKey: ["update-discount"], 11 | mutationFn: (props: TypeUpdateDiscount) => 12 | Api.put(`/discounts/${discount_id}`, props) 13 | }); 14 | 15 | export default useUpdateDiscount; 16 | -------------------------------------------------------------------------------- /src/framework/api/cart/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | /* eslint-disable camelcase */ 4 | import { TypeDeleteCartItem } from "@framework/types"; 5 | import { useMutation } from "@tanstack/react-query"; 6 | 7 | import Api from "../utils/api-config"; 8 | 9 | const useDeleteCartItem = () => 10 | useMutation({ 11 | mutationKey: ["delete-cart-item"], 12 | mutationFn: ({ user_id, product_id }: TypeDeleteCartItem) => 13 | Api.delete(`/carts/${user_id}/${product_id}`) 14 | }); 15 | 16 | export default useDeleteCartItem; 17 | -------------------------------------------------------------------------------- /src/framework/api/address/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | /* eslint-disable camelcase */ 4 | import { TypeDeleteAddressItem } from "@framework/types"; 5 | import { useMutation } from "@tanstack/react-query"; 6 | 7 | import Api from "../utils/api-config"; 8 | 9 | const useDeleteAddress = () => 10 | useMutation({ 11 | mutationKey: ["delete-address-item"], 12 | mutationFn: ({ user_id, address_id }: TypeDeleteAddressItem) => 13 | Api.delete(`/users/${user_id}/addresses/${address_id}`) 14 | }); 15 | 16 | export default useDeleteAddress; 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | max_line_length = 80 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | # Matches multiple files with brace expansion notation 18 | # Set default charset 19 | [*.{js,jsx,ts,tsx,html}] 20 | charset = utf-8 21 | 22 | # Matches the exact files either package.json or .travis.yml 23 | [{package.json,.travis.yml}] 24 | indent_style = space 25 | indent_size = 2 26 | -------------------------------------------------------------------------------- /src/containers/404.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Divider } from "antd"; 2 | import { useNavigate } from "react-router"; 3 | 4 | function NotFoundPage() { 5 | const navigate = useNavigate(); 6 | return ( 7 |
8 |
404
9 | 10 |
اوه به نظر به جای اشتباهی اومدی میتونی راحت بگردی به صفحه اصلی
11 | 12 |
13 | ); 14 | } 15 | 16 | export default NotFoundPage; 17 | -------------------------------------------------------------------------------- /src/framework/api/orders/get-by-user.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeSingleOrder } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const fetch = async ({ queryKey }: any) => { 9 | const [_key, user_id] = queryKey; 10 | 11 | const { data } = await Api.get(`/orders/user/${user_id}`); 12 | return data as TypeSingleOrder; 13 | }; 14 | 15 | export const useGetOrderByUser = ({ user_id }: { user_id: string }) => 16 | useQuery([`order-user-${user_id}`, user_id], fetch); 17 | -------------------------------------------------------------------------------- /src/framework/api/product/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeDeleteProduct } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useDeleteProduct = () => 9 | useMutation({ 10 | mutationKey: ["delete-product"], 11 | mutationFn: ({ user_id, product_id }: TypeDeleteProduct) => 12 | Api.delete("/products", { 13 | data: { 14 | user_id: `${user_id}`, 15 | product_id 16 | } 17 | }) 18 | }); 19 | 20 | export default useDeleteProduct; 21 | -------------------------------------------------------------------------------- /src/helpers/get-order-status.ts: -------------------------------------------------------------------------------- 1 | export const GetOrderStatus = (e: string) => { 2 | switch (e) { 3 | case "Pending": 4 | return " در انتظار تایید "; 5 | case "Processing": 6 | return "درحال انجام "; 7 | case "Packing": 8 | return " درحال بسته بندی "; 9 | case "CancelledByCustomer": 10 | return "لغو توسط مشتری "; 11 | case "CancelledDueToUnavailability": 12 | return "اتمام موجودی 1 یا چند کالا"; 13 | case "CancelledByAdmin": 14 | return "لغو توسط ادمین"; 15 | case "Shipped": 16 | return "تحویل داده شده "; 17 | default: 18 | return " در انتظار تایید "; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/framework/api/categories/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypePostCategories } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddCategories = () => 9 | useMutation({ 10 | mutationKey: ["add-categories"], 11 | mutationFn: ({ user_id, category_name, parent_id }: TypePostCategories) => 12 | Api.post("/categories", { 13 | user_id, 14 | category_name, 15 | parent_id: parent_id || null 16 | }) 17 | }); 18 | 19 | export default useAddCategories; 20 | -------------------------------------------------------------------------------- /src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import Boxes from "@containers/boxes"; 2 | import HeroSlider from "@containers/hero-slider"; 3 | import ProductNews from "@containers/product-news"; 4 | import useTelegram from "@hooks/useTelegram"; 5 | 6 | import AppHeader from "../layouts/header"; 7 | 8 | function Home() { 9 | const tgApp = useTelegram(); 10 | 11 | // const userId = tgApp.initDataUnsafe.user.id; 12 | // const navigate = useNavigate(); 13 | return ( 14 |
15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | 23 | export default Home; 24 | -------------------------------------------------------------------------------- /src/framework/api/orders/getById.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeSingleOrder } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const fetch = async ({ queryKey }: any) => { 9 | const [_key, order_Id] = queryKey; 10 | console.log("getttttt"); 11 | const { data } = await Api.get(`/orders/${order_Id}`); 12 | return data as TypeSingleOrder; 13 | }; 14 | 15 | export const useGetOrderById = ({ order_Id }: { order_Id: string }) => 16 | useQuery([`order-${order_Id}`, order_Id], fetch); 17 | -------------------------------------------------------------------------------- /src/framework/api/master/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { useMutation } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const useDeleteMaster = () => 8 | useMutation({ 9 | mutationKey: ["delete-master"], 10 | mutationFn: ({ 11 | master_id, 12 | user_id 13 | }: { 14 | master_id: string; 15 | user_id: string; 16 | }) => 17 | Api.delete("/master", { 18 | data: { 19 | user_Id: `${user_id}`, 20 | id: master_id 21 | } 22 | }) 23 | }); 24 | 25 | export default useDeleteMaster; 26 | -------------------------------------------------------------------------------- /src/pages/user/categories/list.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import ProductLists from "@components/product/list"; 3 | import ProductsSkeleton from "@components/skeleton/products"; 4 | import { useGetProducts } from "@framework/api/product/get"; 5 | import { Suspense } from "react"; 6 | 7 | function UserCategoriesList() { 8 | const { data } = useGetProducts({}); 9 | return ( 10 | 11 | }> 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default UserCategoriesList; 19 | -------------------------------------------------------------------------------- /src/framework/api/slider/delete.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { useMutation } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const useDeleteSlider = () => 8 | useMutation({ 9 | mutationKey: ["delete-slider"], 10 | mutationFn: ({ 11 | master_id, 12 | user_id 13 | }: { 14 | master_id: string; 15 | user_id: string; 16 | }) => 17 | Api.delete("/main_slider", { 18 | data: { 19 | user_Id: `${user_id}`, 20 | id: master_id 21 | } 22 | }) 23 | }); 24 | 25 | export default useDeleteSlider; 26 | -------------------------------------------------------------------------------- /src/framework/api/categories/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { useMutation } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | const useUpdateCategory = ({ category_id }: { category_id: string | number }) => 8 | useMutation({ 9 | mutationKey: [`edit-categories-${category_id}`], 10 | mutationFn: ({ user_id, category_name, parent_id }: TypePostCategories) => 11 | Api.put(`/categories/${category_id}`, { 12 | user_id, 13 | category_name, 14 | parent_id: parent_id || null 15 | }) 16 | }); 17 | 18 | export default useUpdateCategory; 19 | -------------------------------------------------------------------------------- /src/framework/api/product/get-by-id.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { Product } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | interface Props { 9 | product_id: number | string; 10 | } 11 | const fetch = async ({ queryKey }: any) => { 12 | const [_key, product_id] = queryKey; 13 | const { data } = await Api.get(`/products/${product_id}`); 14 | return data as Product; 15 | }; 16 | 17 | export const useGetProductsById = ({ product_id }: Props) => 18 | useQuery([`product-by-id-${product_id}`, product_id], fetch); 19 | -------------------------------------------------------------------------------- /src/pages/user/product/list.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import ProductLists from "@components/product/list"; 3 | import ProductsSkeleton from "@components/skeleton/products"; 4 | import { useGetProducts } from "@framework/api/product/get"; 5 | import { Suspense } from "react"; 6 | 7 | function ProductList() { 8 | const { data } = useGetProducts({}); 9 | console.log(data); 10 | return ( 11 | 12 | }> 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default ProductList; 20 | -------------------------------------------------------------------------------- /src/framework/api/master/get-by-id.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypePostMaster } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | interface Props { 9 | master_id: number | string; 10 | } 11 | const fetch = async ({ queryKey }: any) => { 12 | const [_key, master_id] = queryKey; 13 | const { data } = await Api.get(`/master/${master_id}`); 14 | return data.master as TypePostMaster; 15 | }; 16 | 17 | export const useGetMasterById = ({ master_id }: Props) => 18 | useQuery([`master-by-id-${master_id}`, master_id], fetch); 19 | -------------------------------------------------------------------------------- /src/framework/api/user-information/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import { TypeUserInfo } from "@framework/types"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | 5 | import Api from "../utils/api-config"; 6 | 7 | interface QueryProps { 8 | user_Id: string | number; 9 | } 10 | 11 | export const fetch = async ({ queryKey }: any) => { 12 | const [_key, user_Id] = queryKey; 13 | const { data } = await Api.get(`/users/${user_Id}/GetUser`); 14 | return data as TypeUserInfo; 15 | }; 16 | 17 | // eslint-disable-next-line prettier/prettier 18 | const useGetUserInfo = ({ user_Id }: QueryProps) => useQuery(["user-info", user_Id], fetch); 19 | 20 | export default useGetUserInfo; 21 | -------------------------------------------------------------------------------- /src/containers/hero-slider.tsx: -------------------------------------------------------------------------------- 1 | import { useGetSliders } from "@framework/api/slider/get"; 2 | import { Carousel } from "antd"; 3 | 4 | function HeroSlider() { 5 | const { data } = useGetSliders(); 6 | return data?.length === 0 ? ( 7 | 8 | ) : ( 9 | 10 | {data?.map((item, idx) => ( 11 |
12 | 13 | slider 17 | 18 |
19 | ))} 20 |
21 | ); 22 | } 23 | 24 | export default HeroSlider; 25 | -------------------------------------------------------------------------------- /src/hooks/useTelegramUser.ts: -------------------------------------------------------------------------------- 1 | import qs from "query-string"; 2 | 3 | interface TGUser { 4 | id: number; 5 | first_name: string; 6 | last_name: string; 7 | username: string; 8 | language_code: string; 9 | } 10 | export interface TypeTGWebAppData { 11 | auth_date: string; 12 | hash: string; 13 | query_id: string; 14 | user: TGUser; 15 | } 16 | const useTelegramUser = () => { 17 | const { tgWebAppData } = JSON.parse( 18 | sessionStorage.getItem("__telegram__initParams") || "" 19 | ); 20 | const TGparse = qs.parse(tgWebAppData); 21 | try { 22 | const user: TGUser = JSON.parse(TGparse.user); 23 | return user; 24 | } catch { 25 | return null; 26 | } 27 | }; 28 | export default useTelegramUser; 29 | -------------------------------------------------------------------------------- /src/framework/api/orders/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable camelcase */ 3 | import { TypeOrderPost } from "@framework/types"; 4 | import { useMutation } from "@tanstack/react-query"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | const useAddOrder = () => 9 | useMutation({ 10 | mutationKey: ["add-Order"], 11 | mutationFn: ({ 12 | user_Id, 13 | order_Description, 14 | receipt_Photo_Path, 15 | shipping_Cost, 16 | user_Address_Id 17 | }: TypeOrderPost) => 18 | Api.post("/orders", { 19 | user_Id, 20 | order_Description, 21 | receipt_Photo_Path, 22 | shipping_Cost, 23 | user_Address_Id 24 | }) 25 | }); 26 | 27 | export default useAddOrder; 28 | -------------------------------------------------------------------------------- /src/framework/api/address/add.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | /* eslint-disable camelcase */ 4 | import { TypeAddAddress } from "@framework/types"; 5 | import { useMutation } from "@tanstack/react-query"; 6 | 7 | import Api from "../utils/api-config"; 8 | 9 | const useAddAddress = () => 10 | useMutation({ 11 | mutationKey: ["add-address"], 12 | mutationFn: ({ 13 | city, 14 | country, 15 | state, 16 | street, 17 | zipcode, 18 | user_Id 19 | }: TypeAddAddress) => 20 | Api.post(`/users/${user_Id}/addresses`, { 21 | city, 22 | country, 23 | state, 24 | street, 25 | zipcode 26 | }) 27 | }); 28 | 29 | export default useAddAddress; 30 | -------------------------------------------------------------------------------- /src/framework/api/categories/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | import { TypeCategories } from "@framework/types"; 4 | import { useQuery } from "@tanstack/react-query"; 5 | import qs from "query-string"; 6 | 7 | import Api from "../utils/api-config"; 8 | 9 | const fetch = async ({ queryKey }: any) => { 10 | const [_key, category_id] = queryKey; 11 | const { data } = await Api.get( 12 | `/categories?${qs.stringify({ category_id })}` 13 | ); 14 | return data.categories as TypeCategories[] | null; 15 | }; 16 | 17 | export interface Props { 18 | category_id?: number | string; 19 | } 20 | 21 | export const useGetCategories = ({ category_id }: Props) => 22 | useQuery(["user-info", category_id], fetch); 23 | -------------------------------------------------------------------------------- /src/framework/api/address/update.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable implicit-arrow-linebreak */ 3 | /* eslint-disable camelcase */ 4 | import { TypeAddAddress } from "@framework/types"; 5 | import { useMutation } from "@tanstack/react-query"; 6 | 7 | import Api from "../utils/api-config"; 8 | 9 | const useUpdateAddress = () => 10 | useMutation({ 11 | mutationKey: ["update-address"], 12 | mutationFn: ({ 13 | address_Id, 14 | user_Id, 15 | city, 16 | country, 17 | state, 18 | street, 19 | zipcode 20 | }: TypeAddAddress) => 21 | Api.put(`/users/${user_Id}/addresses/${address_Id}`, { 22 | city, 23 | country, 24 | state, 25 | street, 26 | zipcode 27 | }) 28 | }); 29 | 30 | export default useUpdateAddress; 31 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 18 | PTI7 19 | 20 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/layouts/header.tsx: -------------------------------------------------------------------------------- 1 | import { ShoppingCartOutlined, UserOutlined } from "@ant-design/icons"; 2 | import { Link } from "react-router-dom"; 3 | 4 | function AppHeader() { 5 | return ( 6 |
7 | 10 | 11 | سبد خرید من 12 | 13 | 16 | حساب کاربری 17 | 18 | 19 |
20 | ); 21 | } 22 | 23 | export default AppHeader; 24 | -------------------------------------------------------------------------------- /src/framework/api/address/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | /* eslint-disable semi */ 3 | /* eslint-disable implicit-arrow-linebreak */ 4 | /* eslint-disable camelcase */ 5 | import { TypeAddresses } from "@framework/types"; 6 | import { useQuery } from "@tanstack/react-query"; 7 | 8 | import Api from "../utils/api-config"; 9 | 10 | const fetch = async ({ queryKey }: any) => { 11 | const [_key, user_id, address_id] = queryKey; 12 | const { data } = await Api.get( 13 | `/users/${user_id}/addresses${address_id ? `?address_id=${address_id}` : "" 14 | }` 15 | ); 16 | return data as TypeAddresses | null; 17 | }; 18 | 19 | export const useGetAddresses = ( 20 | user_id: string, 21 | address_id?: string | number | undefined | null 22 | ) => 23 | useQuery( 24 | ["user-addresses", user_id, address_id], 25 | fetch 26 | ); 27 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable indent */ 2 | import "@style/app.scss"; 3 | import "antd/dist/reset.css"; 4 | 5 | import useTelegramUser from "@hooks/useTelegramUser"; 6 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 7 | 8 | import Main from "./layouts/main"; 9 | import Router from "./router"; 10 | 11 | function App() { 12 | const isTG = useTelegramUser(); 13 | const queryClient = new QueryClient({ 14 | defaultOptions: { 15 | queries: { 16 | refetchOnWindowFocus: true, 17 | refetchOnMount: true, 18 | refetchOnReconnect: true, 19 | retry: false, 20 | staleTime: 5 * 60 * 1000 21 | } 22 | } 23 | }); 24 | 25 | if (!isTG) return <>open in telegram app ; 26 | return ( 27 | 28 |
29 | 30 |
31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.suggest.paths": true, 3 | "javascript.suggest.paths": true, 4 | "editor.defaultFormatter": "esbenp.prettier-vscode", 5 | "editor.formatOnSave": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true, 8 | "source.fixAll.stylelint": true 9 | }, 10 | "css.validate": false, 11 | "scss.validate": false, 12 | "stylelint.validate": ["css", "postcss", "scss"], 13 | "editor.snippetSuggestions": "inline", 14 | "emmet.includeLanguages": { 15 | "javascript": "javascriptreact" 16 | }, 17 | "search.sortOrder": "type", 18 | "explorer.sortOrder": "type", 19 | "cSpell.words": ["Formik", "Signup", "zipcode", "ذخیره"], 20 | "[properties]": { 21 | "editor.defaultFormatter": "foxundermoon.shell-format" 22 | }, 23 | "[nginx]": { 24 | "editor.defaultFormatter": "raynigon.nginx-formatter" 25 | }, 26 | "[dockercompose]": { 27 | "editor.defaultFormatter": "ms-azuretools.vscode-docker" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/admin/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "antd"; 2 | import { useNavigate } from "react-router-dom"; 3 | 4 | function Admin() { 5 | // const tgApp = useTelegram(); 6 | 7 | // const userId = tgApp.initDataUnsafe.user.id; 8 | const navigate = useNavigate(); 9 | return ( 10 |
11 | منو ادمین 12 | {/* 13 | 14 | 15 | 16 | */} 17 | 18 | 21 | 22 | {/* */} 23 | 24 |
25 | ); 26 | } 27 | 28 | export default Admin; 29 | -------------------------------------------------------------------------------- /src/pages/bot/components/button-menu.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-one-expression-per-line */ 2 | /* eslint-disable prettier/prettier */ 3 | /* eslint-disable camelcase */ 4 | /* eslint-disable react/jsx-wrap-multilines */ 5 | import { UnorderedListOutlined, UserOutlined } from "@ant-design/icons"; 6 | import { Link } from "react-router-dom"; 7 | 8 | function UserProfileButtonMenu() { 9 | return ( 10 |
13 | 16 | تنظیمات ربات 17 | 18 | 21 | اساتید 22 | 23 |
24 | ); 25 | } 26 | 27 | export default UserProfileButtonMenu; 28 | -------------------------------------------------------------------------------- /src/framework/api/product/get.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | import { TypeListProducts } from "@framework/types"; 3 | import { useQuery } from "@tanstack/react-query"; 4 | import qs from "query-string"; 5 | 6 | import Api from "../utils/api-config"; 7 | 8 | interface Props { 9 | name?: string; 10 | sortBy?: "Product_Name" | "Updated_At" | "Price"; 11 | order?: "asc" | "desc"; 12 | limit?: number; 13 | page?: number; 14 | categoryId?: number; 15 | } 16 | const fetch = async ({ queryKey }: any) => { 17 | const [_key, categoryId, limit, name, order, page, sortBy] = queryKey; 18 | const { data } = await Api.get( 19 | `/products?${qs.stringify({ 20 | categoryId, 21 | limit, 22 | name, 23 | order, 24 | page, 25 | sortBy 26 | })}` 27 | ); 28 | return data as TypeListProducts; 29 | }; 30 | 31 | export const useGetProducts = ({ 32 | categoryId, 33 | limit = 10, 34 | name, 35 | order, 36 | page = 1, 37 | sortBy = "Price" 38 | }: Props) => 39 | useQuery( 40 | ["products", categoryId, limit, name, order, page, sortBy], 41 | fetch 42 | ); 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | // define your module paths here: 19 | "paths": { 20 | "@components/*": ["./src/components/*"], 21 | "@image/*": ["./src/assets/images/*"], 22 | "@style/*": ["./src/assets/styles/*"], 23 | "@pages/*": ["./src/pages/*"], 24 | "@router/*": ["./src/router/"], 25 | "@hooks/*": ["./src/hooks/*"], 26 | "@helpers/*": ["./src/helpers/*"], 27 | "@containers/*": ["./src/containers/*"], 28 | "@framework/*": ["./src/framework/*"], 29 | "@layout/*": ["./src/layout/*"] 30 | }, 31 | "baseUrl": "." 32 | }, 33 | "include": ["src"], 34 | "references": [{ "path": "./tsconfig.node.json" }] 35 | } 36 | -------------------------------------------------------------------------------- /src/pages/admin/product/list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-nested-ternary */ 2 | /* eslint-disable object-curly-newline */ 3 | import Container from "@components/container"; 4 | import ProductLists from "@components/product/list"; 5 | import { useNavigate } from "react-router"; 6 | 7 | function ProductList() { 8 | const navigate = useNavigate(); 9 | // const { data, error, refetch, isLoading, isFetching } = useGetProducts({}); 10 | // useEffect(() => { 11 | // refetch(); 12 | // }, []); 13 | 14 | return ( 15 | navigate("/admin/products/add")}> 21 | {/* }> */} 22 | {/* {isLoading || isFetching ? ( 23 | 24 | ) : error ? ( 25 | <>مشکلی رخ داده 26 | ) : data?.products.length === 0 ? ( 27 | 28 | ) : ( 29 | 30 | )} */} 31 | 32 | 33 | {/* */} 34 | 35 | ); 36 | } 37 | 38 | export default ProductList; 39 | -------------------------------------------------------------------------------- /src/containers/product-news.tsx: -------------------------------------------------------------------------------- 1 | import Card from "@components/product/card"; 2 | import ProductCardSkeleton from "@components/skeleton/product-card"; 3 | import { useGetProducts } from "@framework/api/product/get"; 4 | import { Divider } from "antd"; 5 | 6 | function ProductNews() { 7 | const { data, isLoading, isFetching } = useGetProducts({ 8 | limit: 6, 9 | sortBy: "Updated_At" 10 | }); 11 | return ( 12 |
13 | محصولات جدید ما 14 | 15 |
16 | {isLoading || isFetching ? ( 17 | <> 18 | {[...Array(8)].map((_, idx) => ( 19 | 20 | ))} 21 | 22 | ) : ( 23 | data?.products.map((item) => ( 24 | 33 | )) 34 | )} 35 |
36 |
37 | ); 38 | } 39 | 40 | export default ProductNews; 41 | -------------------------------------------------------------------------------- /src/containers/order/single.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import { useGetOrderById } from "@framework/api/orders/getById"; 3 | // eslint-disable-next-line object-curly-newline 4 | import { Tabs } from "antd"; 5 | import { useParams } from "react-router"; 6 | 7 | import CustomerDetail from "./components/customer-detail"; 8 | import OrderList from "./components/order-list"; 9 | import OrderSetting from "./components/order-setting"; 10 | 11 | interface Props { 12 | type: "admin" | "user"; 13 | } 14 | 15 | function OrdersSingle({ type }: Props) { 16 | const { order_id } = useParams(); 17 | const { data, isLoading } = useGetOrderById({ order_Id: order_id }); 18 | const order = data?.order; 19 | const items = [ 20 | { 21 | label: "لیست سفارشات", 22 | key: "1", 23 | children: 24 | }, 25 | { 26 | label: "اطلاعات تکمیلی ", 27 | key: "3", 28 | children: 29 | }, 30 | { 31 | label: "تنظبمات سفارش", 32 | key: "2", 33 | children: 34 | } 35 | ]; 36 | return ( 37 | 38 | 39 | 40 | ); 41 | } 42 | 43 | export default OrdersSingle; 44 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/user/profile/components/button-menu.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-one-expression-per-line */ 2 | /* eslint-disable prettier/prettier */ 3 | /* eslint-disable camelcase */ 4 | /* eslint-disable react/jsx-wrap-multilines */ 5 | import "./style.scss"; 6 | 7 | import { 8 | IdcardOutlined, 9 | UnorderedListOutlined, 10 | UserOutlined 11 | } from "@ant-design/icons"; 12 | import { NavLink } from "react-router-dom"; 13 | 14 | function UserProfileButtonMenu() { 15 | return ( 16 |
17 | 20 | حساب کاربری 21 | 22 | 25 | سفارشات 26 | 27 | {/*
28 | 29 |
*/} 30 | 33 | آدرس ها 34 | 35 |
36 | ); 37 | } 38 | 39 | export default UserProfileButtonMenu; 40 | -------------------------------------------------------------------------------- /src/components/skeleton/user-single-product.tsx: -------------------------------------------------------------------------------- 1 | import { Divider } from "antd"; 2 | 3 | function UserSingleProductSkeleton() { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 | ); 23 | } 24 | 25 | export default UserSingleProductSkeleton; 26 | -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: telegram-front 3 | 4 | trigger: 5 | branch: 6 | - main 7 | event: 8 | - push 9 | steps: 10 | - name: install 11 | image: node:14 12 | commands: 13 | - npm install 14 | - name: create-env-file 15 | image: alpine:3 16 | environment: 17 | VITE_API_URL: 18 | from_secret: VITE_API_URL 19 | VITE_API_VERSION: 20 | from_secret: VITE_API_VERSION 21 | VITE_SHOP_NAME: 22 | from_secret: VITE_SHOP_NAME 23 | VITE_DEV_MODE: 24 | from_secret: VITE_DEV_MODE 25 | commands: 26 | - echo "VITE_API_URL=$VITE_API_URL" > .env 27 | - echo "VITE_API_VERSION=$VITE_API_VERSION" >> .env 28 | - echo "VITE_SHOP_NAME=$VITE_SHOP_NAME" >> .env 29 | - echo "VITE_DEV_MODE=$VITE_DEV_MODE" >> .env 30 | - name: build 31 | image: node:14 32 | commands: 33 | - npm run build 34 | 35 | - name: deploy 36 | image: ubuntu:20.04 37 | environment: 38 | SSH_KEY: 39 | from_secret: SSH_KEY 40 | SSH_USER: 41 | from_secret: SSH_USER 42 | SSH_HOST: 43 | from_secret: SSH_HOST 44 | SSH_PATH: 45 | from_secret: SSH_PATH 46 | SSH_PASS: 47 | from_secret: SSH_PASS 48 | commands: 49 | - apt-get update && apt-get install -y openssh-client sshpass 50 | - mkdir -p ~/.ssh 51 | - echo "$SSH_KEY" > ~/.ssh/id_rsa 52 | - chmod 600 ~/.ssh/id_rsa 53 | - ssh-keyscan $SSH_HOST >> ~/.ssh/known_hosts 54 | - ls 55 | - sshpass -p $SSH_PASS scp -r dist/* $SSH_USER@$SSH_HOST:$SSH_PATH 56 | -------------------------------------------------------------------------------- /src/components/skeleton/products.tsx: -------------------------------------------------------------------------------- 1 | function ProductsSkeletonItem({ itemIndex }: { itemIndex: number }) { 2 | return ( 3 |
6 |
7 |
8 |

9 |

10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ); 20 | } 21 | function ProductsSkeleton() { 22 | return ( 23 |
24 | {[...Array(10)].map((_, idx) => ( 25 | 26 | ))} 27 |
28 | ); 29 | } 30 | 31 | export default ProductsSkeleton; 32 | -------------------------------------------------------------------------------- /src/layouts/main.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable indent */ 3 | import { useThemeParams } from "@vkruglikov/react-telegram-web-app"; 4 | import { ConfigProvider, theme } from "antd"; 5 | import fa_IR from "antd/lib/locale/fa_IR"; 6 | import React from "react"; 7 | 8 | interface Props { 9 | children: React.ReactNode; 10 | } 11 | 12 | function Main({ children }: Props) { 13 | const [colorScheme, themeParams] = useThemeParams(); 14 | // const { id } = useTelegramUser(); 15 | // const { data } = useGetUserInfo({ user_Id: id }); 16 | const customizeRenderEmpty = () => ( 17 |
18 |

اطلاعاتی موجود نیست

19 |
20 | ); 21 | return ( 22 |
23 |
24 | 43 | {/*
44 | logo 45 |
*/} 46 |
{children}
47 |
48 |
49 |
50 | ); 51 | } 52 | 53 | export default Main; 54 | -------------------------------------------------------------------------------- /src/components/container/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "antd"; 2 | import React from "react"; 3 | import { useNavigate } from "react-router"; 4 | 5 | interface Props { 6 | title: string; 7 | children: React.ReactNode; 8 | backwardUrl?: string | undefined | number; 9 | customButton?: boolean; 10 | customButtonTitle?: string; 11 | customButtonOnClick?: React.MouseEventHandler; 12 | titleType?: "small" | "default"; 13 | } 14 | 15 | function Container({ 16 | title, 17 | children, 18 | backwardUrl, 19 | customButton, 20 | customButtonTitle, 21 | customButtonOnClick, 22 | titleType 23 | }: Props) { 24 | const navigate = useNavigate(); 25 | 26 | // eslint-disable-next-line operator-linebreak 27 | const headerStyle = 28 | titleType === "default" 29 | ? "sticky top-1 z-30 flex items-center justify-between gap-1 rounded-lg bg-[var(--tg-theme-secondary-bg-color)] p-3" 30 | : " z-20 text-sm text-left -mb-2 border-b-[1px] pb-1 "; 31 | 32 | return ( 33 |
34 |
35 |

{title}

36 |
37 | {customButton && ( 38 | 41 | )} 42 | {backwardUrl && ( 43 | 46 | )} 47 |
48 |
{children}
49 |
50 | ); 51 | } 52 | Container.defaultProps = { 53 | backwardUrl: undefined, 54 | customButton: false, 55 | customButtonTitle: "", 56 | // eslint-disable-next-line @typescript-eslint/no-empty-function 57 | customButtonOnClick: () => {}, 58 | titleType: "default" 59 | }; 60 | export default Container; 61 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.BaseConfig} */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | es2021: true 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:react/recommended", 11 | "plugin:prettier/recommended", 12 | "plugin:@typescript-eslint/recommended", 13 | "airbnb", 14 | "airbnb/hooks" 15 | ], 16 | plugins: ["react", "@typescript-eslint", "simple-import-sort"], 17 | overrides: [], 18 | parser: "@typescript-eslint/parser", 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | sourceType: "module" 22 | }, 23 | rules: { 24 | "no-unused-vars": "off", 25 | "@typescript-eslint/no-unused-vars": "warn", 26 | "import/no-extraneous-dependencies": "off", 27 | "import/prefer-default-export": "off", 28 | "react/jsx-props-no-spreading": "off", 29 | "linebreak-style": ["error", "unix"], 30 | quotes: ["warn", "double"], 31 | "no-console": "off", 32 | "react/jsx-closing-bracket-location": "off", 33 | "react/jsx-uses-react": "off", 34 | "react/react-in-jsx-scope": "off", 35 | "simple-import-sort/imports": "error", 36 | "simple-import-sort/exports": "error", 37 | "comma-dangle": "off", 38 | "import/no-unresolved": "off", 39 | "prettier/prettier": [ 40 | "error", 41 | {}, 42 | { 43 | usePrettierrc: true 44 | } 45 | ], 46 | "react/jsx-filename-extension": [ 47 | "error", 48 | { 49 | extensions: [".ts", ".tsx"] 50 | } 51 | ], 52 | "import/extensions": [ 53 | "error", 54 | "ignorePackages", 55 | { 56 | js: "never", 57 | jsx: "never", 58 | ts: "never", 59 | tsx: "never" 60 | } 61 | ] 62 | }, 63 | settings: { 64 | "import/resolver": { 65 | node: { 66 | extensions: [".js", ".jsx", ".ts", ".tsx"] 67 | } 68 | } 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | // Admin pages 3 | 4 | export { default as CategoriesAdd } from "./admin/categories/add"; 5 | export { default as CategoriesEdit } from "./admin/categories/edit"; 6 | export { default as Categories } from "./admin/categories/list"; 7 | export { default as AdminHome } from "./admin/index"; 8 | export { default as AdminOrders } from "./admin/orders/list"; 9 | export { default as AdminOrdersSingle } from "./admin/orders/single"; 10 | export { default as ProductAdd } from "./admin/product/add"; 11 | export { default as ProductEdit } from "./admin/product/edit"; 12 | export { default as AdminProductList } from "./admin/product/list"; 13 | export { default as AdminAddSlider } from "./admin/slider/add"; 14 | export { default as AdminSlider } from "./admin/slider/list"; 15 | // Bot pages 16 | export { default as BotPanel } from "./bot/index"; 17 | export { default as BotAddMasters } from "./bot/masters/add"; 18 | export { default as BotEditMasters } from "./bot/masters/edit"; 19 | export { default as BotMasters } from "./bot/masters/list"; 20 | export { default as BotSetting } from "./bot/setting"; 21 | export { default as HomePage } from "./home"; 22 | // User pages 23 | export { default as UserCart } from "./user/cart/index"; 24 | export { default as Checkout } from "./user/checkout/index"; 25 | export { default as ProductList } from "./user/product/list"; 26 | export { default as ProductSingle } from "./user/product/single"; 27 | export { default as UserProfileAddAddresses } from "./user/profile/addresses/add"; 28 | export { default as UserProfileEditAddresses } from "./user/profile/addresses/edit"; 29 | export { default as UserProfileAddresses } from "./user/profile/addresses/list"; 30 | export { default as UserProfileEdit } from "./user/profile/edit"; 31 | export { default as UserProfileHome } from "./user/profile/home"; 32 | export { default as UserProfile } from "./user/profile/index"; 33 | export { default as UserProfileOrder } from "./user/profile/order/list"; 34 | export { default as UserProfileOrderSingle } from "./user/profile/order/single"; 35 | -------------------------------------------------------------------------------- /src/assets/styles/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | :root { 5 | --tg-theme-bg-color: #fff; 6 | --tg-theme-text-color: #0a0a0a; 7 | --tg-theme-hint-color: #929292; 8 | --tg-theme-link-color: #207ae4; 9 | --tg-theme-button-color: #5bc8fb; 10 | --tg-theme-button-text-color: #fffeec; 11 | --tg-theme-secondary-bg-color: #f3f2f9; 12 | 13 | --default-font: -apple-system, "Vazirmatn"; 14 | } 15 | 16 | @font-face { 17 | font-family: "iransans"; 18 | src: url("/fonts/IRANSansWeb(FaNum).ttf"), 19 | url("/fonts/IRANSansWeb(FaNum).woff") format("woff"), 20 | url("/fonts/IRANSansWeb(FaNum).woff2") format("woff2"), 21 | url("/fonts/IRANSansWeb(FaNum).eot") format("eot"); 22 | } 23 | 24 | /* button, 25 | [type="button"], 26 | [type="reset"], 27 | [type="submit"] { 28 | background-color: var(--tg-theme-button-color); 29 | } */ 30 | 31 | body { 32 | margin: 0; 33 | padding: 20px 20px; 34 | 35 | font-family: "iransans"; 36 | 37 | background: var(--tg-theme-secondary-bg-color); 38 | 39 | -webkit-font-smoothing: antialiased; 40 | -moz-osx-font-smoothing: grayscale; 41 | } 42 | 43 | code, 44 | label, 45 | span, 46 | div, 47 | label { 48 | font-family: "iransans"; 49 | } 50 | 51 | .App-logo { 52 | pointer-events: none; 53 | height: 40vmin; 54 | } 55 | 56 | @media (prefers-reduced-motion: no-preference) { 57 | .App-logo { 58 | animation: App-logo-spin infinite 20s linear; 59 | } 60 | } 61 | 62 | .App-header { 63 | display: flex; 64 | flex-direction: column; 65 | align-items: center; 66 | justify-content: center; 67 | 68 | font-size: calc(10px + 2vmin); 69 | color: white; 70 | } 71 | 72 | .contentWrapper { 73 | box-sizing: border-box; 74 | margin: 5px 0; 75 | padding: 20px; 76 | 77 | color: var(--tg-theme-text-color) !important; 78 | 79 | background: var(--tg-theme-bg-color); 80 | border-radius: 10px; 81 | } 82 | 83 | .ant-input, 84 | .ant-select, 85 | .ant-select-item { 86 | background-color: unset !important; 87 | } 88 | 89 | @keyframes App-logo-spin { 90 | from { 91 | transform: rotate(0deg); 92 | } 93 | to { 94 | transform: rotate(360deg); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/containers/order/components/order-list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-one-expression-per-line */ 2 | /* eslint-disable react/jsx-wrap-multilines */ 3 | /* eslint-disable prettier/prettier */ 4 | // eslint-disable-next-line object-curly-newline 5 | import { Order } from "@framework/types"; 6 | import { addCommas } from "@persian-tools/persian-tools"; 7 | import { Divider, List } from "antd"; 8 | import { Link } from "react-router-dom"; 9 | 10 | interface Props { 11 | loading: boolean; 12 | orders: Order | undefined; 13 | } 14 | 15 | function OrderList({ loading, orders }: Props) { 16 | return ( 17 | <> 18 | ( 24 | 25 | 28 | نام محصول : 29 |
30 | {item.product_Name} 31 | 32 | } 33 | /> 34 | 35 |
36 |
37 | تومان 38 | {addCommas(item.final_Price)} 39 | قیمت واحد : 40 |
41 |
42 | عدد 43 | {item.quantity} 44 | تعداد : 45 |
46 | 47 |
48 | تومان 49 | {addCommas(item.final_Price * item.quantity)} 50 | قیمت کل : 51 |
52 |
53 |
54 | )} 55 | /> 56 | مجموع قیمت 57 |
58 | {addCommas(orders?.total_Price || 0)} تومان 59 |
60 | 61 | ); 62 | } 63 | 64 | export default OrderList; 65 | -------------------------------------------------------------------------------- /src/containers/order/components/customer-detail.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable operator-linebreak */ 2 | /* eslint-disable react/jsx-wrap-multilines */ 3 | /* eslint-disable prettier/prettier */ 4 | /* eslint-disable jsx-a11y/img-redundant-alt */ 5 | import { Order } from "@framework/types"; 6 | import { List } from "antd"; 7 | import moment from "jalali-moment"; 8 | 9 | interface Props { 10 | orders: Order | undefined; 11 | } 12 | function CustomerDetail({ orders }: Props) { 13 | return ( 14 | 15 | 16 | 20 | receipt-photo 28 |
29 | } 30 | /> 31 | 32 | 33 | 37 | 38 | 39 | 43 | 44 | 45 | {orders?.tracking_Code || "ثبت نشده"}
} 48 | /> 49 | 50 | 51 | 55 | شمسی : {" "} 56 | {moment(orders?.order_Date).locale("fa").format("YYYY/MM/DD") || 57 | ""} 58 |
59 | میلادی : {" "} 60 | {moment(orders?.order_Date).locale("en").format("YYYY/MM/DD") || 61 | ""} 62 |
63 | } 64 | /> 65 | 66 | 67 | ); 68 | } 69 | 70 | export default CustomerDetail; 71 | -------------------------------------------------------------------------------- /src/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-template", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "prev": "vite preview", 9 | "lint:eslint": "eslint . --ext .ts,.tsx", 10 | "fix:eslint": "eslint --fix . --ext .ts,.tsx", 11 | "lint:stylelint": "stylelint **/*.{css,scss,tsx}", 12 | "fix:stylelint": "stylelint --fix ./src/**/*.{css,scss,tsx}" 13 | }, 14 | "dependencies": { 15 | "@ant-design/icons": "^5.0.1", 16 | "@persian-tools/persian-tools": "^3.4.1", 17 | "@tanstack/react-query": "^4.29.7", 18 | "@vkruglikov/react-telegram-web-app": "^1.8.0", 19 | "antd": "^5.4.2", 20 | "antd-jalali": "^1.0.3", 21 | "axios": "^1.4.0", 22 | "clsx": "^1.2.1", 23 | "dayjs": "^1.11.8", 24 | "eslint-plugin-simple-import-sort": "^9.0.0", 25 | "formik": "^2.2.9", 26 | "jalali-moment": "^3.3.11", 27 | "jotai": "^2.1.0", 28 | "query-string": "^8.1.0", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-images-uploading": "^3.1.7", 32 | "react-router": "^6.10.0", 33 | "react-router-dom": "^6.10.0", 34 | "react-telegram-webapp": "^0.1.5", 35 | "yup": "^1.1.0" 36 | }, 37 | "devDependencies": { 38 | "@tanstack/eslint-plugin-query": "^4.29.4", 39 | "@types/eslint": "^8.21.1", 40 | "@types/node": "^18.11.18", 41 | "@types/prettier": "^2.7.2", 42 | "@types/react": "^18.0.26", 43 | "@types/react-dom": "^18.0.9", 44 | "@typescript-eslint/eslint-plugin": "^5.48.2", 45 | "@typescript-eslint/parser": "^5.48.2", 46 | "@vitejs/plugin-react": "^3.0.0", 47 | "autoprefixer": "^10.4.13", 48 | "eslint": "^8.32.0", 49 | "eslint-config-airbnb": "^19.0.4", 50 | "eslint-config-prettier": "^8.6.0", 51 | "eslint-plugin-import": "^2.27.5", 52 | "eslint-plugin-jsx-a11y": "^6.7.1", 53 | "eslint-plugin-prettier": "^4.2.1", 54 | "eslint-plugin-react": "^7.32.1", 55 | "eslint-plugin-react-hooks": "^4.6.0", 56 | "postcss": "^8.4.21", 57 | "prettier": "^2.8.3", 58 | "prettier-plugin-tailwindcss": "^0.2.1", 59 | "sass": "^1.57.1", 60 | "stylelint": "^14.16.1", 61 | "stylelint-config-clean-order": "^2.3.1", 62 | "stylelint-config-prettier": "^9.0.4", 63 | "stylelint-config-standard": "^29.0.0", 64 | "stylelint-config-standard-scss": "^6.1.0", 65 | "stylelint-order": "^6.0.1", 66 | "tailwindcss": "^3.2.4", 67 | "terser": "^5.16.1", 68 | "typescript": "^4.9.3", 69 | "vite": "^4.0.0", 70 | "vite-tsconfig-paths": "^4.0.3" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/containers/order/components/order-setting.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | import useUpdateOrder from "@framework/api/orders/update"; 3 | import { Order } from "@framework/types"; 4 | import useTelegramUser from "@hooks/useTelegramUser"; 5 | import { Button, Divider, Input, message, Radio } from "antd"; 6 | import { useState } from "react"; 7 | 8 | interface Props { 9 | orders: Order | undefined; 10 | } 11 | 12 | function OrderSetting({ orders }: Props) { 13 | const mutation = useUpdateOrder({ order_id: orders?.order_Id }); 14 | const [status, setStatus] = useState(orders?.order_Status); 15 | const [tracking_Code, setTracking_Code] = useState(orders?.tracking_Code); 16 | const { id } = useTelegramUser(); 17 | const onChange = (e) => { 18 | setStatus(e.target.value); 19 | }; 20 | const handleSubmitStatus = () => { 21 | mutation.mutate( 22 | { 23 | order_Status: status, 24 | tracking_Code: tracking_Code || "", 25 | user_Id: id.toString() 26 | }, 27 | { 28 | onSuccess: () => { 29 | message.success("وضعیت تغییر یافت"); 30 | }, 31 | onError: () => { 32 | message.error("مشکلی رخ داده"); 33 | } 34 | } 35 | ); 36 | }; 37 | return ( 38 |
39 | تعیین وضعیت سفارش 40 | 41 | در انتظار تایید 42 | درحال انجام 43 | درحال بسته بندی 44 | لغو توسط مشتری 45 | 46 | اتمام موجودی 1 یا چند کالا 47 | 48 | لغو توسط ادمین 49 | تحویل داده شده 50 | 51 | 52 |
53 |
کد رهگیری :
54 | { 56 | setTracking_Code(e.target.value); 57 | }} 58 | value={tracking_Code} 59 | /> 60 |
61 | 70 |
71 | ); 72 | } 73 | 74 | export default OrderSetting; 75 | -------------------------------------------------------------------------------- /src/pages/user/profile/home.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import useGetUserInfo from "@framework/api/user-information/get"; 3 | import useTelegramUser from "@hooks/useTelegramUser"; 4 | import { Alert, List } from "antd"; 5 | import { useEffect } from "react"; 6 | import { useLocation, useNavigate } from "react-router"; 7 | import { Link } from "react-router-dom"; 8 | 9 | function StatusBox({ title, total }: { title: string; total: number }) { 10 | return ( 11 |
12 |
{total}
13 |
{title}
14 |
15 | ); 16 | } 17 | 18 | function UserProfileHome() { 19 | const navigate = useNavigate(); 20 | const location = useLocation(); 21 | const { id } = useTelegramUser(); 22 | const { data, isFetching, isLoading, refetch } = useGetUserInfo({ 23 | user_Id: id 24 | }); 25 | useEffect(() => { 26 | refetch(); 27 | }, [location, refetch]); 28 | const isCompleteProfile = 29 | !data?.phone_Number || !data?.name || !data?.last_Name; 30 | return ( 31 | navigate("edit")} 36 | backwardUrl="/"> 37 | {/*
38 | {BoxItem.map((item) => ( 39 | 40 | ))} 41 |
*/} 42 | 43 | {isCompleteProfile && ( 44 | 48 |
لطفا حساب کاربری خود را قبل از ثبت سفارش تکمیل کنید
49 | 52 | تکمیل 53 | 54 |
55 | } 56 | /> 57 | )} 58 | 59 | 60 | 66 | 67 | 68 | 72 | 73 | 74 | 75 | ); 76 | } 77 | 78 | export default UserProfileHome; 79 | -------------------------------------------------------------------------------- /src/containers/boxes.tsx: -------------------------------------------------------------------------------- 1 | import { QueryCache } from "@tanstack/react-query"; 2 | import { Divider } from "antd"; 3 | import { Link } from "react-router-dom"; 4 | 5 | function Boxes() { 6 | // eslint-disable-next-line prettier/prettier 7 | const queryCache = new QueryCache(); 8 | 9 | const query = queryCache.findAll(["user-info"]); 10 | 11 | console.log(query); 12 | const itemClass = 13 | "w-full h-16 border-2 flex gap-3 border-[var(--tg-theme-button-color)] border-opacity-80 items-center justify-center rounded-lg "; 14 | return ( 15 |
16 | منو 17 |
18 | 19 | محصولات 20 | 21 | 28 | 33 | 34 | 35 | 36 | 37 | 38 | سفارشات من 39 | 40 | 47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 | ); 55 | } 56 | 57 | export default Boxes; 58 | -------------------------------------------------------------------------------- /src/components/product/card.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-one-expression-per-line */ 2 | /* eslint-disable object-curly-newline */ 3 | import { addCommas } from "@persian-tools/persian-tools"; 4 | import { Button, Divider } from "antd"; 5 | import { Link } from "react-router-dom"; 6 | 7 | interface Props { 8 | url: string; 9 | title: string; 10 | price: number; 11 | quantity: number; 12 | imageURL: string | []; 13 | discountedPrice: number; 14 | } 15 | function Card({ 16 | url, 17 | title, 18 | price, 19 | quantity, 20 | imageURL, 21 | discountedPrice 22 | }: Props) { 23 | const finalPrice = 24 | discountedPrice !== price && 100 - (discountedPrice * 100) / price; 25 | return ( 26 | 31 |
36 | {finalPrice && ( 37 | 38 | {finalPrice} % 39 | 40 | )} 41 |
42 |
43 |
44 | {title} 45 |
46 |
47 | {/*
48 |
49 | ⭐4.3 50 |
51 |
غذا
52 |
*/} 53 | 54 |
58 | تومان {addCommas(price)} 59 |
60 | {finalPrice && ( 61 |
62 | تومان {addCommas(discountedPrice)} 63 |
64 | )} 65 | {/*
تعداد :{quantity} عدد
*/} 66 |
67 | 70 |
71 | 72 | ); 73 | } 74 | 75 | export default Card; 76 | -------------------------------------------------------------------------------- /src/pages/bot/masters/list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable no-nested-ternary */ 3 | /* eslint-disable object-curly-newline */ 4 | import { DeleteOutlined, EditOutlined } from "@ant-design/icons"; 5 | import Container from "@components/container"; 6 | import useDeleteMaster from "@framework/api/master/delete"; 7 | import { useGetMasters } from "@framework/api/master/get"; 8 | import useTelegramUser from "@hooks/useTelegramUser"; 9 | import { Button, Popconfirm, Space, Table, message } from "antd"; 10 | import { useEffect } from "react"; 11 | import { useLocation, useNavigate } from "react-router"; 12 | import { Link } from "react-router-dom"; 13 | 14 | function BotMastersList() { 15 | const navigate = useNavigate(); 16 | const { data, isFetching, isLoading, refetch } = useGetMasters(); 17 | const deleteMutation = useDeleteMaster(); 18 | const location = useLocation(); 19 | const { id: user_id } = useTelegramUser(); 20 | const handleDeleteMaster = (id: string) => { 21 | deleteMutation.mutate( 22 | { 23 | master_id: id, 24 | user_id 25 | }, 26 | { 27 | onSuccess: () => { 28 | message.success("کاربر حذف شد"); 29 | refetch(); 30 | }, 31 | onError: () => { 32 | message.error(" مشکل برای حذف رخ داد "); 33 | refetch(); 34 | } 35 | } 36 | ); 37 | }; 38 | 39 | const dataSource = data?.map((item) => ({ 40 | ...item, 41 | title: `${item.name} ${item.last_Name} `, 42 | key: item.id 43 | })); 44 | useEffect(() => { 45 | refetch(); 46 | }, [location, refetch]); 47 | 48 | const columns = [ 49 | { 50 | title: "نام", 51 | dataIndex: "title", 52 | key: "name" 53 | }, 54 | { 55 | title: "عملیات", 56 | dataIndex: "عملیات", 57 | key: "عملیات", 58 | render: (_, record) => ( 59 | 60 | 61 | 62 | 63 | handleDeleteMaster(record.id)} 67 | okText="حذف" 68 | okType="default" 69 | cancelText="انصراف"> 70 | 73 | 74 | 75 | ) 76 | } 77 | ]; 78 | return ( 79 | navigate("add")}> 85 | 90 | 91 | ); 92 | } 93 | 94 | export default BotMastersList; 95 | -------------------------------------------------------------------------------- /src/components/product/item.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable operator-linebreak */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | /* eslint-disable react/jsx-one-expression-per-line */ 4 | import { addCommas } from "@persian-tools/persian-tools"; 5 | import { useNavigate } from "react-router"; 6 | 7 | interface Props { 8 | url: string; 9 | title: string; 10 | price: number; 11 | quantity: number; 12 | imageURL: string | Array; 13 | pageType: "admin" | "user"; 14 | discountedPrice: number; 15 | } 16 | 17 | function ProductItem({ 18 | url, 19 | title, 20 | price, 21 | quantity, 22 | imageURL, 23 | pageType, 24 | discountedPrice 25 | }: Props) { 26 | const navigate = useNavigate(); 27 | 28 | const handleClick = () => { 29 | navigate(url); 30 | }; 31 | const finalPrice = 32 | discountedPrice !== price && 100 - (discountedPrice * 100) / price; 33 | return ( 34 | // eslint-disable-next-line jsx-a11y/no-static-element-interactions 35 |
40 |
41 |

42 | {title} 43 |

44 |
45 | {/*
46 |
47 | ⭐4.3 48 |
49 |
غذا
50 |
*/} 51 |
55 | تومان {addCommas(price)} 56 |
57 | {finalPrice && ( 58 |
59 | تومان {addCommas(discountedPrice)} 60 |
61 | )} 62 | {pageType === "admin" && ( 63 |
تعداد :{quantity} عدد
64 | )} 65 |
66 |
67 |
72 | {finalPrice && ( 73 | 74 | {finalPrice} % 75 | 76 | )} 77 |
78 |
79 | ); 80 | } 81 | 82 | export default ProductItem; 83 | -------------------------------------------------------------------------------- /src/pages/admin/slider/list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-hooks/rules-of-hooks */ 2 | /* eslint-disable camelcase */ 3 | /* eslint-disable no-nested-ternary */ 4 | /* eslint-disable object-curly-newline */ 5 | import { DeleteOutlined } from "@ant-design/icons"; 6 | import Container from "@components/container"; 7 | import useDeleteSlider from "@framework/api/slider/delete"; 8 | import { useGetSliders } from "@framework/api/slider/get"; 9 | import useTelegramUser from "@hooks/useTelegramUser"; 10 | import { Button, message, Popconfirm, Space, Table } from "antd"; 11 | import { useEffect } from "react"; 12 | import { useLocation, useNavigate } from "react-router"; 13 | 14 | function list() { 15 | const navigate = useNavigate(); 16 | const { data, isFetching, isLoading, refetch } = useGetSliders(); 17 | const deleteMutation = useDeleteSlider(); 18 | const location = useLocation(); 19 | const { id: user_id } = useTelegramUser(); 20 | const handleDeleteSlide = (id: string) => { 21 | deleteMutation.mutate( 22 | { 23 | master_id: id, 24 | user_id 25 | }, 26 | { 27 | onSuccess: () => { 28 | message.success("اسلاید حذف شد"); 29 | refetch(); 30 | }, 31 | onError: () => { 32 | message.error(" مشکل برای حذف رخ داد "); 33 | refetch(); 34 | } 35 | } 36 | ); 37 | }; 38 | 39 | const dataSource = data?.map((item) => ({ 40 | ...item, 41 | 42 | key: item.id 43 | })); 44 | useEffect(() => { 45 | refetch(); 46 | }, [location, refetch]); 47 | 48 | const columns = [ 49 | { 50 | title: "نام", 51 | key: "name", 52 | render: (_, record) => ( 53 |
54 | slider 58 |
59 | ) 60 | }, 61 | { 62 | title: "عملیات", 63 | key: "عملیات", 64 | render: (_, record) => ( 65 | 66 | handleDeleteSlide(record.id)} 70 | okText="حذف" 71 | okType="default" 72 | cancelText="انصراف"> 73 | 76 | 77 | 78 | ) 79 | } 80 | ]; 81 | return ( 82 | navigate("add")}> 88 |
93 | 94 | ); 95 | } 96 | 97 | export default list; 98 | -------------------------------------------------------------------------------- /src/containers/order/list-admin.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import { useGetOrders } from "@framework/api/orders/get"; 3 | import { GetOrderStatus } from "@helpers/get-order-status"; 4 | import { addCommas } from "@persian-tools/persian-tools"; 5 | import { Table } from "antd"; 6 | import { ColumnsType } from "antd/es/table"; 7 | import moment from "jalali-moment"; 8 | import { Link } from "react-router-dom"; 9 | 10 | interface DataType { 11 | key: string; 12 | code: string; 13 | name: string; 14 | time: string; 15 | status: string; 16 | tracking_code: string; 17 | } 18 | 19 | function OrderListAdmin() { 20 | const { data, isLoading, isFetching } = useGetOrders(); 21 | const orders = data?.orders || []; 22 | const dataChangingStructure: DataType[] = 23 | orders.map((item) => ({ 24 | key: item.order_Id.toString(), 25 | code: item.order_Id.toString(), 26 | name: item.user_Full_Name, 27 | price: item.total_Price, 28 | status: item.order_Status, 29 | time: item.order_Date, 30 | tracking_code: item.tracking_Code 31 | })) || []; 32 | const columns: ColumnsType = [ 33 | { 34 | title: "شماره ", 35 | width: "fit-content", 36 | dataIndex: "code", 37 | key: "code", 38 | render: (text, record) => ( 39 | 42 | {text}# 43 | 44 | ) 45 | }, 46 | { 47 | title: "نام", 48 | 49 | dataIndex: "name", 50 | key: "name", 51 | render: (text, record) => ( 52 | 55 | {text} 56 | 57 | ) 58 | }, 59 | { 60 | title: "مبلغ", 61 | 62 | dataIndex: "price", 63 | key: "price", 64 | render: (text, record) => ( 65 | 68 | {addCommas(text || 0)} 69 | 70 | ) 71 | }, 72 | 73 | { 74 | title: "وضعیت", 75 | dataIndex: "status", 76 | key: "status", 77 | render: (text) =>

{GetOrderStatus(text)}

78 | }, 79 | { 80 | title: "تاریخ ثبت سفارش", 81 | dataIndex: "time", 82 | key: "time", 83 | render: (text) => ( 84 |

{moment(text).locale("fa").format("YYYY/MM/DD") || ""}

85 | ) 86 | } 87 | ]; 88 | 89 | return ( 90 | 91 |
97 | 98 | ); 99 | } 100 | 101 | export default OrderListAdmin; 102 | -------------------------------------------------------------------------------- /src/pages/user/profile/addresses/add.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable object-curly-newline */ 2 | /* eslint-disable prettier/prettier */ 3 | /* eslint-disable camelcase */ 4 | /* eslint-disable react/jsx-wrap-multilines */ 5 | import Container from "@components/container"; 6 | import useAddAddress from "@framework/api/address/add"; 7 | import { TypeAddAddress } from "@framework/types"; 8 | import useTelegramUser from "@hooks/useTelegramUser"; 9 | import { Button, Form, Input, message } from "antd"; 10 | import { useNavigate } from "react-router"; 11 | 12 | function AddAddress() { 13 | const navigate = useNavigate(); 14 | const mutation = useAddAddress(); 15 | const { id } = useTelegramUser(); 16 | const onFinish = ({ 17 | city, 18 | country, 19 | state, 20 | street, 21 | user_id, 22 | zipcode 23 | }: TypeAddAddress) => { 24 | mutation.mutate( 25 | { 26 | city, 27 | country, 28 | state, 29 | street, 30 | user_id, 31 | zipcode, 32 | user_Id: `${id}` 33 | }, 34 | { 35 | onSuccess: () => { 36 | message.success("آدرس شما با موفقیت ثبت شد"); 37 | setTimeout(() => navigate(-1), 1000); 38 | }, 39 | onError: () => { 40 | message.error("مشکلی رخ داده است لطفا دوباره تلاش کنید"); 41 | } 42 | } 43 | ); 44 | }; 45 | 46 | const onFinishFailed = (errorInfo: any) => { 47 | console.log("Failed:", errorInfo); 48 | }; 49 | return ( 50 | 51 |
59 | 63 | 64 | 65 | 66 | 70 | 71 | 72 | 76 | 77 | 78 | 82 | 83 | 84 | 88 | 89 | 90 | 91 | 92 | 101 | 102 | 103 |
104 | ); 105 | } 106 | 107 | export default AddAddress; 108 | -------------------------------------------------------------------------------- /src/containers/order/list.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import { useGetOrderByUser } from "@framework/api/orders/get-by-user"; 3 | import { GetOrderStatus } from "@helpers/get-order-status"; 4 | import useTelegramUser from "@hooks/useTelegramUser"; 5 | import { addCommas } from "@persian-tools/persian-tools"; 6 | import { Table } from "antd"; 7 | import { ColumnsType } from "antd/es/table"; 8 | import moment from "jalali-moment"; 9 | import { useEffect } from "react"; 10 | import { Link, useLocation } from "react-router-dom"; 11 | 12 | interface DataType { 13 | key: string; 14 | name: string; 15 | code: string; 16 | time: string; 17 | status: string; 18 | tracking_code: string; 19 | } 20 | interface Props { 21 | type: "profile" | "user"; 22 | } 23 | 24 | function OrderList({ type }: Props) { 25 | const { id } = useTelegramUser(); 26 | const location = useLocation(); 27 | const { data, isLoading, isFetching, refetch } = useGetOrderByUser({ 28 | user_id: id 29 | }); 30 | useEffect(() => { 31 | refetch(); 32 | }, [refetch, location]); 33 | const orders = data?.orders || []; 34 | const dataChangingStructure: DataType[] = 35 | orders.map((item) => ({ 36 | key: item.order_Id.toString(), 37 | code: item.order_Id.toString(), 38 | name: item.user_Full_Name, 39 | price: item.total_Price, 40 | status: item.order_Status, 41 | time: item.order_Date, 42 | tracking_code: item.tracking_Code 43 | })) || []; 44 | 45 | const columns: ColumnsType = [ 46 | { 47 | title: "شماره ", 48 | width: "fit-content", 49 | dataIndex: "code", 50 | key: "code", 51 | render: (text, record) => ( 52 | 55 | {text}# 56 | 57 | ) 58 | }, 59 | { 60 | title: "نام", 61 | 62 | dataIndex: "name", 63 | key: "name", 64 | render: (text, record) => ( 65 | 68 | {text} 69 | 70 | ) 71 | }, 72 | { 73 | title: "مبلغ", 74 | 75 | dataIndex: "price", 76 | key: "price", 77 | render: (text, record) => ( 78 | 81 | {addCommas(text || 0)} 82 | 83 | ) 84 | }, 85 | 86 | { 87 | title: "وضعیت", 88 | dataIndex: "status", 89 | key: "status", 90 | render: (text) =>

{GetOrderStatus(text)}

91 | }, 92 | { 93 | title: "تاریخ ثبت سفارش", 94 | dataIndex: "time", 95 | key: "time", 96 | render: (text) => ( 97 |

{moment(text).locale("fa").format("YYYY/MM/DD") || ""}

98 | ) 99 | } 100 | ]; 101 | 102 | return ( 103 | 104 |
110 | 111 | ); 112 | } 113 | 114 | export default OrderList; 115 | -------------------------------------------------------------------------------- /src/pages/admin/categories/add.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable implicit-arrow-linebreak */ 2 | /* eslint-disable indent */ 3 | /* eslint-disable object-curly-newline */ 4 | // eslint-disable-next-line object-curly-newline 5 | import Container from "@components/container"; 6 | import useAddCategories from "@framework/api/categories/add"; 7 | import useTelegramUser from "@hooks/useTelegramUser"; 8 | import { Button, Form, Input, message } from "antd"; 9 | import { useNavigate, useParams } from "react-router"; 10 | 11 | interface FromProps { 12 | name: string; 13 | description?: string; 14 | } 15 | 16 | function CategoriesAdd() { 17 | const [form] = Form.useForm(); 18 | const mutation = useAddCategories(); 19 | const { id } = useTelegramUser(); 20 | // const { data, refetch, isLoading, isFetching } = useGetCategories(); 21 | // const isLoadCategories = isLoading || isFetching; 22 | const { parentId } = useParams(); 23 | const navigate = useNavigate(); 24 | return ( 25 | 26 |
{ 33 | // console.log("params", name, parentId); 34 | mutation.mutate( 35 | { 36 | user_id: `${id}`, 37 | category_name: name, 38 | parent_id: (parentId && parseInt(parentId)) || null 39 | }, 40 | { 41 | onSuccess: () => { 42 | message.success("دسته بندی شما با موفقیت ثبت شد"); 43 | form.resetFields(); 44 | navigate("/admin/categories"); 45 | }, 46 | onError: (err) => { 47 | message.error("مشکلی رخ داده است لطفا دقایقی دیگر تلاش کنید"); 48 | console.log(err); 49 | } 50 | } 51 | ); 52 | }}> 53 | 54 | 55 | 56 | 57 | {/* 58 | 59 | */} 60 | 61 | {/* 62 | 75 | 76 | */} 77 | {/* 78 | 79 | 80 | */} 81 | 82 | 93 | 94 |
95 | ); 96 | } 97 | 98 | export default CategoriesAdd; 99 | -------------------------------------------------------------------------------- /src/pages/user/profile/edit.tsx: -------------------------------------------------------------------------------- 1 | import Container from "@components/container"; 2 | import useGetUserInfo from "@framework/api/user-information/get"; 3 | import useUpdateUser from "@framework/api/user-information/update"; 4 | import useTelegramUser from "@hooks/useTelegramUser"; 5 | import { phoneNumberValidator } from "@persian-tools/persian-tools"; 6 | import { Button, Form, Input, message, Spin } from "antd"; 7 | import { useNavigate } from "react-router"; 8 | 9 | function EditProfile() { 10 | const [form] = Form.useForm(); 11 | const { id } = useTelegramUser(); 12 | const { data, isFetching, isLoading } = useGetUserInfo({ user_Id: id }); 13 | const mutation = useUpdateUser({ user_id: id }); 14 | const navigate = useNavigate(); 15 | const dataLoading = isFetching || isLoading; 16 | return ( 17 | 18 | 19 | {dataLoading ? ( 20 |
21 | ) : ( 22 |
{ 35 | console.log(e); 36 | if (!phoneNumberValidator(e.phone_number)) { 37 | message.error("شماره همراه وارد شده اشتباه است"); 38 | form.scrollToField("phone_number"); 39 | } else { 40 | mutation.mutate(e, { 41 | onSuccess: () => { 42 | message.success("حساب کاربری شما بروزرسانی شد "); 43 | navigate(-1); 44 | }, 45 | onError: (e) => { 46 | message.error("مشکل در بروزرسانی حساب کاربری "); 47 | } 48 | }); 49 | } 50 | }} 51 | autoComplete="off"> 52 | 56 | 57 | 58 | 62 | 63 | 64 | 65 | 66 | 67 | 71 | 72 | 73 | 84 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | )} 94 | 95 | 96 | ); 97 | } 98 | 99 | export default EditProfile; 100 | -------------------------------------------------------------------------------- /src/pages/user/profile/addresses/list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-hooks/exhaustive-deps */ 2 | /* eslint-disable prettier/prettier */ 3 | /* eslint-disable camelcase */ 4 | /* eslint-disable react/jsx-wrap-multilines */ 5 | 6 | import Container from "@components/container"; 7 | import useDeleteAddress from "@framework/api/address/delete"; 8 | import { useGetAddresses } from "@framework/api/address/get"; 9 | import useTelegramUser from "@hooks/useTelegramUser"; 10 | import { Button, List, message, Popconfirm } from "antd"; 11 | import { useEffect } from "react"; 12 | import { useLocation, useNavigate } from "react-router"; 13 | 14 | function AddessesList() { 15 | const navigate = useNavigate(); 16 | const { id } = useTelegramUser(); 17 | const { data, isLoading, isFetching, refetch } = useGetAddresses(id); 18 | const deleteMutation = useDeleteAddress(); 19 | const location = useLocation(); 20 | useEffect(() => { 21 | refetch(); 22 | }, [location]); 23 | return ( 24 | navigate("add")} 29 | backwardUrl="/"> 30 | ( 35 | 36 | 41 | // } 42 | title={
{item.city}
} 43 | description={ 44 |
45 |
46 | {item.state} 47 |
48 |
49 | {item.zipcode} 50 |
51 |
52 | } 53 | /> 54 | 55 |
56 | { 61 | deleteMutation.mutate( 62 | { user_id: id, address_id: item.address_Id }, 63 | { 64 | onSuccess: () => { 65 | message.success("آدرس با موفقیت حذف شد"); 66 | refetch(); 67 | }, 68 | onError: (e) => { 69 | console.log(e); 70 | message.error("حذف نشد دوباره تلاش کنید!"); 71 | refetch(); 72 | } 73 | } 74 | ); 75 | }} 76 | okText="حذف" 77 | okType="default" 78 | cancelText="انصراف"> 79 | 85 | 86 | 91 |
92 |
93 | )} 94 | /> 95 |
96 | ); 97 | } 98 | 99 | export default AddessesList; 100 | -------------------------------------------------------------------------------- /src/router/routes.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | // eslint-disable-next-line object-curly-newline 3 | import NotFoundPage from "@containers/404"; 4 | import { 5 | AdminAddSlider, 6 | AdminHome, 7 | AdminOrders, 8 | AdminOrdersSingle, 9 | AdminProductList, 10 | AdminSlider, 11 | BotAddMasters, 12 | BotEditMasters, 13 | BotMasters, 14 | BotPanel, 15 | BotSetting, 16 | Categories, 17 | CategoriesAdd, 18 | CategoriesEdit, 19 | Checkout, 20 | HomePage, 21 | ProductAdd, 22 | ProductEdit, 23 | ProductList, 24 | ProductSingle, 25 | UserCart, 26 | UserProfile, 27 | UserProfileAddAddresses, 28 | UserProfileAddresses, 29 | UserProfileEdit, 30 | UserProfileEditAddresses, 31 | UserProfileHome, 32 | UserProfileOrder, 33 | UserProfileOrderSingle 34 | } from "@pages/index"; 35 | import { createBrowserRouter } from "react-router-dom"; 36 | 37 | export const routes = createBrowserRouter([ 38 | { 39 | path: "/", 40 | element: 41 | }, 42 | // admin 43 | { path: "/admin", element: }, 44 | // ## products 45 | { path: "/admin/products", element: }, 46 | { path: "/admin/products/add", element: }, 47 | { path: "/admin/products/:product_id", element: }, 48 | // ## categories 49 | { path: "/admin/categories", element: }, 50 | { path: "/admin/categories/:parentId", element: }, 51 | { path: "/admin/categories/edit/:cat_id", element: }, 52 | // ## order 53 | { path: "/admin/orders", element: }, 54 | { path: "/admin/orders/:order_id", element: }, 55 | // ## slider 56 | { path: "/admin/slider", element: }, 57 | { path: "/admin/slider/add", element: }, 58 | 59 | // user 60 | { 61 | path: "/products", 62 | element: 63 | }, 64 | { 65 | path: "/products/:product_id", 66 | element: 67 | }, 68 | { 69 | path: "/categories", 70 | element: 71 | }, 72 | { 73 | path: "/cart", 74 | element: 75 | }, 76 | { 77 | path: "/checkout", 78 | element: 79 | }, 80 | 81 | // ## profile 82 | { 83 | path: "/profile", 84 | element: , 85 | children: [ 86 | { 87 | index: true, 88 | path: "home", 89 | element: 90 | }, 91 | { 92 | index: true, 93 | path: "home/edit", 94 | element: 95 | }, 96 | { 97 | path: "orders", 98 | element: 99 | }, 100 | { 101 | path: "orders/:order_id", 102 | element: 103 | }, 104 | { 105 | path: "address", 106 | element: 107 | }, 108 | { 109 | path: "address/add", 110 | element: 111 | }, 112 | { 113 | path: "address/:address_id", 114 | element: 115 | } 116 | ] 117 | }, 118 | { 119 | path: "/bot", 120 | element: , 121 | children: [ 122 | { 123 | path: "", 124 | element: 125 | }, 126 | { 127 | path: "masters", 128 | element: 129 | }, 130 | { 131 | path: "masters/add", 132 | element: 133 | }, 134 | { 135 | path: "masters/:master_id", 136 | element: 137 | } 138 | ] 139 | }, 140 | 141 | { 142 | path: "*", 143 | element: 144 | } 145 | ]); 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Web App Shop 2 | 3 | Welcome to the `Telegram Web App Shop` repository! This project is an innovative e-commerce solution built as a Telegram web app using modern web technologies. It integrates with a backend API to deliver a seamless shopping experience. 4 | 5 | --- 6 | 7 | ## 🖥️ Backend Project 8 | 9 | This project relies on a backend API developed by my friend. You can find it here: 10 | [Backend Repository: Ecommerce Project](https://github.com/Ecarin/Ecommerce.Project) 11 | 12 | The backend provides the necessary APIs to handle product management, user authentication, orders, and more. 13 | 14 | --- 15 | 16 | ## 🛠️ Project Overview 17 | 18 | This project is a web-based e-commerce platform featuring: 19 | - **Core Features**: 20 | - Product and category browsing 21 | - User account management 22 | - Cart and order checkout 23 | - Discounts and promotions 24 | - **Architecture**: 25 | - Modular React-based front-end design 26 | - Integration with Telegram WebApp API for seamless user experience 27 | - **Design**: 28 | - Built with **TailwindCSS** for responsive and visually appealing interfaces 29 | - **API Interaction**: 30 | - Connects to backend API endpoints for data operations 31 | 32 | --- 33 | 34 | ## 📂 Project Structure 35 | 36 | ### **Public** 37 | - Contains static assets like fonts, images, and the Telegram WebApp integration script (`telegram.js`). 38 | 39 | ### **Src** 40 | - **Components**: Reusable React components for UI elements, such as product cards and lists. 41 | - **Containers**: Higher-level components organizing page structures, like sliders and admin panels. 42 | - **Framework**: API interaction logic, including endpoints for products, orders, and user data. 43 | - **Pages**: Specific page layouts for different user roles (admin, user) and features (cart, profile). 44 | - **Helpers**: Utility functions to enhance reusability and maintainability. 45 | - **Styles**: SCSS and CSS files for styling components and layouts. 46 | 47 | --- 48 | 49 | ## ⚠️ Known Issues and Limitations 50 | 51 | - **Environment Configuration**: 52 | The `.env` file must be configured to set up Telegram WebApp integration and connect with the backend API. 53 | 54 | - **Development Phase**: 55 | This project is still in development and may have incomplete features or bugs. 56 | 57 | - **Backend Dependency**: 58 | Full functionality requires a running instance of the backend API. 59 | 60 | --- 61 | 62 | ## 🚀 Future Plans 63 | 64 | Planned improvements include: 65 | - **Feature Expansion**: Adding support for analytics and advanced admin controls. 66 | - **Error Handling**: Enhancing error messages and fail-safes for edge cases. 67 | - **Performance Optimization**: Refining front-end and API calls for better responsiveness. 68 | 69 | --- 70 | 71 | ## 📝 How to Use 72 | 73 | Follow these steps to set up and run the project: 74 | 75 | 1. **Clone the repository**: 76 | ```bash 77 | git clone https://github.com/mojtaba1180/telegram-web-app-shop.git 78 | cd telegram-web-app-shop 79 | ``` 80 | 81 | 2. **Install dependencies**: 82 | ```bash 83 | pnpm i 84 | ``` 85 | 86 | 3. **Configure environment variables**: 87 | Create a `.env` file in the root directory with the following variables: 88 | ``` 89 | VITE_APP_BACKEND_URL= 90 | VITE_APP_TELEGRAM_BOT_TOKEN= 91 | ``` 92 | 93 | 4. **Run the project**: 94 | Start the development server: 95 | ```bash 96 | pnpm dev 97 | ``` 98 | 99 | 5. **Access the application**: 100 | Open the browser at: 101 | ``` 102 | http://localhost:3000 103 | ``` 104 | 105 | 6. **Integrate with the backend**: 106 | Ensure the backend API is running and accessible at the configured `REACT_APP_BACKEND_URL`. 107 | 108 | --- 109 | 110 | ## 🧰 Tools and Technologies 111 | 112 | - **Frontend Framework**: React 113 | - **Styling**: TailwindCSS, SCSS 114 | - **Build Tool**: Vite 115 | - **API Integration**: Axios 116 | 117 | --- 118 | 119 | ## 📞 Contact 120 | 121 | If you have any questions, suggestions, or feedback about this project, feel free to reach out: 122 | 123 | - **GitHub Profile**: [My GitHub](https://github.com/mojtaba1180) 124 | - **Telegram**: [@mojtaba1180](https://t.me/mojtaba1180) 125 | 126 | -------------------------------------------------------------------------------- /src/pages/user/profile/addresses/edit.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable object-curly-newline */ 2 | /* eslint-disable prettier/prettier */ 3 | /* eslint-disable camelcase */ 4 | /* eslint-disable react/jsx-wrap-multilines */ 5 | import Container from "@components/container"; 6 | import { useGetAddresses } from "@framework/api/address/get"; 7 | import useUpdateAddress from "@framework/api/address/update"; 8 | import { TypeAddAddress } from "@framework/types"; 9 | import useTelegramUser from "@hooks/useTelegramUser"; 10 | import { Button, Form, Input, message, Spin } from "antd"; 11 | import { useEffect } from "react"; 12 | import { useLocation, useNavigate, useParams } from "react-router"; 13 | 14 | function EditAddress() { 15 | const navigate = useNavigate(); 16 | const { address_id } = useParams(); 17 | const mutation = useUpdateAddress(); 18 | const { id } = useTelegramUser(); 19 | const { data, isFetching, refetch, isLoading } = useGetAddresses( 20 | id, 21 | address_id 22 | ); 23 | const address = data?.addresses[0]; 24 | const location = useLocation(); 25 | useEffect(() => { 26 | refetch(); 27 | }, [location]); 28 | 29 | const componentDisable = isFetching || isLoading; 30 | const onFinish = ({ 31 | city, 32 | country, 33 | state, 34 | street, 35 | user_id, 36 | zipcode 37 | }: TypeAddAddress) => { 38 | mutation.mutate( 39 | { 40 | city, 41 | country, 42 | state, 43 | street, 44 | user_id, 45 | zipcode, 46 | user_Id: `${id}`, 47 | address_Id: address_id 48 | }, 49 | { 50 | onSuccess: () => { 51 | message.success("آدرس شما با موفقیت ثبت شد"); 52 | navigate(-1); 53 | }, 54 | onError: () => { 55 | message.error("مشکلی رخ داده است لطفا دوباره تلاش کنید"); 56 | } 57 | } 58 | ); 59 | }; 60 | 61 | const onFinishFailed = (errorInfo: any) => { 62 | console.log("Failed:", errorInfo); 63 | }; 64 | return ( 65 | 66 | 67 | {componentDisable ? ( 68 |
69 | ) : ( 70 |
84 | 88 | 89 | 90 | 91 | 95 | 96 | 97 | 101 | 102 | 103 | 107 | 108 | 109 | 113 | 114 | 115 | 116 | 117 | 126 | 127 | 128 | )} 129 | 130 | 131 | ); 132 | } 133 | 134 | export default EditAddress; 135 | -------------------------------------------------------------------------------- /src/pages/admin/categories/list.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable object-curly-newline */ 3 | import { DeleteOutlined, EditOutlined } from "@ant-design/icons"; 4 | // eslint-disable-next-line import/extensions 5 | import Container from "@components/container"; 6 | import useDeleteCategories from "@framework/api/categories/delete"; 7 | import { useGetCategories } from "@framework/api/categories/get"; 8 | import { TypeCategories } from "@framework/types"; 9 | import useTelegramUser from "@hooks/useTelegramUser"; 10 | import { Button, message, Modal, Space, Table } from "antd"; 11 | import { ColumnsType } from "antd/es/table"; 12 | import { useEffect } from "react"; 13 | import { useNavigate } from "react-router"; 14 | import { Link } from "react-router-dom"; 15 | 16 | const { confirm } = Modal; 17 | function List() { 18 | const { data, error, isLoading, isFetching, refetch } = useGetCategories({}); 19 | const mutationDelete = useDeleteCategories(); 20 | const { id } = useTelegramUser(); 21 | const navigate = useNavigate(); 22 | 23 | const customizeData = () => { 24 | const childHandler = (childItem: any[]) => { 25 | if (childItem.length > 0) { 26 | return childItem.map((item) => { 27 | const c = { 28 | ...item, 29 | key: item.category_Id 30 | }; 31 | 32 | if (item.children && item.children.length > 0) { 33 | c.children = childHandler(item.children); 34 | } else { 35 | c.children = null; 36 | } 37 | return c; 38 | }); 39 | } 40 | return null; 41 | }; 42 | return data?.map((item) => ({ 43 | ...item, 44 | key: item.category_Id, 45 | children: childHandler(item.children) 46 | })); 47 | }; 48 | 49 | const handleDelete = (cat_id: any) => { 50 | mutationDelete.mutate( 51 | { category_id: cat_id, user_id: id }, 52 | { 53 | onSuccess: () => { 54 | message.success("دسته بندی حذف شد"); 55 | refetch(); 56 | }, 57 | onError: (err) => { 58 | if (err.response.status !== 404) { 59 | message.error("حذف با مشکل مواجه شد"); 60 | refetch(); 61 | } else { 62 | window.location.reload(); 63 | } 64 | } 65 | } 66 | ); 67 | }; 68 | const config = (cat_id) => ({ 69 | title: " برای حذف این دسته بندی اطمینان دارید ؟", 70 | content: <>sdd, 71 | okType: "danger", 72 | cancelText: "انصراف", 73 | okText: "حذف", 74 | onOk: () => handleDelete(cat_id) 75 | }); 76 | useEffect(() => { 77 | refetch(); 78 | }, []); 79 | const columns: ColumnsType = [ 80 | { 81 | title: "نام", 82 | dataIndex: "name", 83 | key: "name", 84 | render: (_, record) => ( 85 |
{record.category_Name}
86 | ) 87 | }, 88 | { 89 | title: "عملیات", 90 | key: "action", 91 | render: (_, record) => ( 92 | 93 | 96 | 97 | 98 | 107 | 108 | افزودن زیرمجموعه 109 | 110 | 111 | ) 112 | } 113 | ]; 114 | 115 | return ( 116 | navigate("/admin/categories/null")} 121 | title="دسته بندی ها"> 122 | {/* 123 |
124 | 125 | 126 |
127 |
*/} 128 |
129 | {/* */} 130 |
135 | {/* */} 136 | {/* */} 137 | 138 | ); 139 | } 140 | 141 | export default List; 142 | -------------------------------------------------------------------------------- /src/pages/bot/setting/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | /* eslint-disable operator-linebreak */ 3 | /* eslint-disable react/no-array-index-key */ 4 | /* eslint-disable camelcase */ 5 | 6 | import { InfoCircleOutlined } from "@ant-design/icons"; 7 | import Container from "@components/container"; 8 | import { useGetBotSetting } from "@framework/api/bot-setting/get"; 9 | import useUpdateBotSetting from "@framework/api/bot-setting/update"; 10 | import useTelegramUser from "@hooks/useTelegramUser"; 11 | import { Button, Divider, Form, List, message, Popover, Spin } from "antd"; 12 | import TextArea from "antd/es/input/TextArea"; 13 | 14 | function BotSetting() { 15 | const mutation = useUpdateBotSetting(); 16 | const { id } = useTelegramUser(); 17 | const { data, isFetching, isLoading, refetch } = useGetBotSetting(); 18 | const loading = isFetching || isLoading; 19 | return ( 20 | 21 | 22 | {loading ? ( 23 |
24 | ) : ( 25 | <> 26 |
{ 37 | mutation.mutate( 38 | { 39 | ...props, 40 | user_id: id.toString() 41 | }, 42 | { 43 | onSuccess: () => { 44 | message.success("تنظیمات با موفقیت ثبت شد."); 45 | refetch(); 46 | }, 47 | onError: () => { 48 | message.error( 49 | "ثبت با مشکل مواجه شد دقایقی دیگر تلاش کنید" 50 | ); 51 | refetch(); 52 | } 53 | } 54 | ); 55 | }}> 56 | 57 |