commit 2852c2c05455f1d1b8de7bf70ea9151f327ebd99 Author: Mohammed Al-yaseen Date: Sat Oct 4 18:22:24 2025 +0300 Initial commit diff --git a/.env b/.env new file mode 100644 index 0000000..e5a30ab --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_BASE_URL=http://localhost:3000 +VITE_BASE_API_URL=https://dev.fascano.com/api/user_app/ +VITE_DEFAULT_LANGUAGE="en" \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..9ee95cf --- /dev/null +++ b/.env.production @@ -0,0 +1,3 @@ +VITE_BASE_API_URL = https://menu.fascano.com/api/user_app/ +VITE_BASE_URL = https://pos_development_easypay.generation-zee.ae/ +VITE_DEFAULT_LANGUAGE = "ar" diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..bb041fd --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,24 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks + "react/require-default-props": "off", + "no-use-before-define": "off", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/ban-ts-comment": "warn", + "no-param-reassign": ["warn", { "props": true, "ignorePropertyModificationsFor": ["state"] }], + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c9ed28 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Fascano \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..7fb58a2 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Fascano | Restaurant + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..28f1407 --- /dev/null +++ b/package.json @@ -0,0 +1,53 @@ +{ + "name": "easy-pay", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --open", + "build": "vite build", + "production": "vite --mode production --open", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons": "^5.3.7", + "@googlemaps/react-wrapper": "^1.2.0", + "@react-pdf/renderer": "^3.4.4", + "@reduxjs/toolkit": "^2.2.6", + "@types/node": "^20.14.11", + "antd": "^5.27.4", + "dayjs": "^1.11.11", + "fuse.js": "^7.0.0", + "i18next": "^23.15.1", + "normalizr": "^3.6.2", + "path": "^0.12.7", + "react": "^18.3.1", + "react-barcode": "^1.5.3", + "react-dom": "^18.3.1", + "react-drag-listview": "^2.0.0", + "react-i18next": "^15.0.2", + "react-phone-input-2": "^2.15.1", + "react-redux": "^9.1.2", + "react-resizable": "^3.0.5", + "react-router-dom": "^6.24.1", + "react-use": "^17.5.0", + "recharts": "^2.15.1", + "virtuallist-antd": "^0.8.0-beta.1", + "vite-tsconfig-paths": "^4.3.2" + }, + "devDependencies": { + "@types/google.maps": "^3.58.1", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/react-resizable": "^3.0.4", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "typescript": "^5.2.2", + "vite": "^5.3.1" + } +} diff --git a/public/america.svg b/public/america.svg new file mode 100644 index 0000000..3189d8e --- /dev/null +++ b/public/america.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/background-dark.svg b/public/background-dark.svg new file mode 100644 index 0000000..b61546c --- /dev/null +++ b/public/background-dark.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/background.svg b/public/background.svg new file mode 100644 index 0000000..04d5c74 --- /dev/null +++ b/public/background.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/default.png b/public/default.png new file mode 100644 index 0000000..39a2c13 Binary files /dev/null and b/public/default.png differ diff --git a/public/empty-dish.png b/public/empty-dish.png new file mode 100644 index 0000000..4adcbb9 Binary files /dev/null and b/public/empty-dish.png differ diff --git a/public/me.png b/public/me.png new file mode 100644 index 0000000..53afc00 Binary files /dev/null and b/public/me.png differ diff --git a/public/restaurant-bg.png b/public/restaurant-bg.png new file mode 100644 index 0000000..3e7e17b Binary files /dev/null and b/public/restaurant-bg.png differ diff --git a/public/restaurant-logo.png b/public/restaurant-logo.png new file mode 100644 index 0000000..7844539 Binary files /dev/null and b/public/restaurant-logo.png differ diff --git a/public/saudi-arabia.svg b/public/saudi-arabia.svg new file mode 100644 index 0000000..094930e --- /dev/null +++ b/public/saudi-arabia.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..a3c9343 --- /dev/null +++ b/src/App.css @@ -0,0 +1,349 @@ +/*react transition group css*/ +.page-enter { + opacity: 0; +} + +.page-enter-active { + opacity: 1; +} + +.page-exit { + opacity: 1; +} + +.page-exit-active { + opacity: 0; +} + +.page-enter-active, +.page-exit-active { + transition: opacity 200ms; +} + +.fade-enter-active, +.fade-exit-active { + transition: opacity 300ms; +} + +/*====*/ + +.right-to-left-enter { + transform: translateX(100%); +} + +.right-to-left-enter-active { + transform: translateX(0); + transition: all 300ms ease; +} + +.right-to-left-exit { + transform: translateX(0); +} + +.right-to-left-exit-active { + transform: translateX(-100%); + transition: all 300ms ease; +} + +/*====*/ + +.left-to-right-enter { + transform: translateX(-100%); +} + +.left-to-right-enter-active { + transform: translateX(0); + transition: all 300ms ease; +} + +.left-to-right-exit { + transform: translateX(0); +} + +.left-to-right-exit-active { + transform: translateX(100%); + transition: all 300ms ease; +} + +/*===*/ + +.top-to-bottom-enter { + transform: translateY(-100%); +} + +.top-to-bottom-enter-active { + transform: translateY(0); + transition: all 300ms ease; +} + +.top-to-bottom-exit { + transform: translateY(0); +} + +.top-to-bottom-exit-active { + transform: translateY(100%); + transition: all 300ms ease; +} + +/*===*/ + +.bottom-to-top-enter { + transform: translateY(100%); +} + +.bottom-to-top-enter-active { + transform: translateY(0); + transition: all 300ms ease; +} + +.bottom-to-top-exit { + transform: translateY(0); +} + +.bottom-to-top-exit-active { + transform: translateY(-100%); + transition: all 300ms ease; +} + +/*UTILITIES*/ + +.text-highlight { + background: -webkit-linear-gradient(45deg, #8e44ad, #3498db); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.text-capitalize { + text-transform: capitalize; +} + +.text-uppercase { + text-transform: uppercase; +} + +.text-center { + text-align: center; +} + +.text-end { + text-align: right; +} + +.text-start { + text-align: start; +} + +.text-white { + color: white !important; +} + +.text-black { + color: black !important; +} + +.overflow-scroll { + overflow: auto; +} + +.m-0 { + margin: 0 !important; +} + +.p-0 { + padding: 0 !important; +} + +.w-100 { + width: 100%; +} + +.fw-bold { + font-weight: bold; +} + +.fw-bolder { + font-weight: bolder; +} + +/* Extra large devices (large laptops and desktops, 1200px and up) */ +@media only screen and (min-width: 1200px) { + .overflow-scroll { + overflow: hidden; + } +} + +/* .ant-tabs-tab { + margin-left: 20px !important; + margin-right: 20px !important; + height: 50px; +} */ + +.ant-tabs-nav::before { + display: none !important; +} + +.ant-badge-count { + top: 30px; + right: -8px; + width: 12px; + font-size: 9px; + font-weight: bold; +} + +.ant-tabs-nav .ant-tabs-nav-wrap { + display: block !important; +} + +.topBorder { + background-color: black; + width: 100%; + position: absolute; + top: 0; + left: 0px; + height: 10px; + border-radius: 10px 10px 0px 0px; +} + +.bottomBorder { + background-color: black; + width: 100%; + position: absolute; + bottom: 0; + left: 0px; + height: 10px; + border-radius: 0px 0px 10px 10px; +} + +.columnTitle { + font-weight: 700; + font-size: 14px; + line-height: 16px; + color: #000000; + margin-top: 0px !important; + margin-bottom: 0px !important; + display: inline-block; +} + +.rowText { + font-weight: 400; + font-size: 14px; + line-height: 16px; + color: black; + display: inline-block; + vertical-align: middle; + margin-top: 0px; + margin-bottom: 0px; + max-width: 150px !important; + width: 100px; +} + +.rowTextUnderlined { + font-weight: 400; + font-size: 14px; + line-height: 16px; + text-decoration: underline; + color: black; + display: inline-block; + vertical-align: middle; + margin-top: 0px; + margin-bottom: 0px; +} + +.dotContainer { + margin-top: 5px; +} + +.iconContainer { + display: inline-block; + vertical-align: middle; + margin-top: 5px; + margin-left: 5px; + cursor: pointer; +} + +.columnTitleContainer { + display: inline-block; + vertical-align: middle; + margin-left: 5px; +} + +.title { + width: 100%; + text-align: center; + font-weight: 400; + font-size: 18px; + line-height: 20px; + padding-bottom: 10px; + margin-bottom: 40px; + border-bottom: 2px solid var(--primary-yellow); +} + +.two-main-form-container { + display: "grid"; + grid-template-columns: 1fr 1fr; + column-gap: 10%; +} + +.dashboard-container .ant-table-body { + overflow: scroll; + max-height: 285px; +} + +.dashboard-container .ant-table-body::-webkit-scrollbar { + width: 12px; + height: 10px; +} + +.dashboard-container .ant-table-body::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 10px !important; +} + +.dashboard-container .ant-table-body::-webkit-scrollbar-thumb { + background: #c9c9c9; + border-radius: 10px !important; +} + +.dashboard-container .ant-table-body::-webkit-scrollbar-thumb:hover { + background: #8f8f8f; +} + +.dashboard-container .ant-table-sticky-scroll { + display: none; +} + +.visually-hidden { + position: absolute !important; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); +} + +.barcode-container::-webkit-scrollbar { + width: 12px; + height: 10px; +} + +.barcode-container::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 10px !important; +} + +.barcode-container::-webkit-scrollbar-thumb { + background: #c9c9c9; + border-radius: 10px !important; +} + +.barcode-container::-webkit-scrollbar-thumb:hover { + background: #8f8f8f; +} + +/* Charts Styles */ +.recharts-legend-item-text { + margin: 20px !important; +} + +.recharts-legend-item{ + margin-right: -10px !important; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..fcb85b0 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,52 @@ +import { App as AntdApp, ConfigProvider } from "antd"; +import { ScrollHandlerProvider } from "contexts/ScrollHandlerContext"; +import { getDirection, LocalesMap } from "i18n/helper"; +import { useEffect, useMemo } from "react"; +import { RouterProvider, useLocation } from "react-router-dom"; +import { useAppSelector } from "redux/hooks"; +import { darkThemeConfig, themeConfig } from "ThemeConstants"; +import "./App.css"; +import router from "./routes/routes"; + +export function useQuery() { + const { search } = useLocation(); + + return useMemo(() => new URLSearchParams(search), [search]); +} + +function App() { + const { themeName } = useAppSelector((state) => state.theme); + const { locale } = useAppSelector((state) => state.locale); + + // Set data-theme attribute on html element for CSS access + useEffect(() => { + document.documentElement.setAttribute("data-theme", themeName); + }, [themeName]); + + // Select theme based on current theme state + const currentTheme = themeName === "dark" ? darkThemeConfig : themeConfig; + + return ( + + + +
+ +
+
+
+
+ ); +} + +export default App; diff --git a/src/ThemeConstants.ts b/src/ThemeConstants.ts new file mode 100644 index 0000000..b14ae8f --- /dev/null +++ b/src/ThemeConstants.ts @@ -0,0 +1,517 @@ +// src/appThemeConfig.ts +import type { ThemeConfig } from "antd"; + +export const colors = { + primary: "#FFC600", + secondary: "linear-gradient(135deg, #00C1D4 0%, #FF5F6D 100%)", + darkSpace: "linear-gradient(to right, #0F0525, #2A0B45)", +}; + +export const ProGray1 = "#f7f9fa"; +export const ProGray2 = "#F1F1F1"; +export const ProGray3 = "#edf0f2"; // for modal header background +export const ProGray4 = "#DCDCDC"; // lines color +export const ProGreen = "#03B100"; +export const ProGreen2 = "#52c41a"; +export const ProDraft = "#f36c00"; +export const ProBlack1 = "#181818"; +export const ProBlack2 = "#0a0a0a"; +export const errorColor = "#DC3545"; +export const errorColorDark = "#d9363e"; +export const DisabledColor = "#424242"; + + +export const COLOR = { + 50: "#e0f1ff", + 100: "#b0d2ff", + 200: "#7fb0ff", + 300: "#4d8bff", + 400: "#1e79fe", + 500: "#1890ff", + 600: "#09237D", + 700: "#004f81", + 800: "#003650", + 900: "#001620", + borderColor: "#E7EAF3B2", + primaryColor: "#FFC600", + primaryColor2: "#09237D", + whiteColor: "#FFF", + blackColor: "#000000e0", + sidebarBg: "#FFFFFF", + tableBg: "#FFFFFF", + cardBg: "#FFFFFF", + headerBg: "#FFFFFF", + hoverBg: "#F1F5F9", + activeBg: "#E2E8F0", +}; + +// Enhanced Dark theme colors with better contrast and modern aesthetics +export const DARK_COLOR = { + // Background colors - from darkest to lightest + 50: "#0a0a0a", // Deepest background + 100: "#141414", // Main background + 200: "#1e1e1e", // Elevated background + 300: "#181818", // Card background + 400: "#363636", // Hover background + 500: "#424242", // Active background + 600: "#525252", // Border color + 700: "#6b6b6b", // Secondary text + 800: "#8a8a8a", // Primary text + 900: "#b0b0b0", // Highlight text + + // Semantic colors + borderColor: "#363636", + borderColorLight: "#424242", + primaryColor: "#FFC600", + primaryColorHover: "#FFD633", + primaryColorActive: "#E6B800", + primaryColor2: "#FFD633", + + // Background colors + whiteColor: "#0a0a0a", // Main background + blackColor: "#ffffff", // Primary text + sidebarBg: "#141414", + tableBg: "#1e1e1e", + headerBg: "#141414", + hoverBg: "#363636", + activeBg: "#424242", + + // Additional semantic colors + success: "#10b981", + warning: "#f59e0b", + error: "#ef4444", + info: "#3b82f6", + + // Text colors + textPrimary: "#ffffff", + textSecondary: "#b0b0b0", + textTertiary: "#8a8a8a", + textQuaternary: "#6b6b6b", + textDisabled: "#525252", + + // Overlay colors + overlayBg: "rgba(0, 0, 0, 0.45)", + maskBg: "rgba(0, 0, 0, 0.65)", + + // Shadow colors + shadowLight: "0 1px 2px 0 rgba(0, 0, 0, 0.3)", + shadowMedium: "0 4px 6px -1px rgba(0, 0, 0, 0.4)", + shadowHeavy: "0 10px 15px -3px rgba(0, 0, 0, 0.5)", +}; + +export const componentBackground = "#f7f7f7"; +export const darkComponentBackground = "#181818"; + +export const SharedThemeVars = { + borderRadius: 8, + borderRadiusLG: 8, + fontWeightStrong: 500, + colorPrimary: colors.primary, + fontFamily: "var(--font-roboto)", + colorLink: colors.primary, + // colorItemTextSelected: colors.primary // Menu Item Active Item Color +}; + +export const LightThemeVars = { + ...SharedThemeVars, + white: componentBackground, + colorBgElevated: componentBackground, + colorBgLayout: componentBackground, + + // ------ Pro colors + proSiderColor: "#1E5083", + proHeaderColor: colors.primary, +}; + +export const DarkThemeVars = { + ...SharedThemeVars, + white: darkComponentBackground, + colorBgElevated: DARK_COLOR[200], + colorBgContainer: DARK_COLOR[300], + colorBgLayout: darkComponentBackground, + colorText: DARK_COLOR.textPrimary, + colorTextSecondary: DARK_COLOR.textSecondary, + colorTextTertiary: DARK_COLOR.textTertiary, + colorTextQuaternary: DARK_COLOR.textQuaternary, + colorBorder: DARK_COLOR.borderColor, + colorBorderSecondary: DARK_COLOR.borderColorLight, + + // ------ Pro colors + proSiderColor: DARK_COLOR[200], + proHeaderColor: colors.primary, +}; + +// AntD Theme Configuration +export const themeConfig: ThemeConfig = { + token: { + colorPrimary: colors.primary, // Cosmic purple + colorLink: "#9d50bb", // Nebula pink + fontFamily: "var(--font-roboto)", + borderRadius: 8, + // Futuristic button styles + controlHeight: 40, + colorTextLabel: COLOR.blackColor, + colorBgLayout: componentBackground, + }, + components: { + Breadcrumb: { + linkColor: "rgba(0,0,0,.8)", + itemColor: "rgba(0,0,0,.8)", + }, + Button: { + colorLink: COLOR["600"], + colorLinkActive: COLOR["700"], + colorLinkHover: COLOR["300"], + borderRadius: 1000, + borderRadiusLG: 1000, + // defaultShadow: "none", + }, + Calendar: { + colorBgContainer: "none", + }, + Card: { + colorBgContainer: COLOR.cardBg, + colorBorderSecondary: COLOR["borderColor"], + boxShadow: "0 1px 3px rgba(0,0,0,0.1)", + padding: 16, + paddingLG: 16, + borderRadius: 24, + borderRadiusLG: 24, + }, + Carousel: { + colorBgContainer: COLOR["800"], + dotWidth: 8, + }, + Rate: { + colorFillContent: COLOR["100"], + colorText: COLOR["600"], + }, + Segmented: { + colorBgLayout: COLOR["100"], + borderRadius: 6, + colorTextLabel: "#000000", + }, + Table: { + borderColor: COLOR["100"], + headerBg: COLOR.primaryColor, + headerColor: COLOR.whiteColor, + rowSelectedBg: COLOR.primaryColor, + cellPaddingBlock: 8, + colorBgContainer: COLOR.tableBg, + colorFillAlter: COLOR.hoverBg, + }, + Tabs: { + colorBorderSecondary: COLOR["100"], + }, + Timeline: { + dotBg: "none", + }, + Typography: { + colorLink: COLOR["600"], + colorLinkActive: COLOR["700"], + colorLinkHover: COLOR["300"], + linkHoverDecoration: "underline", + }, + Drawer: { + wireframe: true, + colorSplit: ProGray4, + lineWidth: 3, + controlHeightLG: 30, + }, + Menu: { + colorItemBg: COLOR.sidebarBg, + colorItemText: COLOR["800"], + colorItemTextSelected: COLOR.primaryColor2, + colorItemBgSelected: COLOR.activeBg, + colorItemBgHover: COLOR.hoverBg, + }, + Layout: { + headerBg: COLOR.headerBg, + bodyBg: componentBackground, + }, + Input: { + borderRadiusLG: 1000, + + fontSize: 12, + fontSizeLG: 12, + }, + InputNumber: { + borderRadiusLG: 1000, + fontSize: 12, + fontSizeLG: 12, + }, + DatePicker: { + borderRadiusOuter: 1000, + borderRadius: 1000, + }, + Select: { + borderRadius: 1000, + multipleItemHeightLG: 32, + }, + }, +}; + +// Enhanced Dark theme configuration +export const darkThemeConfig: ThemeConfig = { + token: { + // Primary colors + colorPrimary: DARK_COLOR.primaryColor, + colorPrimaryHover: DARK_COLOR.primaryColorHover, + colorPrimaryActive: DARK_COLOR.primaryColorActive, + colorLink: DARK_COLOR.primaryColor, + colorLinkHover: DARK_COLOR.primaryColorHover, + colorLinkActive: DARK_COLOR.primaryColorActive, + + // Typography + fontFamily: "var(--font-roboto)", + fontSize: 14, + fontSizeLG: 16, + fontSizeSM: 12, + fontSizeXL: 20, + + // Spacing and sizing + borderRadius: 8, + borderRadiusLG: 8, + borderRadiusSM: 6, + controlHeight: 40, + controlHeightLG: 48, + controlHeightSM: 32, + + // Background colors + colorBgContainer: DARK_COLOR[300], + colorBgElevated: DARK_COLOR[200], + colorBgLayout: darkComponentBackground, + colorBgSpotlight: DARK_COLOR[400], + colorBgMask: DARK_COLOR.maskBg, + + // Text colors + colorText: DARK_COLOR.textPrimary, + colorTextSecondary: DARK_COLOR.textSecondary, + colorTextTertiary: DARK_COLOR.textTertiary, + colorTextQuaternary: DARK_COLOR.textQuaternary, + colorTextPlaceholder: DARK_COLOR.textDisabled, + colorTextDisabled: DARK_COLOR.textDisabled, + colorTextLabel: DARK_COLOR.textSecondary, + colorTextDescription: DARK_COLOR.textTertiary, + + // Border colors + colorBorder: DARK_COLOR.borderColor, + colorBorderSecondary: DARK_COLOR.borderColorLight, + + // Fill colors + colorFill: DARK_COLOR[400], + colorFillSecondary: DARK_COLOR[500], + colorFillTertiary: DARK_COLOR[600], + colorFillQuaternary: DARK_COLOR[700], + + // Success, warning, error colors + colorSuccess: DARK_COLOR.success, + colorWarning: DARK_COLOR.warning, + colorError: DARK_COLOR.error, + colorInfo: DARK_COLOR.info, + + // Shadow + boxShadow: DARK_COLOR.shadowLight, + boxShadowSecondary: DARK_COLOR.shadowMedium, + }, + components: { + // Layout components + Layout: { + headerBg: DARK_COLOR.headerBg, + siderBg: DARK_COLOR.sidebarBg, + bodyBg: darkComponentBackground, + footerBg: DARK_COLOR.headerBg, + }, + + // Card component + Card: { + colorBgContainer: ProBlack2, + colorBorderSecondary: DARK_COLOR.borderColor, + boxShadow: DARK_COLOR.shadowLight, + boxShadowTertiary: DARK_COLOR.shadowMedium, + padding: 16, + paddingLG: 20, + paddingSM: 12, + borderRadius: 24, + borderRadiusLG: 24, + }, + + // Button component + Button: { + colorPrimary: DARK_COLOR.primaryColor, + colorPrimaryHover: DARK_COLOR.primaryColorHover, + colorPrimaryActive: DARK_COLOR.primaryColorActive, + colorBgContainer: DARK_COLOR[300], + colorBorder: DARK_COLOR.borderColor, + borderRadius: 8, + borderRadiusLG: 8, + borderRadiusSM: 6, + controlHeight: 40, + controlHeightLG: 48, + controlHeightSM: 32, + defaultShadow: "none", + }, + + // Input components + Input: { + colorBgContainer: DARK_COLOR[300], + colorBorder: DARK_COLOR.borderColor, + colorText: DARK_COLOR.textPrimary, + colorTextPlaceholder: DARK_COLOR.textDisabled, + borderRadiusLG: 1000, + fontSize: 12, + fontSizeLG: 12, + }, + + InputNumber: { + colorBgContainer: DARK_COLOR[300], + colorBorder: DARK_COLOR.borderColor, + colorText: DARK_COLOR.textPrimary, + borderRadiusLG: 1000, + fontSize: 12, + fontSizeLG: 12, + }, + + // Select component + Select: { + colorBgContainer: DARK_COLOR[300], + colorBgElevated: DARK_COLOR[200], + colorBorder: DARK_COLOR.borderColor, + colorText: DARK_COLOR.textPrimary, + colorTextPlaceholder: DARK_COLOR.textDisabled, + borderRadius: 1000, + }, + + // Menu component + Menu: { + colorItemBg: DARK_COLOR.sidebarBg, + colorItemText: DARK_COLOR.textPrimary, + colorItemTextSelected: DARK_COLOR.primaryColor, + colorItemBgSelected: DARK_COLOR.activeBg, + colorItemBgHover: DARK_COLOR.hoverBg, + colorSubItemBg: DARK_COLOR[200], + colorActiveBarWidth: 3, + colorActiveBarBorderSize: 0, + }, + + // Table component + Table: { + colorBgContainer: DARK_COLOR.tableBg, + colorFillAlter: DARK_COLOR[200], + colorFillContent: DARK_COLOR[300], + colorFillSecondary: DARK_COLOR[400], + headerBg: DARK_COLOR[400], + headerColor: DARK_COLOR.textPrimary, + rowSelectedBg: DARK_COLOR.activeBg, + rowHoverBg: DARK_COLOR.hoverBg, + borderColor: DARK_COLOR.borderColor, + headerSplitColor: DARK_COLOR.borderColor, + }, + + // Drawer component + Drawer: { + colorBgElevated: DARK_COLOR[200], + colorBgMask: DARK_COLOR.maskBg, + colorBorder: DARK_COLOR.borderColor, + colorSplit: DARK_COLOR.borderColor, + }, + + // Modal component + Modal: { + colorBgElevated: DARK_COLOR[200], + colorBgMask: DARK_COLOR.maskBg, + colorIcon: DARK_COLOR.textSecondary, + colorIconHover: DARK_COLOR.textPrimary, + }, + + // Tabs component + Tabs: { + colorBgContainer: DARK_COLOR[300], + colorBorderSecondary: DARK_COLOR.borderColor, + colorText: DARK_COLOR.textPrimary, + colorTextDescription: DARK_COLOR.textSecondary, + colorPrimary: DARK_COLOR.primaryColor, + colorPrimaryHover: DARK_COLOR.primaryColorHover, + colorPrimaryActive: DARK_COLOR.primaryColorActive, + }, + + // Typography component + Typography: { + colorText: DARK_COLOR.textPrimary, + colorTextSecondary: DARK_COLOR.textSecondary, + colorTextDescription: DARK_COLOR.textTertiary, + colorLink: DARK_COLOR.primaryColor, + colorLinkHover: DARK_COLOR.primaryColorHover, + colorLinkActive: DARK_COLOR.primaryColorActive, + }, + + // Breadcrumb component + Breadcrumb: { + colorText: DARK_COLOR.textSecondary, + colorTextDescription: DARK_COLOR.textTertiary, + linkColor: DARK_COLOR.textSecondary, + lastItemColor: DARK_COLOR.textPrimary, + }, + + // Divider component + Divider: { + colorSplit: DARK_COLOR.borderColor, + }, + + // Rate component + Rate: { + colorFillContent: DARK_COLOR[400], + colorText: DARK_COLOR.primaryColor, + }, + + // Segmented component + Segmented: { + colorBgLayout: DARK_COLOR[200], + colorBgElevated: DARK_COLOR[300], + colorText: DARK_COLOR.textPrimary, + colorTextLabel: DARK_COLOR.textPrimary, + borderRadius: 8, + }, + + // Calendar component + Calendar: { + colorBgContainer: DARK_COLOR[300], + colorBgElevated: DARK_COLOR[200], + colorText: DARK_COLOR.textPrimary, + colorTextSecondary: DARK_COLOR.textSecondary, + colorBorder: DARK_COLOR.borderColor, + }, + + // Carousel component + Carousel: { + colorBgContainer: DARK_COLOR[400], + dotWidth: 8, + dotHeight: 8, + dotActiveWidth: 24, + }, + + // Timeline component + Timeline: { + colorText: DARK_COLOR.textPrimary, + colorTextSecondary: DARK_COLOR.textSecondary, + colorSplit: DARK_COLOR.borderColor, + }, + + // DatePicker component + DatePicker: { + colorBgContainer: DARK_COLOR[300], + colorBgElevated: DARK_COLOR[200], + colorBorder: DARK_COLOR.borderColor, + colorText: DARK_COLOR.textPrimary, + colorTextPlaceholder: DARK_COLOR.textDisabled, + borderRadius: 1000, + }, + }, +}; + +// CSS-in-JS Utilities (For global use) +export const futuristicStyles = { + textGradient: `background: ${colors.secondary}; -webkit-background-clip: text; -webkit-text-fill-color: transparent;`, + glassEffect: `background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1);`, + darkGlassEffect: `background: rgba(0, 0, 0, 0.3); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1);`, +}; diff --git a/src/assets/locals/ar.json b/src/assets/locals/ar.json new file mode 100644 index 0000000..989dfd2 --- /dev/null +++ b/src/assets/locals/ar.json @@ -0,0 +1,327 @@ +{ + "orders": { + "title": "الطلبات", + "browseMenu": "تصفح القائمة", + "emptyOrders": "ليس لديك أي طلب", + "emptyOrdersDescription": "سيتم سرد جميع طلباتك هنا", + "rateOrder": "تقييم الطلب", + "reOrder": "إعادة الطلب", + "orderID": "رقم الطلب", + "errorLoadingOrders": "خطأ في تحميل الطلبات", + "retry": "إعادة المحاولة", + "noOrdersFound": "لا يوجد طلبات", + "youHavenTPlacedAnyOrdersYet": "لم تضع أي طلبات بعد. تصفح قائمتنا للعثور على وجبات لذيذة!" + }, + "common": { + "home": "الرئيسية", + "menu": "المنتجات", + "cart": "السلة", + "checkout": "الدفع", + "login": "تسجيل الدخول", + "contact": "اتصل بنا", + "admin": "لوحة الإدارة", + "search": "البحث عن المنتجات...", + "addToCart": "أضف إلى السلة", + "addedToCart": "تم إضافة العنصر إلى السلة", + "viewDetails": "عرض التفاصيل", + "price": "السعر", + "category": "الفئة", + "inStock": "متوفر", + "outOfStock": "غير متوفر", + "total": "المجموع", + "quantity": "الكمية", + "remove": "إزالة", + "proceedToCheckout": "متابعة إلى الدفع", + "emptyCart": "السلة فارغة", + "continueShopping": "متابعة التسوق", + "myOrder": "طلباتي", + "branches": "الفروع", + "settings": "الإعدادات", + "light": "نهاري", + "dark": "ليلي", + "arabic": "العربية", + "english": "الإنجليزية", + "add": "إضافة", + "dineIn": "تناول", + "pickup": "استلام", + "sendGift": "إرسال هدية", + "roomService": "خدمة الغرف", + "officeDelivery": "توصيل للمكتب", + "tableBooking": "حجز طاولة", + "showDetails": "عرض التفاصيل", + "language": "اللغة", + "more": "المزيد", + "offInYourListing": "% خصم في قائمتك", + "about_this_dish": "عن هذه الوجبة", + "pickYourBirthday": "اختر تاريخ ميلادك", + "confirm": "تأكيد", + "January": "يناير", + "February": "فبراير", + "March": "مارس", + "April": "أبريل", + "May": "مايو", + "June": "يونيو", + "July": "يوليو", + "August": "أغسطس", + "September": "سبتمبر", + "October": "أكتوبر", + "November": "نوفمبر", + "December": "ديسمبر", + "omanCurrency": "ر.ع.", + "noResultsFound": "لم يتم العثور على نتائج", + "delivery": "التوصيل", + "room": "الغرفة", + "office": "المكتب", + "booking": "الحجز" + }, + "home": { + "title": "العنوان", + "showDetails": "عرض التفاصيل", + "services": { + "dineIn": "استمتع بوجبتك في مطعمنا.", + "pickup": "استلم طلبك في الوقت المناسب لك.", + "gift": "أرسل وجبة كهدية لشخص مميز.", + "room": "خدمة الغرف لراحتك.", + "office": "توصيل إلى مكتبك.", + "booking": "احجز طاولة مسبقاً." + }, + "promotion": { + "title": "الترويجات", + "description": "احصل على خصم 10% على طلبك الأول" + } + }, + "contact": { + "title": "اتصل بنا", + "subtitle": "تواصل مع المتخصصين الطبيين لدينا", + "name": "الاسم الكامل", + "email": "البريد الإلكتروني", + "phone": "رقم الهاتف", + "message": "الرسالة", + "send": "إرسال الرسالة", + "address": "العنوان", + "phone_label": "الهاتف", + "email_label": "البريد الإلكتروني" + }, + "menu": { + "meal": "الوجبة", + "title": "القائمة", + "ourMenu": "قائمتنا", + "addToCart": "أضف إلى السلة", + "updateCart": "تحديث السلة", + "currentlyInCart": "في السلة حالياً: {{quantity}}", + "description": "الوصف", + "ingredients": "المكونات", + "nutritionalInfo": "المعلومات الغذائية", + "calories": "السعرات الحرارية", + "protein": "البروتين", + "carbs": "الكربوهيدرات", + "fat": "الدهون", + "viewCart": "عرض السلة", + "rating": "التقييم ", + "loyaltyPoints": "نقاط الولاء", + "loyaltyDescription": "اشترى 5 وجبات واحصل على وجبة مجانية", + "youMightAlsoLike": "قد تعجبك أيضاً..", + "choose1": "اختر 1", + "specialRequest": "طلب خاص", + "categories": { + "all": "الكل", + "burgers": "البرغر", + "shawarmas": "الشاورما", + "sides": "الأطباق الثانوية", + "desserts": "الحلويات", + "drinks": "المشروبات", + "popular": "الأكثر مبيعاً" + }, + "claim": "إدخال", + "required": "مطلوب", + "optional": "اختياري", + "pleaseSelectRequiredOptions": "يرجى اختيار جميع الخيارات المطلوبة قبل إضافة إلى السلة", + "selectRequiredOptions": "اختر الخيارات المطلوبة", + "pleaseCustomizeProduct": "يرجى تخصيص هذا المنتج بالنقر عليه لاختيار الخيارات", + "search": "البحث", + "searchPlaceholder": "البحث عن المنتجات...", + "noResultsFound": "لم يتم العثور على نتائج", + "cart": "السلة", + "delivery": "التوصيل", + "noMenuItemsAvailable": "لا توجد عناصر قائمة متاحة", + "restaurantCover": "غلاف المطعم", + "restaurantLogo": "شعار المطعم" + }, + "cart": { + "title": "السلة", + "emptyCart": "سلة المشتريات فارغة", + "emptyCartMessage": "يبدو أنك لم تضيف أي عناصر إلى سلة المشتريات بعد. ابدأ في استكشاف قائمتنا للعثور على وجبات لذيذة!", + "browseMenu": "تصفح القائمة", + "paymentSummary": "ملخص الدفعة", + "subtotal": "المجموع الفرعي", + "tax": "الضريبة (10%)", + "remove": "حذف", + "proceedToCheckout": "المتابعة إلى الدفع", + "totalAmount": "المجموع الكلي", + "basketTotal": "المجموع الفرعي", + "discount": "الخصم", + "riderTip": "نصيب الرادير", + "addToCart": "أضف إلى السلة", + "deleteConfirmation": { + "title": "حذف العنصر", + "content": "هل أنت متأكد أنك تريد حذف من سلة المشتريات؟", + "confirm": "حذف", + "cancel": "إلغاء", + "success": "تم حذف العنصر من سلة المشتريات" + }, + "quantity": "الكمية", + "price": "السعر", + "perItem": "للقطعة", + "couponCode": "رمز الخصم", + "couponCodePlaceholder": "أدخل رمز الخصم", + "specialRequest": "طلب خاص", + "specialRequestPlaceholder": "أي تعليمات خاصة لطلبك؟", + "tableNumber": "رقم الطاولة", + "tableNumberPlaceholder": "اختر طاولتك", + "coupon": "القسيمة", + "apply": "تطبيق", + "viewOffers": "عرض العروض", + "amount": "المبلغ", + "addTip": "أضف نصيب الرادير", + "tip": "نصيب الرادير", + "rewardYourWaiter": "أعط نصيب الرادير", + "rewardYourWaiter100": "100% من نصيب الرادير", + "addItem": "أضف عنصر", + "checkout": "الدفع", + "other": "أخرى", + "save": "حفظ", + "edit": "تعديل", + "cancel": "إلغاء", + "selectTable": "اختيار رقم الطاولة", + "youMightAlsoLike": "قد تعجبك أيضاً..", + "recommendationsMessage": "اكتشف المزيد من الخيارات اللذيذة لإضافتها إلى طلبك", + "orderSummary": "ملخص الطلب", + "tables": "الطاولات", + "cartItems": "عناصر السلة", + "item": "عنصر", + "items": "عناصر", + "yourOrder": "طلبك", + "addMore": "أضف أكثر", + "selectLocation": "اختر الموقع", + "locationSelected": "تم اختيار الموقع", + "selectedLocation": "الموقع المختار", + "mapFallbackText": "لا يمكن تحميل الخريطة؟ أدخل العنوان يدوياً", + "selectDay": "اختر اليوم", + "selectTime": "اختر الوقت", + "clickToMoveToNextDay": "انقر للانتقال إلى اليوم التالي", + "confirmTime": "تأكيد الوقت", + "estimateTime": "وقت التقديم", + "now": "الآن", + "later": "لاحقاً", + "collectionMethod": "طريقة الاستلام", + "Cash": "كاش", + "e-payment": "الدفع عبر الإنترنت", + "plateNumber": "رقم السيارة", + "plateNumberPlaceholder": "أدخل رقم السيارة", + "inYourCart": "في سلة المشتريات", + "updatedSuccessfully": "تم التحديث بنجاح", + "editNote": "تعديل الملاحظة" + }, + "checkout": { + "title": "الدفع", + "creditDebitCard": "بطاقة ائتمان/ائتمان", + "differentCard": "بطاقة اخرى", + "fascanoWallet": "محفظة فاسكانو", + "selectedPaymentMethod": "اختر طريقة الدفع", + "splitBill": "تقسيم الفاتورة", + "table": "الطاولة", + "totalPeople": "عدد الأشخاص", + "payFor": "الدفع ل", + "placeOrder": "وضع الطلب", + "remainingAmount": "المبلغ المتبقي", + "totalAmount": "المبلغ الكلي", + "items": "العناصر" + }, + "address": { + "title": "العنوان", + "saveAddress": "حفظ العنوان", + "locationDetails": "تفاصيل الموقع", + "noLocationSelected": "لم يتم اختيار موقع", + "clickEditToSelect": "انقر على تعديل لاختيار موقع على الخريطة", + "selectedAddress": "العنوان المختار:", + "changeLocation": "تغيير الموقع", + "selectLocation": "اختر الموقع", + "room": "الغرفة", + "office": "المكتب", + "booking": "الحجز", + "delivery": "التوصيل", + "addressDetails": "تفاصيل العنوان", + "addressLabel": "العنوان المراد حفظه", + "additionalDirection": "الاتجاه الإضافي", + "aptNumber": "رقم الشقة", + "floor": "الطابق", + "street": "الشارع", + "apartment": "الشقة", + "house": "المنزل", + "mobileNumber": "رقم الهاتف", + "roomDetails": "تفاصيل الغرفة", + "changeRoom": "تغيير الغرفة", + "selectRoom": "اختر الغرفة", + "roomNo": "رقم الغرفة", + "floorNo": "رقم الطابق", + "note": "خدمة الغرف", + "guestName": "اسم الضيف", + "deliverToRoom": "توصيل إلى الغرفة", + "pleaseKnockSoftly": "يرجى إصدار ضربة خفيفة", + "officeDetails": "تفاصيل المكتب", + "changeOffice": "تغيير المكتب", + "selectOffice": "اختر المكتب", + "officeNo": "رقم المكتب", + "company": "الشركة", + "contactPerson": "الاتصال بالشخص", + "phone": "الهاتف", + "giftDetails": "تفاصيل الهدية", + "changeGift": "تغيير الهدية", + "selectGift": "اختر الهدية", + "receiverName": "اسم المستلم", + "receiverPhone": "رقم هاتف المستلم", + "keepMyNameSecret": "الاحتفاظ باسمي مخفياً", + "howItWorks": "كيف يعمل", + "senderName": "اسم المرسل", + "senderPhone": "رقم هاتف المرسل", + "gotIt": "فهمت", + "howItWorksDescription": "يمكنك إرسال هدية إلى أي شخص عبر التطبيق. يمكنك إرسال هدية إلى أي شخص عبر التطبيق. يمكنك إرسال هدية إلى أي شخص عبر التطبيق. يمكنك إرسال هدية إلى أي شخص عبر التطبيق. يمكنك إرسال هدية إلى أي شخص عبر التطبيق." + }, + "login": { + "singup/Login": "الدخول / التسجيل", + "EnterYourNumber": "أدخل رقمك", + "WeWillSendYouAWhatsAppMessageWithAOneTimeVerificationCode": "سنرسل لك رسالة عبر واتساب مع رمز التحقق", + "EnterYourName": "أدخل اسمك", + "DateOfBirth": "تاريخ الميلاد", + "mobileNumber": "رقم الهاتف", + "signIn": "تسجيل الدخول", + "sendCode": "إرسال الرمز", + "OTPSentToYourPhoneNumber": "تم إرسال رمز التحقق إلى رقم هاتفك" + }, + "otp": { + "title": "التحقق", + "verification": "التحقق", + "enterThe4DigitCodeThatSentToYourPhoneNumber": "أدخل رمز التحقق الذي تم إرساله إلى رقم هاتفك", + "resendOtp": "إعادة الإرسال", + "otpSent": "تم إرسال رمز التحقق إلى رقم هاتفك", + "otpExpired": "رمز التحقق لم ينتهي صلاحيته", + "otpExpiredMessage": "يرجى إعادة الإرسال", + "continue": "متابعة", + "resendAvailableIn": "يمكن إعادة الإرسال بعد ثانية", + "confirmOTPSuccess": "تم التحقق من رمز التحقق بنجاح" + }, + "order": { + "title": "الطلب", + "yourOrderFromFascanoRestaurant": "طلبك من مطعم فاسكانو", + "muscat": "مسقط", + "reserved": "محجوز", + "prepare": "جاري التحضير", + "ready": "جاهز", + "inProgressOrder": "الطلبات المستمرة", + "yourOrderFrom": "طلبك من", + "cancelOrder": "إلغاء الطلب", + "areYouSureYouWantToCancelThisOrder?": "هل أنت متأكد أنك تريد إلغاء هذا الطلب؟", + "keepOrder": "الاحتفاظ بالطلب", + "thisActionCannotBeUndone": "هذا الإجراء لا يمكن التراجع عنه." + } +} diff --git a/src/assets/locals/en.json b/src/assets/locals/en.json new file mode 100644 index 0000000..04a3fb9 --- /dev/null +++ b/src/assets/locals/en.json @@ -0,0 +1,337 @@ +{ + "orders": { + "title": "Orders", + "browseMenu": "Browse Menu", + "emptyOrders": "You don’t have any order", + "emptyOrdersDescription": "All your orders will be listed here", + "rateOrder": "Rate Order", + "reOrder": "Re-Order", + "orderID": "Order ID", + "errorLoadingOrders": "Error Loading Orders", + "retry": "Retry", + "noOrdersFound": "No Orders Found", + "youHavenTPlacedAnyOrdersYet": "You haven't placed any orders yet. Browse our menu to get started!" + }, + "common": { + "home": "Home", + "menu": "Products", + "cart": "Cart", + "checkout": "Checkout", + "login": "Login", + "contact": "Contact Us", + "admin": "Admin Panel", + "search": "Search products...", + "addToCart": "Add to Cart", + "addedToCart": "Added to Cart", + "viewDetails": "View Details", + "price": "Price", + "category": "Category", + "inStock": "In Stock", + "outOfStock": "Out of Stock", + "total": "Total", + "quantity": "Quantity", + "remove": "Remove", + "proceedToCheckout": "Proceed to Checkout", + "emptyCart": "Your cart is empty", + "continueShopping": "Continue Shopping", + "myOrder": "My Order", + "branches": "Branches", + "settings": "Settings", + "light": "Light", + "dark": "Dark", + "arabic": "Arabic", + "english": "English", + "add": "Add", + "dineIn": "Dine-in", + "pickup": "Pickup", + "sendGift": "Send a Gift", + "roomService": "Room Service", + "officeDelivery": "Office Delivery", + "tableBooking": "Table Booking", + "showDetails": "Show details", + "language": "Language", + "offInYourListing": "% off in your listing", + "more": "More", + "about_this_dish": "About this dish", + "pickYourBirthday": "Pick your birthday", + "confirm": "Confirm", + "January": "January", + "February": "February", + "March": "March", + "April": "April", + "May": "May", + "June": "June", + "July": "July", + "August": "August", + "September": "September", + "October": "October", + "November": "November", + "December": "December", + "omanCurrency": "OMR", + "noResultsFound": "No results found", + "delivery": "Delivery", + "noMenuItemsAvailable": "No menu items available", + "restaurantCover": "Restaurant Cover", + "restaurantLogo": "Restaurant Logo" + }, + "home": { + "title": "title", + "showDetails": "Show details", + "hero": { + "title": "Fascano Medical Technologies", + "subtitle": "Pioneering Breast Aesthetics & Reconstruction Solutions", + "description": "Enhancing women's health through innovative breast implant technologies and comprehensive medical solutions.", + "cta": "Explore Products" + }, + "features": { + "title": "Why Choose Fascano", + "innovation": "Cutting-edge Innovation", + "innovationDesc": "Advanced medical technologies for optimal patient outcomes", + "safety": "Proven Safety", + "safetyDesc": "Rigorous testing and quality assurance for patient safety", + "support": "Professional Support", + "supportDesc": "24/7 medical professional support and consultation" + }, + "services": { + "dineIn": "Enjoy your meal in our restaurant.", + "pickup": "Pick up your order at your convenience.", + "gift": "Send a meal as a gift to someone special.", + "room": "Room service for your comfort.", + "office": "Delivery to your office.", + "booking": "Book a table in advance.", + "delivery": "Delivery" + }, + "promotion": { + "title": "Promotions", + "description": "Get 10% off your first order" + } + }, + "contact": { + "title": "Contact Us", + "subtitle": "Get in touch with our medical professionals", + "name": "Full Name", + "email": "Email Address", + "phone": "Phone Number", + "message": "Message", + "send": "Send Message", + "address": "Address", + "phone_label": "Phone", + "email_label": "Email" + }, + "menu": { + "meal": "Meal", + "title": "Menu", + "ourMenu": "Our Menu", + "addToCart": "Add to Cart", + "updateCart": "Update Cart", + "currentlyInCart": "Currently in cart: {{quantity}}", + "description": "Description", + "ingredients": "Ingredients", + "nutritionalInfo": "Nutritional Information", + "calories": "Calories", + "protein": "Protein", + "carbs": "Carbs", + "fat": "Fat", + "viewCart": "View Cart", + "rating": "Rating ", + "loyaltyPoints": "Loyalty Points", + "loyaltyDescription": "Buy 5 meals and get 1 FREE", + "choose1": "Choose 1", + "youMightAlsoLike": "You might also like..", + "specialRequest": "Special Request", + "categories": { + "all": "All", + "burgers": "Burgers", + "shawarmas": "Shawarmas", + "sides": "Sides", + "desserts": "Desserts", + "drinks": "Drinks", + "popular": "Popular" + }, + "claim": "Claim", + "required": "Required", + "optional": "Optional", + "pleaseSelectRequiredOptions": "Please select all required options before adding to cart", + "selectRequiredOptions": "Select Required Options", + "pleaseCustomizeProduct": "Please customize this product by clicking on it to select options", + "search": "Search", + "searchPlaceholder": "Search for products...", + "noResultsFound": "No results found", + "cart": "Cart" + }, + "cart": { + "title": "Cart", + "emptyCart": "Cart is empty", + "emptyCartMessage": "Looks like you haven't added any items to your cart yet. Start exploring our menu to find delicious meals!", + "browseMenu": "Browse Menu", + "paymentSummary": "Payment Summary", + "subtotal": "Subtotal", + "tax": "Tax (10%)", + "remove": "Remove", + "proceedToCheckout": "Proceed to Checkout", + "basketTotal": "Basket Total", + "discount": "Discount", + "riderTip": "Rider Tip", + "addToCart": "Add to Cart", + "deleteConfirmation": { + "title": "Remove Item", + "content": "Are you sure you want to remove from your cart?", + "confirm": "Remove", + "cancel": "Cancel", + "success": ",Item removed from cart" + }, + "quantity": "Quantity", + "price": "Price", + "perItem": "Per Item", + "totalAmount": "Total Amount", + "couponCode": "Coupon Code", + "couponCodePlaceholder": "Enter coupon code", + "specialRequest": "Special Request", + "specialRequestPlaceholder": "Any special instructions for your order?", + "tableNumber": "Table Number", + "tableNumberPlaceholder": "Select your table", + "coupon": "Coupon", + "apply": "Apply", + "viewOffers": "View Offers", + "amount": "Amount", + "addTip": "Add Tip", + "tip": "Tip", + "rewardYourWaiter": "Reward your waiter with a tip", + "rewardYourWaiter100": "Your waiter keeps 100% of the tip", + "addItem": "Add Item", + "checkout": "Checkout", + "other": "Other", + "selectTable": "Select Table", + "tables": "Tables", + "orderSummary": "Order Summary", + "save": "Save", + "edit": "Edit", + "cancel": "Cancel", + "youMightAlsoLike": "You might also like..", + "recommendationsMessage": "Discover more delicious options to add to your order", + "cartItems": "Cart Items", + "item": "item", + "items": "items", + "yourOrder": "Your Order", + "addMore": "Add More", + "selectLocation": "Select Location", + "locationSelected": "Location Selected", + "selectedLocation": "Selected Location", + "mapFallbackText": "Can't load map? Enter address manually", + "selectDay": "Select Day", + "selectTime": "Select Time", + "clickToMoveToNextDay": "Click to move to next day", + "confirmTime": "Confirm Time", + "estimateTime": "Estimate Time", + "now": "Now", + "later": "Later", + "collectionMethod": "Collection Method", + "Cash": "Cash", + "e-payment": "e-payment", + "plateNumber": "Plate Number", + "plateNumberPlaceholder": "Enter plate number" + }, + "checkout": { + "title": "Checkout", + "creditDebitCard": "Credit/Debit Card", + "differentCard": "Different Card", + "fascanoWallet": "Fascano Wallet", + "selectedPaymentMethod": "Selected Payment Method", + "splitBill": "Split Bill", + "table": "Table", + "totalPeople": "Total People", + "payFor": "Pay For", + "placeOrder": "Place Order", + "remainingAmount": "Remaining Amount", + "totalAmount": "Total Amount", + "items": "Items" + }, + "address": { + "title": "Address", + "saveAddress": "Save Address", + "locationDetails": "Location Details", + "noLocationSelected": "No location selected", + "clickEditToSelect": "Click Edit to select a location on the map", + "selectedAddress": "Selected Address:", + "changeLocation": "Change Location", + "selectLocation": "Select Location", + "room": "Room", + "office": "Office", + "booking": "Booking", + "delivery": "Delivery", + "addressDetails": "Address Details", + "addressLabel": "Address Label", + "additionalDirection": "Additional Direction", + "aptNumber": "Apt Number", + "floor": "Floor", + "street": "Street", + "apartment": "Apartment", + "house": "House", + "mobileNumber": "Mobile Number", + "items": "Items", + "roomDetails": "Room Details", + "changeRoom": "Change Room", + "selectRoom": "Select Room", + "roomNo": "Room Number", + "floorNo": "Floor Number", + "note": "Note", + "guestName": "Guest Name", + "deliverToRoom": "Deliver to Room", + "pleaseKnockSoftly": "Please knock softly", + "officeDetails": "Office Details", + "changeOffice": "Change Office", + "selectOffice": "Select Office", + "officeNo": "Office Number", + "company": "Company", + "contactPerson": "Contact Person", + "phone": "Phone", + "giftDetails": "Gift Details", + "changeGift": "Change Gift", + "selectGift": "Select Gift", + "receiverName": "Receiver Name", + "receiverPhone": "Receiver Phone", + "keepMyNameSecret": "Keep my name secret", + "howItWorks": "How it works", + "senderName": "Sender Name", + "senderPhone": "Sender Phone", + "gotIt": "Got It", + "howItWorksDescription": "The gifted amount will be credited directly to your friend's wallet in the app. The recipient can use the amount to book a session of their choice within the app. The gifted amount is non-refundable and can only be used for booking sessions." + }, + "login": { + "singup/Login": "Sing up / Login", + "EnterYourNumber": "Enter your Number", + "WeWillSendYouAWhatsAppMessageWithAOneTimeVerificationCode": "We will send you a WhatsApp message with a one-time verification code", + "EnterYourName": "Enter your name", + "DateOfBirth": "Date of birth", + "mobileNumber": "Mobile Number", + "signIn": "Sign In", + "sendCode": "Send Code", + "OTPSentToYourPhoneNumber": "OTP sent to your phone number" + }, + "otp": { + "title": "Verification", + "verification": "Verification", + "enterThe4DigitCodeThatSentToYourPhoneNumber": "Enter the 4-digit code that was sent to your phone number", + "resendOtp": "Resend OTP", + "otpSent": "OTP sent to your phone number", + "otpExpired": "OTP expired", + "otpExpiredMessage": "Please resend the OTP", + "continue": "Continue", + "resendAvailableIn": "You can resend the OTP in seconds", + "confirmOTPSuccess": "OTP confirmed successfully" + }, + "order": { + "title": "Order", + "yourOrderFromFascanoRestaurant": "Your order from Fascano restaurant", + "muscat": "Muscat", + "reserved": "Reserved", + "prepare": "Prepare", + "ready": "Ready", + "inProgressOrder": "In Progress Order", + "yourOrderFrom": "Your Order From", + "cancelOrder": "Cancel Order", + "areYouSureYouWantToCancelThisOrder?": "Are you sure you want to cancel this order?", + "keepOrder": "Keep Order", + "thisActionCannotBeUndone": "This action cannot be undone." + } +} diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/AccessDenied/AccessDenied.tsx b/src/components/AccessDenied/AccessDenied.tsx new file mode 100644 index 0000000..650e2c8 --- /dev/null +++ b/src/components/AccessDenied/AccessDenied.tsx @@ -0,0 +1,25 @@ +import { Button, Result } from "antd"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { PATH_AUTH } from "utils/constants"; + +const AccessDenied: React.FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + return ( + navigate(PATH_AUTH.signin)}> + {t("back to home")} + + } + /> + ); +}; + +export default AccessDenied; diff --git a/src/components/ActionsButtons/ActionsButtons.module.css b/src/components/ActionsButtons/ActionsButtons.module.css new file mode 100644 index 0000000..ade1e78 --- /dev/null +++ b/src/components/ActionsButtons/ActionsButtons.module.css @@ -0,0 +1,86 @@ +.quantityControls { + display: flex; + align-items: center; + background-color: #ffb70014; + border-radius: 888px; + width: fit-content; +} + +.quantityLabel { + font-size: 14px; + color: var(--secondary-color); + font-weight: 500; +} + +.quantityInputContainer { + display: flex; + align-items: center; + padding: 8px; + border-radius: 888px; + width: 84px; + height: 32px; +} + +.quantityButton { + padding: 0px; + width: 25px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + color: var(--secondary-color); + transition: all 0.2s ease; +} + +.quantityInput { + text-align: center; + border: none; + box-shadow: none; + font-size: 16px; + font-weight: 600; +} + +.removeButton { + padding: 4px 0px; + height: 32px; + display: flex; + align-items: center; + gap: 4px; + width: 30px; +} + +.deleteButtonContainer { + position: absolute; + top: 12px; + right: 12px; + background-color: var(--primary-color); + border-radius: 50%; + padding: 8px; + cursor: pointer; + transition: all 0.3s ease; +} + +.deleteIcon { + font-size: 18px; + color: var(--secondary-color); +} + +.cartItemActions :global(.ant-input-number-outlined) { + border: none; + width: 40px; + background-color: inherit; + text-align: center; +} + +.cartItemActions :global(.ant-input-number-input) { + text-align: center !important; +} + +.plusIcon { +margin-bottom: 1px; +} + +.deleteIcon { + position: relative; + right: 1px; +} \ No newline at end of file diff --git a/src/components/ActionsButtons/ActionsButtons.tsx b/src/components/ActionsButtons/ActionsButtons.tsx new file mode 100644 index 0000000..93f7b2e --- /dev/null +++ b/src/components/ActionsButtons/ActionsButtons.tsx @@ -0,0 +1,110 @@ +import { MinusOutlined } from "@ant-design/icons"; +import { Button, Grid, InputNumber, Popconfirm } from "antd"; +import DeleteIcon from "components/Icons/DeleteIcon"; +import PlusIcon from "components/Icons/PlusIcon"; +import { useTranslation } from "react-i18next"; +import { useAppSelector } from "redux/hooks"; +import { colors } from "../../ThemeConstants"; +import styles from "./ActionsButtons.module.css"; + +const { useBreakpoint } = Grid; + +export default function ActionsButtons({ + quantity, + setQuantity, + max, + min, +}: { + quantity: number; + setQuantity: (quantity: number) => void; + max?: number; + min?: number; +}) { + const { t } = useTranslation(); + const { xs } = useBreakpoint(); // Default to desktop + const { isRTL } = useAppSelector((state) => state.locale); // Default to LTR + + const getPopconfirmOverlayStyle = () => ({ + width: xs ? "280px" : "auto", + maxWidth: "320px", + ".antPopconfirmMessageTitle": { + fontSize: xs ? "14px" : "16px", + paddingRight: xs ? "24px" : "0", + }, + ".antPopconfirmMessageContent": { + fontSize: xs ? "13px" : "14px", + marginTop: "4px", + }, + ".antPopconfirmButtons": { + marginTop: "12px", + ".ant-btn": { + fontSize: xs ? "13px" : "14px", + height: xs ? "28px" : "32px", + padding: xs ? "0 12px" : "4px 15px", + }, + }, + }); + + return ( +
+
+
+ {quantity > 0 ? ( + + ) : ( + setQuantity(0)} + okText={t("cart.deleteConfirmation.confirm")} + cancelText={t("cart.deleteConfirmation.cancel")} + okButtonProps={{ danger: true }} + placement={isRTL ? "left" : "right"} + styles={{ + root: getPopconfirmOverlayStyle(), + body: { + padding: xs ? "12px" : "16px", + }, + }} + > + +
+
+
+ ); +} diff --git a/src/components/Ads/Ads1.module.css b/src/components/Ads/Ads1.module.css new file mode 100644 index 0000000..a043268 --- /dev/null +++ b/src/components/Ads/Ads1.module.css @@ -0,0 +1,70 @@ +.adsContainer { + margin: 0 16px; +} + +.adsContainer :global(.ant-card-body) { + padding: 6px 10px !important; + text-align: start; + width: 100%; +} + +.adsContainer * { + transition: all 0.3s ease; +} + +:global(.darkApp) .adsContainer { + margin: 0 16px; + background-color: #181818 !important; + border-color: #363636 !important; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); +} + +:global(.darkApp) .adsContainer:hover { + background-color: #363636 !important; + border-color: #424242 !important; + transform: translateY(-1px); + box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.5); +} + +:global(.darkApp) .adsContainer { + backdrop-filter: blur(12px); + border-radius: 8px; +} + +.rightRectangle { + position: absolute; + right: 16px; + z-index: 10; +} + +.leftRectangle { + position: absolute; + left: 16px; + z-index: 10; +} + +:global(.darkApp) .rightRectangle { + right: 0px; +} + +:global(.darkApp) .leftRectangle { + left: 0px; +} + +.adsCard { + display: flex; + flex-direction: row; + justify-content: space-between; + text-align: left; + height: 64px; + border-radius: 8px; + background-color: #c8e6a9; +} + +.adsCard :global(.ant-card-body) { + padding: 8px !important; +} + +:global(.darkApp) .adsCard { + background-color: #2b4511; +} diff --git a/src/components/Ads/Ads1.tsx b/src/components/Ads/Ads1.tsx new file mode 100644 index 0000000..52a38da --- /dev/null +++ b/src/components/Ads/Ads1.tsx @@ -0,0 +1,59 @@ +import { Card } from "antd"; +import BuildIcon from "components/Icons/ads/BuildIcon"; +import LeftRectangle from "components/Icons/LeftRectangle"; +import RightRectangle from "components/Icons/RightRectangle"; +import ProText from "components/ProText"; +import ProTitle from "components/ProTitle"; +import { useTranslation } from "react-i18next"; +import { useAppSelector } from "redux/hooks"; +import styles from "./Ads1.module.css"; + +export default function Ads1({ className }: { className?: string }) { + const { isRTL } = useAppSelector((state) => state.locale); + const { t } = useTranslation(); + + return ( +
+ {isRTL ? ( + + ) : ( + + )} + + + {t("home.promotion.description")} + + + + {t("home.promotion.title")} + + +
+ +
+
+
+ ); +} diff --git a/src/components/Ads/Ads2.module.css b/src/components/Ads/Ads2.module.css new file mode 100644 index 0000000..b2ef0a9 --- /dev/null +++ b/src/components/Ads/Ads2.module.css @@ -0,0 +1,71 @@ +.adsContainer { + margin: 0px; + position: relative; +} + +.adsContainer :global(.ant-card-body) { + padding: 6px 10px !important; + text-align: start; + width: 100%; +} + +.adsContainer * { + transition: all 0.3s ease; +} + +:global(.darkApp) .adsContainer { + margin: 0 16px; + background-color: #181818 !important; + border-color: #363636 !important; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4); +} + +:global(.darkApp) .adsContainer:hover { + background-color: #363636 !important; + border-color: #424242 !important; + transform: translateY(-1px); + box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.5); +} + +:global(.darkApp) .adsContainer { + backdrop-filter: blur(12px); + border-radius: 8px; +} + +.rightRectangle { + position: absolute; + right: 0px; + z-index: 10; +} + +.leftRectangle { + position: absolute; + left: 0px; + z-index: 10; +} + +:global(.darkApp) .rightRectangle { + right: 0px; +} + +:global(.darkApp) .leftRectangle { + left: 0px; +} + +.adsCard { + display: flex; + flex-direction: row; + justify-content: space-between; + text-align: left; + height: 64px; + border-radius: 8px; + background-color: #c8e6a9; +} + +.adsCard :global(.ant-card-body) { + padding: 8px !important; +} + +:global(.darkApp) .adsCard { + background-color: #2b4511; +} diff --git a/src/components/Ads/Ads2.tsx b/src/components/Ads/Ads2.tsx new file mode 100644 index 0000000..562a2aa --- /dev/null +++ b/src/components/Ads/Ads2.tsx @@ -0,0 +1,58 @@ +import { Card } from "antd"; +import BuildIcon from "components/Icons/ads/BuildIcon"; +import LeftRectangle from "components/Icons/LeftRectangle"; +import RightRectangle from "components/Icons/RightRectangle"; +import ProText from "components/ProText"; +import ProTitle from "components/ProTitle"; +import { useTranslation } from "react-i18next"; +import { useAppSelector } from "redux/hooks"; +import styles from "./Ads2.module.css"; + +export default function Ads2({ + className, +}: { + className?: string; +}) { + const { t } = useTranslation(); + const { isRTL } = useAppSelector((state) => state.locale); + + return ( +
+ {isRTL ? ( + + ) : ( + + )} + + + {t("home.promotion.description")} + + + + {t("home.promotion.title")} + + +
+ +
+
+
+ ); +} diff --git a/src/components/ArabicPrice/ArabicPrice.tsx b/src/components/ArabicPrice/ArabicPrice.tsx new file mode 100644 index 0000000..1c74489 --- /dev/null +++ b/src/components/ArabicPrice/ArabicPrice.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { useAppSelector } from "redux/hooks"; +import ProText from "../ProText"; + +interface ArabicPriceProps { + price: number | string; + style?: React.CSSProperties; + strong?: boolean; + type?: "secondary" | "success" | "warning" | "danger"; + className?: string; +} + +const ArabicPrice: React.FC = ({ + price, + style = {}, + strong = false, + type, + className, +}) => { + const { t } = useTranslation(); + const { isRTL } = useAppSelector((state) => state.locale); + + // Format the price to ensure it has 2 decimal places + const formattedPrice = typeof price === "number" ? price.toFixed(2) : price; + + return ( + + {isRTL ? ( + <> + + {formattedPrice} + + + + {t("common.omanCurrency")} + + + ) : ( + <> + + {formattedPrice} + + + {t("common.omanCurrency")} + + + )} + + ); +}; + +export default ArabicPrice; diff --git a/src/components/ArabicPrice/index.ts b/src/components/ArabicPrice/index.ts new file mode 100644 index 0000000..8fe5621 --- /dev/null +++ b/src/components/ArabicPrice/index.ts @@ -0,0 +1,2 @@ +export { default as ArabicPrice, default } from './ArabicPrice'; + diff --git a/src/components/CartActionsButtons/CartActionsButtons.module.css b/src/components/CartActionsButtons/CartActionsButtons.module.css new file mode 100644 index 0000000..27c2243 --- /dev/null +++ b/src/components/CartActionsButtons/CartActionsButtons.module.css @@ -0,0 +1,92 @@ +.quantityControls { + display: flex; + align-items: center; + background-color: #ffb70014; + border-radius: 888px; + width: fit-content; + margin-left: 3px; +} + +:global(.rtl) .quantityControls { + margin-left: 0; + margin-right: 3px; +} + +.quantityLabel { + font-size: 14px; + color: var(--secondary-color); + font-weight: 500; +} + +.quantityInputContainer { + display: flex; + align-items: center; + padding: 8px; + border-radius: 888px; + width: 84px; + height: 32px; +} + +.quantityButton { + padding: 0px; + width: 25px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + color: var(--secondary-color); + transition: all 0.2s ease; +} + +.quantityInput { + text-align: center; + border: none; + box-shadow: none; + font-size: 16px; + font-weight: 600; +} + +.removeButton { + padding: 4px 0px; + height: 32px; + display: flex; + align-items: center; + gap: 4px; + width: 30px; +} + +.deleteButtonContainer { + position: absolute; + top: 12px; + right: 12px; + background-color: var(--primary-color); + border-radius: 50%; + padding: 8px; + cursor: pointer; + transition: all 0.3s ease; +} + +.deleteIcon { + font-size: 18px; + color: var(--secondary-color); +} + +.cartItemActions :global(.ant-input-number-outlined) { + border: none; + width: 40px; + background-color: inherit; + text-align: center; +} + +.cartItemActions :global(.ant-input-number-input) { + text-align: center !important; +} + +.plusIcon { +margin-bottom: 1px; +} + +.deleteIcon { + position: relative; + right: 1px; +} \ No newline at end of file diff --git a/src/components/CartActionsButtons/CartActionsButtons.tsx b/src/components/CartActionsButtons/CartActionsButtons.tsx new file mode 100644 index 0000000..c47d558 --- /dev/null +++ b/src/components/CartActionsButtons/CartActionsButtons.tsx @@ -0,0 +1,109 @@ +import { MinusOutlined } from "@ant-design/icons"; +import { Button, InputNumber, Popconfirm } from "antd"; +import DeleteIcon from "components/Icons/DeleteIcon"; +import PlusIcon from "components/Icons/PlusIcon"; +import { removeItem, updateQuantity } from "features/order/orderSlice"; +import { useTranslation } from "react-i18next"; +import { useAppDispatch } from "redux/hooks"; +import { CartItem } from "utils/types/appTypes"; +import { colors } from "../../ThemeConstants"; +import styles from "./CartActionsButtons.module.css"; + +export default function CartActionsButtons({ item }: { item: CartItem }) { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const isMobile = false; // Default to desktop + const message = { success: (msg: string) => console.log(msg) }; // Simple message handler + const isRTL = false; // Default to LTR + + const handleDeleteItem = (itemId: string) => { + dispatch(removeItem(Number(itemId))); + message.success(t("cart.deleteConfirmation.success")); + }; + + const getPopconfirmOverlayStyle = () => ({ + width: isMobile ? "280px" : "auto", + maxWidth: "320px", + ".antPopconfirmMessageTitle": { + fontSize: isMobile ? "14px" : "16px", + paddingRight: isMobile ? "24px" : "0", + }, + ".antPopconfirmMessageContent": { + fontSize: isMobile ? "13px" : "14px", + marginTop: "4px", + }, + ".antPopconfirmButtons": { + marginTop: "12px", + ".ant-btn": { + fontSize: isMobile ? "13px" : "14px", + height: isMobile ? "28px" : "32px", + padding: isMobile ? "0 12px" : "4px 15px", + }, + }, + }); + + return ( +
+
+
+ {item.quantity > 1 ? ( + + ) : ( + handleDeleteItem(item.id.toString())} + okText={t("cart.deleteConfirmation.confirm")} + cancelText={t("cart.deleteConfirmation.cancel")} + okButtonProps={{ danger: true }} + placement={isRTL ? "left" : "right"} + styles={{ + root: getPopconfirmOverlayStyle(), + body: { + padding: isMobile ? "12px" : "16px", + }, + }} + > + +
+
+
+ ); +} diff --git a/src/components/CustomBottomSheet/CancelOrderBottomSheet.tsx b/src/components/CustomBottomSheet/CancelOrderBottomSheet.tsx new file mode 100644 index 0000000..0906c4d --- /dev/null +++ b/src/components/CustomBottomSheet/CancelOrderBottomSheet.tsx @@ -0,0 +1,138 @@ +// import { useGlobals } from "../../hooks/useGlobals"; +import { Button, Card } from "antd"; +import BackIcon from "components/Icons/BackIcon"; +import CancelIcon from "components/Icons/CancelIcon"; +import CancelPopupIcon from "components/Icons/CancelPopupIcon"; +import NextIcon from "components/Icons/NextIcon"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import ProText from "../ProText"; +import ProTitle from "../ProTitle"; +import styles from "./CustomBottomSheet.module.css"; + +interface CancelOrderBottomSheetProps { + initialValue?: string; + onSave?: (value: string) => void; +} + +export function CancelOrderBottomSheet({}: CancelOrderBottomSheetProps) { + const { t } = useTranslation(); + const [isOpen, setIsOpen] = useState(false); + const isRTL = false; // Default to LTR + + const handleSave = () => { + setIsOpen(false); + }; + + const handleCancel = () => { + setIsOpen(false); + }; + + return ( + <> + setIsOpen(true)}> +
+
+ + + + {t("order.cancelOrder")} + +
+ + {isRTL ? ( + + ) : ( + + )} +
+
+ + +
+ + + + {t("order.areYouSureYouWantToCancelThisOrder?")} + + + + {t("order.thisActionCannotBeUndone")} + + +
+ + + +
+
+
+ + ); +} diff --git a/src/components/CustomBottomSheet/CouponBottomSheet.tsx b/src/components/CustomBottomSheet/CouponBottomSheet.tsx new file mode 100644 index 0000000..6bb7012 --- /dev/null +++ b/src/components/CustomBottomSheet/CouponBottomSheet.tsx @@ -0,0 +1,70 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import ProRatioGroups from "../ProRatioGroups/ProRatioGroups"; + +interface CouponBottomSheetProps { + isOpen: boolean; + onClose: () => void; + initialValue: string; + onSave: (value: string) => void; +} + +export function CouponBottomSheet({ + isOpen, + onClose, + initialValue, + onSave, +}: CouponBottomSheetProps) { + const { t } = useTranslation(); + const [value, setValue] = useState(initialValue); + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + const handleSave = () => { + onSave(value); + onClose(); + }; + + const handleCancel = () => { + setValue(initialValue); + onClose(); + }; + + return ( + +
+ +
+
+ ); +} diff --git a/src/components/CustomBottomSheet/CouponDialog.tsx b/src/components/CustomBottomSheet/CouponDialog.tsx new file mode 100644 index 0000000..500f756 --- /dev/null +++ b/src/components/CustomBottomSheet/CouponDialog.tsx @@ -0,0 +1,73 @@ +import { Button, Modal } from "antd"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import ProRatioGroups from "../ProRatioGroups/ProRatioGroups"; + +interface CouponDialogProps { + isOpen: boolean; + onClose: () => void; + initialValue: string; + onSave: (value: string) => void; +} + +export function CouponDialog({ + isOpen, + onClose, + initialValue, + onSave, +}: CouponDialogProps) { + const { t } = useTranslation(); + const [value, setValue] = useState(initialValue); + + console.log(value); + + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + const handleSave = (selectedValue: string) => { + onSave(selectedValue); + onClose(); + }; + + const handleCancel = () => { + setValue(initialValue); + onClose(); + }; + + return ( + + {t("cart.cancel")} + , + ]} + width={600} + destroyOnHidden + > +
+ +
+
+ ); +} diff --git a/src/components/CustomBottomSheet/CustomBottomSheet.module.css b/src/components/CustomBottomSheet/CustomBottomSheet.module.css new file mode 100644 index 0000000..ece3d23 --- /dev/null +++ b/src/components/CustomBottomSheet/CustomBottomSheet.module.css @@ -0,0 +1,33 @@ +.homeServiceCard { + width: 100%; + height: 48px; + display: flex; + justify-content: flex-start; + padding: 12px 18px !important; + row-gap: 10px; + transition: all 0.3s ease; + border-radius: 50px; + background-color: rgba(234, 31, 34, 0.04); + color: #ea1f22; + } + + .homeServiceCard :global(.ant-card-body) { + padding: 0px !important; + text-align: start; + width: 100%; + } + + .serviceIcon path { + stroke: #ea1f22; + } + + .nextIcon { + width: 24px; + height: 24px; + } + + .backIcon { + width: 24px; + height: 24px; + } + \ No newline at end of file diff --git a/src/components/CustomBottomSheet/DatePickerBottomSheet.tsx b/src/components/CustomBottomSheet/DatePickerBottomSheet.tsx new file mode 100644 index 0000000..c478c1a --- /dev/null +++ b/src/components/CustomBottomSheet/DatePickerBottomSheet.tsx @@ -0,0 +1,263 @@ +import { Button } from "antd"; +import { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import Picker from "../WheelPicker"; + +interface DatePickerBottomSheetProps { + isOpen: boolean; + onClose: () => void; + onDateSelect?: (date: { month: string; day: string; year: string }) => void; + initialDate?: Date; +} + +export default function DatePickerBottomSheet({ + isOpen, + onClose, + onDateSelect, + initialDate = new Date(), +}: DatePickerBottomSheetProps) { + const { t } = useTranslation(); + + const [selectedDate, setSelectedDate] = useState({ + month: (initialDate.getMonth() + 1).toString().padStart(2, "0"), + day: initialDate.getDate().toString().padStart(2, "0"), + year: initialDate.getFullYear().toString(), + }); + + // Generate picker options + const pickerOptions = useMemo(() => { + const months = [ + { value: "01", label: t("common.January") }, + { value: "02", label: t("common.February") }, + { value: "03", label: t("common.March") }, + { value: "04", label: t("common.April") }, + { value: "05", label: t("common.May") }, + { value: "06", label: t("common.June") }, + { value: "07", label: t("common.July") }, + { value: "08", label: t("common.August") }, + { value: "09", label: t("common.September") }, + { value: "10", label: t("common.October") }, + { value: "11", label: t("common.November") }, + { value: "12", label: t("common.December") }, + ]; + + const days = Array.from({ length: 31 }, (_, i) => ({ + value: (i + 1).toString().padStart(2, "0"), + label: (i + 1).toString(), + })); + + const years = Array.from({ length: 21 }, (_, i) => { + const year = new Date().getFullYear() - 10 + i; + return { + value: year.toString(), + label: year.toString(), + }; + }); + + return { + month: months, + day: days, + year: years, + }; + }, [t]); + + const handleDateChange = (month: string, day: string, year: string) => { + setSelectedDate((prev) => ({ + ...prev, + month, + day, + year, + })); + }; + + const handleConfirm = () => { + onDateSelect?.(selectedDate); + onClose(); + }; + + const handleTouchStart = (e: React.TouchEvent) => { + e.stopPropagation(); + }; + + const handleTouchMove = (e: React.TouchEvent) => { + e.stopPropagation(); + }; + + const handleTouchEnd = (e: React.TouchEvent) => { + e.stopPropagation(); + }; + + const handleMouseDown = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + const handleMouseMove = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + const handleMouseUp = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + const handleWheel = (e: React.WheelEvent) => { + e.stopPropagation(); + }; + + return ( + +
+ {/* Date Picker */} +
+ { + handleDateChange(value.month, value.day, value.year); + }} + width="100%" + height={250} + style={{ + position: "relative", + display: "flex", + backgroundColor: "#ffffff", + overflow: "hidden", + width: "100%", + height: 250, + }} + aria-selected={false} + > + + {pickerOptions.month.map((option) => ( + + {({ selected }) => ( +
+ {option.label} +
+ )} +
+ ))} +
+ + {pickerOptions.day.map((option) => ( + + {({ selected }) => ( +
+ {option.label} +
+ )} +
+ ))} +
+ + {pickerOptions.year.map((option) => ( + + {({ selected }) => ( +
+ {option.label} +
+ )} +
+ ))} +
+
+
+ + +
+
+ ); +} diff --git a/src/components/CustomBottomSheet/EstimateTimeBottomSheet.tsx b/src/components/CustomBottomSheet/EstimateTimeBottomSheet.tsx new file mode 100644 index 0000000..aae647a --- /dev/null +++ b/src/components/CustomBottomSheet/EstimateTimeBottomSheet.tsx @@ -0,0 +1,265 @@ +// import { useGlobals } from "../../hooks/useGlobals"; +import { Button, Divider } from "antd"; +import BackIcon from "components/Icons/BackIcon"; +import NextIcon from "components/Icons/NextIcon"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import styles from "./CustomBottomSheet.module.css"; + +interface EstimateTimeBottomSheetProps { + isOpen: boolean; + onClose: () => void; + onSave: (date: Date, time: string) => void; + initialDate?: Date; +} + +export function EstimateTimeBottomSheet({ + isOpen, + onClose, + onSave, + initialDate = new Date(), +}: EstimateTimeBottomSheetProps) { + const { t } = useTranslation(); + const isRTL = false; // Default to LTR + const [selectedDate, setSelectedDate] = useState(initialDate); + const [selectedTime, setSelectedTime] = useState("12:00"); + const [isAM, setIsAM] = useState(true); + + useEffect(() => { + setSelectedDate(initialDate); + }, [initialDate]); + + const formatDate = (date: Date) => { + return date.toLocaleDateString("en-US", { + weekday: "long", + month: "short", + day: "numeric", + }); + }; + + const goToPreviousDay = () => { + const newDate = new Date(selectedDate); + newDate.setDate(newDate.getDate() - 1); + setSelectedDate(newDate); + }; + + const goToNextDay = () => { + const newDate = new Date(selectedDate); + newDate.setDate(newDate.getDate() + 1); + setSelectedDate(newDate); + }; + + const handleTimeSelect = (time: string) => { + setSelectedTime(time); + }; + + const handleSave = () => { + const [hours, minutes] = selectedTime.split(":"); + const date = new Date(selectedDate); + date.setHours( + isAM ? parseInt(hours) : parseInt(hours) + 12, + parseInt(minutes), + 0, + 0 + ); + + onSave(date, `${selectedTime} ${isAM ? "AM" : "PM"}`); + onClose(); + }; + + const timeSlots = [ + "12:00", + "1:00", + "2:00", + "3:00", + "4:00", + "5:00", + "6:00", + "7:00", + "8:00", + "9:00", + "10:00", + "11:00", + ]; + + return ( + +
+ {/* Day Selection */} +
+
+
+
+ + {/* Time Selection */} +
+ {/* AM/PM Toggle */} +
+ + +
+ + {/* Time Slots Grid */} +
+ {timeSlots.map((time) => ( + + ))} +
+ + + + {/* Save Button */} + +
+
+
+ ); +} diff --git a/src/components/CustomBottomSheet/GiftBottomSheet.tsx b/src/components/CustomBottomSheet/GiftBottomSheet.tsx new file mode 100644 index 0000000..9cab852 --- /dev/null +++ b/src/components/CustomBottomSheet/GiftBottomSheet.tsx @@ -0,0 +1,156 @@ +import { Button, Checkbox, Form, Input } from "antd"; +import { GiftDetailsType } from "features/order/orderSlice"; +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { ProBottomSheet } from "../ProBottomSheet/ProBottomSheet"; +import ProText from "../ProText"; + +const { TextArea } = Input; + +interface GiftBottomSheetProps { + isOpen: boolean; + onClose: () => void; + initialValue: GiftDetailsType | null; + onSave: (value: GiftDetailsType) => void; +} + +export function GiftBottomSheet({ + isOpen, + onClose, + initialValue, + onSave, +}: GiftBottomSheetProps) { + const { t } = useTranslation(); + const [giftForm] = Form.useForm(); + useEffect(() => { + giftForm.setFieldsValue(initialValue); + }, [initialValue, giftForm]); + + const handleSave = () => { + onSave(giftForm.getFieldsValue()); + giftForm.resetFields(); + onClose(); + }; + + const handleCancel = () => { + giftForm.setFieldsValue(initialValue); + giftForm.resetFields(); + onClose(); + }; + + return ( + +
+ + + + + + + + +