Compare commits
104 Commits
731353a4a8
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 677c62e6eb | |||
| e0eb29488a | |||
| ae3787241a | |||
| 9e105606a3 | |||
| c6bb275d05 | |||
| 4273e899ca | |||
| 0361e05acb | |||
| 33cce93064 | |||
| a2a591420c | |||
| 3c764a1652 | |||
| 954ad48486 | |||
| 368cace143 | |||
| 46926c66e7 | |||
| a448ff10de | |||
| 80a92ea4c4 | |||
| 63a774c4af | |||
| 1ed198ca69 | |||
| d94bf77522 | |||
| 5e314bb581 | |||
| 0391513928 | |||
| 22d15f9a13 | |||
| bbb797cc62 | |||
| 4af7181384 | |||
| cda1b5b5bd | |||
| 2b4fd31ea2 | |||
| 168af3bf95 | |||
| 0bef54c51c | |||
| 85ba8e4261 | |||
| 39e18c3331 | |||
| ca02a621da | |||
| b13a46ec65 | |||
| e93ab38b05 | |||
| 052b737653 | |||
| b8de25e9d1 | |||
| 8b194fd7cd | |||
| b7ffb2b857 | |||
| c6d1097a30 | |||
| 494a6a53fc | |||
| ebb2092ec1 | |||
| 9447500725 | |||
| 46f5389036 | |||
| f9113ef9c8 | |||
| e6fc300fba | |||
| 01f890e9ea | |||
| f57761b726 | |||
| 0f0408d9f6 | |||
| e987163bd9 | |||
| d573e8dc8d | |||
| 0245e8d407 | |||
| 3402ad39f6 | |||
| 121852ab20 | |||
| 879bc0ae49 | |||
| 5174d0371d | |||
| ef4ddeb3fa | |||
| 7f35f5b9dd | |||
| a1c182afde | |||
| 89d9155184 | |||
| 5002596bb9 | |||
| 9b00a22d11 | |||
| 03fc293ee9 | |||
| 2804cc4100 | |||
| 17717eb195 | |||
| 0138261643 | |||
| 6653e3db82 | |||
| 032050bacc | |||
| c65e8cd303 | |||
| d95a6e679e | |||
| 3c9bb6274f | |||
| 8dd00d4cc7 | |||
| 3c833df63c | |||
| 4daa03d0de | |||
| 172cbea22c | |||
| 8014fe7ad3 | |||
| d977c20744 | |||
| 81a786d2a4 | |||
| 251d18145a | |||
| 79636590cd | |||
| 7f4c29bd33 | |||
| 9b72105115 | |||
| 3bfd5fd1eb | |||
| 76f3e1bf6e | |||
| 8a5cd09172 | |||
| 04da87b7d7 | |||
| da0069f2bc | |||
| 9d8dd549dc | |||
| 42ce8fd892 | |||
| 994fc4af4b | |||
| f6147fafa1 | |||
| f95aeb1810 | |||
| d374055aeb | |||
| 4babfbc232 | |||
| d50b2b7182 | |||
| 112cc1c4dc | |||
| 96647786e8 | |||
| 83b2fd809c | |||
| cbdc7edbf5 | |||
| 612deba297 | |||
| 517c77af77 | |||
| ebb6c1c9c6 | |||
| b6bff9956a | |||
| 4390937b19 | |||
| 642155661c | |||
| 5bbd677f41 | |||
| 7589a26706 |
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
name: dpage-editor
|
||||||
|
description: Guides development in the dpage-editor React/Konva catalogue editor and HTML flip-book viewer. Use when editing editor tools, viewer rendering, Zustand store, catalogue autosave, entrance animations, masks, or any code under src/pages/editor or src/pages/viewer.
|
||||||
|
---
|
||||||
|
|
||||||
|
# dpage-editor
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- React 19 + TypeScript + Vite
|
||||||
|
- Canvas editor: **Konva** / **react-konva**
|
||||||
|
- Viewer: **react-pageflip** (HTML/CSS, not Konva)
|
||||||
|
- State: **Zustand** (`editorStore`)
|
||||||
|
- Data: **TanStack Query** + axios services
|
||||||
|
- Styling: **Tailwind CSS 4**, `clx()` from `@/helpers/utils`
|
||||||
|
- i18n: react-i18next (Persian UI)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
| Area | Path | Role |
|
||||||
|
|------|------|------|
|
||||||
|
| Editor page | `src/pages/editor/Editor.tsx` | Loads catalogue, autosave, Konva lifecycle |
|
||||||
|
| Editor store | `src/pages/editor/store/editorStore.ts` | Pages, objects, undo, selection |
|
||||||
|
| Object types | `src/pages/editor/store/editorStore.types.ts` | `EditorObject`, `ToolType`, animations |
|
||||||
|
| Canvas | `src/pages/editor/components/canvas/` | Layers, drawing, transform, snap |
|
||||||
|
| Tools (Konva) | `src/pages/editor/components/tools/` | One component per shape/media type |
|
||||||
|
| Sidebar | `src/pages/editor/components/sidebar/` | ToolsBar, settings, instructions |
|
||||||
|
| Viewer | `src/pages/viewer/` | Flip-book, entrance animations, magnifier |
|
||||||
|
| Book render | `src/pages/viewer/components/BookPage.tsx` | HTML mirror of `EditorObject` |
|
||||||
|
| Catalogue API | `src/pages/catalogue/hooks/useCatalogueData.ts` | Load/save `content` JSON |
|
||||||
|
|
||||||
|
**Source of truth:** `EditorObject[]` per page, serialized into catalogue `content`. Editor and viewer must stay in sync.
|
||||||
|
|
||||||
|
## Critical conventions
|
||||||
|
|
||||||
|
### Konva text rendering
|
||||||
|
|
||||||
|
In `Editor.tsx`, `Konva.legacyTextRendering = true` is required. Konva 10's new text baseline breaks transformer bounds for Persian/Latin text. Do not remove without fixing text measurement.
|
||||||
|
|
||||||
|
### Store sync
|
||||||
|
|
||||||
|
`objects` and `pages[currentPageId].objects` must stay synced. Use existing helpers (`withSyncedCurrentPageObjects`, store actions) — never update one without the other.
|
||||||
|
|
||||||
|
### IDs and cloning
|
||||||
|
|
||||||
|
- Use `createScopedId` / helpers in `editorStore.helpers.ts` for new IDs.
|
||||||
|
- Undo history uses `structuredClone` on object lists; new fields on `EditorObject` must be JSON-serializable.
|
||||||
|
|
||||||
|
### Path alias
|
||||||
|
|
||||||
|
Use `@/` imports (e.g. `@/pages/editor/store/editorStore`).
|
||||||
|
|
||||||
|
## Adding or changing a tool type
|
||||||
|
|
||||||
|
Checklist — touch all relevant layers:
|
||||||
|
|
||||||
|
1. **`ToolType`** in `editorStore.types.ts` — add union member + fields on `EditorObject` if needed.
|
||||||
|
2. **Konva shape** — `src/pages/editor/components/tools/<Name>Shape.tsx`, export from `tools/index.ts`.
|
||||||
|
3. **`ObjectRenderer.tsx`** — `switch (obj.type)` case + props wiring.
|
||||||
|
4. **Sidebar** — `ToolsBar.tsx`, `ToolInstructions.tsx`, optional `instructions/` + `settings/`.
|
||||||
|
5. **Creation defaults** — store action or drawing handler that sets initial `x`, `y`, `width`, `height`.
|
||||||
|
6. **Viewer** — `BookPage.tsx` `renderObject` branch; reuse utils from `src/pages/editor/utils/` (gradient, fontFamily, textStyle, customShape).
|
||||||
|
7. **Entrance animation** (if applicable) — `entranceAnimations.css`, `entranceAnimationStyle.ts`, `useBookEntranceController.ts`.
|
||||||
|
|
||||||
|
Default tool prop pattern:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type ShapeProps = {
|
||||||
|
obj: EditorObject;
|
||||||
|
isSelected: boolean;
|
||||||
|
transformerRef: React.RefObject<Konva.Transformer | null>;
|
||||||
|
onSelect: (id: string, node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => void;
|
||||||
|
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||||
|
draggable: boolean;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Editor vs viewer
|
||||||
|
|
||||||
|
| Concern | Editor | Viewer |
|
||||||
|
|---------|--------|--------|
|
||||||
|
| Rendering | Konva nodes | HTML/CSS (`BookPage`) |
|
||||||
|
| Interaction | Select, transform, draw | Flip pages, links, autoplay |
|
||||||
|
| Scale | Stage zoom | `VIEWER_SCALE` + viewport hooks |
|
||||||
|
| Media | Full controls in sidebar | `staticMedia` option for thumbnails |
|
||||||
|
| RTL | Stage coordinates | `RTL_COVER_PAD_PAGE_ID` for odd page counts |
|
||||||
|
|
||||||
|
Viewer-specific logic belongs in `src/pages/viewer/hooks/` and `utils/`, not in editor components.
|
||||||
|
|
||||||
|
## Autosave (Editor.tsx)
|
||||||
|
|
||||||
|
- `useUpdateCatalog` debounces saves.
|
||||||
|
- On catalogue `id` change: `cancelPendingUpdate`, `resetEditor`, reload once per id (`loadedCatalogIdRef`).
|
||||||
|
- Skip first autosave after load (`skipNextAutosaveRef`).
|
||||||
|
|
||||||
|
## Code style for this repo
|
||||||
|
|
||||||
|
- Prefer extending existing components over new abstractions.
|
||||||
|
- Keep comments for non-obvious Konva/RTL/safari workarounds (see `BookViewer.tsx`, `Editor.tsx`).
|
||||||
|
- Match Persian UI strings in surrounding files.
|
||||||
|
- Minimize diff scope; do not refactor unrelated editor/viewer code.
|
||||||
|
- Run `npm run build` after substantive changes (strict TypeScript).
|
||||||
|
|
||||||
|
## Common pitfalls
|
||||||
|
|
||||||
|
- Adding an `EditorObject` field but forgetting viewer render → object invisible in preview.
|
||||||
|
- Transform commits: use `transformCommit.ts` patterns so scale/rotation persist correctly.
|
||||||
|
- Mask/group: check `groupId`, `maskId`, `isMask` in both editor layers and `maskStyle.ts`.
|
||||||
|
- Page background: editor `documentSettings` ↔ viewer `resolvePageBackground`.
|
||||||
|
- Fullscreen/viewer chrome: `isViewerPath` hides app header/sidebar in `MainRouter.tsx`.
|
||||||
|
|
||||||
|
## Key routes
|
||||||
|
|
||||||
|
- Editor: `/editor/:id`
|
||||||
|
- Viewer: `/viewer/:id` or `/viewer/:businessSlug/:slug`
|
||||||
|
- Catalogue list: see `src/config/Paths.ts`
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
dist-ssr/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
.cache/
|
||||||
|
.vite/
|
||||||
|
|
||||||
|
# Coverage
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Environment / secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Editor / IDE
|
||||||
|
.vscode/
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
@@ -1,4 +1,13 @@
|
|||||||
VITE_API_URL = 'http://188.121.103.8:4141'
|
VITE_API_URL=https://dpage-api.danakcorp.com
|
||||||
# VITE_API_URL = 'http://188.121.103.8:4141'
|
# VITE_API_URL=http://192.168.99.131:4000
|
||||||
VITE_TOKEN_NAME = 'dpage-editor-t'
|
VITE_TOKEN_NAME=dpage-editor-t
|
||||||
VITE_REFRESH_TOKEN_NAME = 'dpage-editor-refresh-t'
|
VITE_REFRESH_TOKEN_NAME=dpage-editor-refresh-t
|
||||||
|
VITE_INVOICE_URL=https://console.danakcorp.com/receipts/
|
||||||
|
VITE_LOGIN_URL=https://console.danakcorp.com/auth/login
|
||||||
|
VITE_DESIGN_TIER_1_MAX=10
|
||||||
|
VITE_DESIGN_PRICE_TIER_1=700000
|
||||||
|
VITE_DESIGN_TIER_2_MAX=30
|
||||||
|
VITE_DESIGN_PRICE_TIER_2=500000
|
||||||
|
VITE_DESIGN_TIER_3_MAX=70
|
||||||
|
VITE_DESIGN_PRICE_TIER_3=400000
|
||||||
|
VITE_DESIGN_PRICE_TIER_4=350000
|
||||||
+40
-3
@@ -1,19 +1,56 @@
|
|||||||
FROM node:22-alpine AS builder
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
# Change Alpine repo
|
||||||
|
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
|
||||||
|
|
||||||
|
# Configure npm registry mirror (Liara)
|
||||||
|
ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
|
||||||
|
RUN npm config set registry https://package-mirror.liara.ir/repository/npm/
|
||||||
|
|
||||||
# Install tzdata to support timezone settings
|
# Install tzdata to support timezone settings
|
||||||
RUN apk add --no-cache tzdata
|
# RUN apk add --no-cache tzdata
|
||||||
|
|
||||||
# Set the timezone to Asia/Tehran
|
# Set the timezone to Asia/Tehran
|
||||||
RUN cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && echo "Asia/Tehran" > /etc/timezone
|
# RUN cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && echo "Asia/Tehran" > /etc/timezone
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
|
# Optional build-args (CapRover / docker build --build-arg). When unset, Vite reads .env from COPY.
|
||||||
|
ARG VITE_API_URL
|
||||||
|
ARG VITE_TOKEN_NAME
|
||||||
|
ARG VITE_REFRESH_TOKEN_NAME
|
||||||
|
ARG VITE_INVOICE_URL
|
||||||
|
ARG VITE_DESIGN_TIER_1_MAX
|
||||||
|
ARG VITE_DESIGN_PRICE_TIER_1
|
||||||
|
ARG VITE_DESIGN_TIER_2_MAX
|
||||||
|
ARG VITE_DESIGN_PRICE_TIER_2
|
||||||
|
ARG VITE_DESIGN_TIER_3_MAX
|
||||||
|
ARG VITE_DESIGN_PRICE_TIER_3
|
||||||
|
ARG VITE_DESIGN_PRICE_TIER_4
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm ci --legacy-peer-deps
|
RUN npm ci --legacy-peer-deps
|
||||||
COPY . ./
|
COPY . ./
|
||||||
RUN npm run build
|
RUN set -e; \
|
||||||
|
env_args=""; \
|
||||||
|
[ -n "$VITE_API_URL" ] && env_args="$env_args VITE_API_URL=$VITE_API_URL"; \
|
||||||
|
[ -n "$VITE_TOKEN_NAME" ] && env_args="$env_args VITE_TOKEN_NAME=$VITE_TOKEN_NAME"; \
|
||||||
|
[ -n "$VITE_REFRESH_TOKEN_NAME" ] && env_args="$env_args VITE_REFRESH_TOKEN_NAME=$VITE_REFRESH_TOKEN_NAME"; \
|
||||||
|
[ -n "$VITE_INVOICE_URL" ] && env_args="$env_args VITE_INVOICE_URL=$VITE_INVOICE_URL"; \
|
||||||
|
[ -n "$VITE_DESIGN_TIER_1_MAX" ] && env_args="$env_args VITE_DESIGN_TIER_1_MAX=$VITE_DESIGN_TIER_1_MAX"; \
|
||||||
|
[ -n "$VITE_DESIGN_PRICE_TIER_1" ] && env_args="$env_args VITE_DESIGN_PRICE_TIER_1=$VITE_DESIGN_PRICE_TIER_1"; \
|
||||||
|
[ -n "$VITE_DESIGN_TIER_2_MAX" ] && env_args="$env_args VITE_DESIGN_TIER_2_MAX=$VITE_DESIGN_TIER_2_MAX"; \
|
||||||
|
[ -n "$VITE_DESIGN_PRICE_TIER_2" ] && env_args="$env_args VITE_DESIGN_PRICE_TIER_2=$VITE_DESIGN_PRICE_TIER_2"; \
|
||||||
|
[ -n "$VITE_DESIGN_TIER_3_MAX" ] && env_args="$env_args VITE_DESIGN_TIER_3_MAX=$VITE_DESIGN_TIER_3_MAX"; \
|
||||||
|
[ -n "$VITE_DESIGN_PRICE_TIER_3" ] && env_args="$env_args VITE_DESIGN_PRICE_TIER_3=$VITE_DESIGN_PRICE_TIER_3"; \
|
||||||
|
[ -n "$VITE_DESIGN_PRICE_TIER_4" ] && env_args="$env_args VITE_DESIGN_PRICE_TIER_4=$VITE_DESIGN_PRICE_TIER_4"; \
|
||||||
|
eval "env$env_args npm run build"
|
||||||
|
|
||||||
FROM nginx:stable-alpine AS production-stage
|
FROM nginx:stable-alpine AS production-stage
|
||||||
|
|
||||||
|
# Change Alpine repo
|
||||||
|
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
|
||||||
|
|
||||||
COPY --from=builder /build/dist /usr/share/nginx/html
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
|
|
||||||
COPY --from=builder /build/nginx.con[f] /etc/nginx/conf.d/default.conf
|
COPY --from=builder /build/nginx.con[f] /etc/nginx/conf.d/default.conf
|
||||||
|
|||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/fav.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>dpage-editor</title>
|
<title>کاتالوگ دیجیتال | داناک</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Generated
+498
-3402
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -10,13 +10,14 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@liara/cli": "^9.4.7",
|
"@headlessui/react": "^2.2.10",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"formik": "^2.4.9",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"i18next": "^25.6.2",
|
"i18next": "^25.6.2",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
"moment-jalaali": "^0.10.4",
|
"moment-jalaali": "^0.10.4",
|
||||||
"page-flip": "^2.0.7",
|
"page-flip": "^2.0.7",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
|
"react-colorful": "^5.7.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
"react-i18next": "^16.3.0",
|
"react-i18next": "^16.3.0",
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"use-image": "^1.1.4",
|
"use-image": "^1.1.4",
|
||||||
|
"yup": "^1.7.1",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_13784_19054)">
|
||||||
|
<path d="M4 14.1001V10.0001C4.4 10.0001 4.9 9.90006 5.3 9.90006H17.7C23.9 9.90006 28.2 14.3001 28.1 20.2001C28.1 25.9001 23.6 30.2001 17.6 30.3001H8.3V33.8001C13.4 33.4001 19.7 34.9001 24.6 31.9001C29.3 29.0001 32.7 22.3001 31.3 17.2001C29.8 11.5001 24 6.70006 18.2 6.60006H4V2.00006H13.3C16.1425 1.91726 18.9868 2.08457 21.8 2.50006C25.8647 3.32115 29.5123 5.54253 32.1072 8.77703C34.7021 12.0115 36.0798 16.0541 36 20.2001C35.7 29.0001 29.5 36.5001 20.9 37.5001C15.4 38.1001 9.8 37.6001 4.1 37.6001V17.4001H8.2V25.6001C11.8 25.6001 16.2 25.9001 19.6 25.6001C21.9 25.3001 24.1 21.8001 23.8 19.2001C23.5353 17.8626 22.8484 16.6454 21.8403 15.7273C20.8323 14.8093 19.5563 14.2389 18.2 14.1001H4Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_13784_19054">
|
||||||
|
<rect width="32" height="35.8" fill="white" transform="translate(4 2)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 993 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,398 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const ROOT = path.join(__dirname, "../src/assets/fonts/irancell/fonts-editor");
|
||||||
|
const OUT_JSON = path.join(ROOT, "fonts.json");
|
||||||
|
const OUT_CSS = path.join(ROOT, "fonts.css");
|
||||||
|
|
||||||
|
const WEIGHT_LABELS = {
|
||||||
|
100: "Thin",
|
||||||
|
200: "Extra Light",
|
||||||
|
300: "Light",
|
||||||
|
400: "Regular",
|
||||||
|
500: "Medium",
|
||||||
|
600: "Semi Bold",
|
||||||
|
700: "Bold",
|
||||||
|
800: "Extra Bold",
|
||||||
|
900: "Black",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {Array<{ id: string; label: string; cssFamily: string; category: "persian" | "english"; faces: Array<{ weight: number; src: string; format?: string }> }>} */
|
||||||
|
const FONT_FAMILIES = [
|
||||||
|
{
|
||||||
|
id: "irancell",
|
||||||
|
label: "ایرانسل",
|
||||||
|
cssFamily: "editor-irancell",
|
||||||
|
category: "persian",
|
||||||
|
defaultWeight: "300",
|
||||||
|
faces: [
|
||||||
|
{ weight: 200, src: "../irancell-extralight.ttf" },
|
||||||
|
{ weight: 300, src: "../irancell-light.ttf" },
|
||||||
|
{ weight: 600, src: "../irancell-bold.ttf" },
|
||||||
|
{ weight: 700, src: "../irancell-bold.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "peyda",
|
||||||
|
label: "پیدا",
|
||||||
|
cssFamily: "editor-peyda",
|
||||||
|
category: "persian",
|
||||||
|
faces: [
|
||||||
|
{ weight: 100, src: "Persian Font/peyda/woff2/PeydaWeb-Thin.woff2", format: "woff2" },
|
||||||
|
{ weight: 200, src: "Persian Font/peyda/woff2/peydaWeb-extralight.woff2", format: "woff2" },
|
||||||
|
{ weight: 300, src: "Persian Font/peyda/woff2/peydaWeb-light.woff2", format: "woff2" },
|
||||||
|
{ weight: 400, src: "Persian Font/peyda/woff2/PeydaWeb-Regular.woff2", format: "woff2" },
|
||||||
|
{ weight: 500, src: "Persian Font/peyda/woff2/PeydaWeb-Medium.woff2", format: "woff2" },
|
||||||
|
{ weight: 600, src: "Persian Font/peyda/woff2/PeydaWeb-SemiBold.woff2", format: "woff2" },
|
||||||
|
{ weight: 700, src: "Persian Font/peyda/woff2/PeydaWeb-Bold.woff2", format: "woff2" },
|
||||||
|
{ weight: 800, src: "Persian Font/peyda/woff2/PeydaWeb-ExtraBold.woff2", format: "woff2" },
|
||||||
|
{ weight: 900, src: "Persian Font/peyda/woff2/PeydaWeb-Black.woff2", format: "woff2" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "iransans-fanum",
|
||||||
|
label: "ایرانسنس (اعداد فارسی)",
|
||||||
|
cssFamily: "editor-iransans-fanum",
|
||||||
|
category: "persian",
|
||||||
|
faces: [
|
||||||
|
{ weight: 200, src: "Persian Font/IRANSansWeb(FaNum)_UltraLight.ttf" },
|
||||||
|
{ weight: 300, src: "Persian Font/IRANSansWeb(FaNum)_Light.ttf" },
|
||||||
|
{ weight: 400, src: "Persian Font/IRANSansWeb(FaNum).ttf" },
|
||||||
|
{ weight: 500, src: "Persian Font/IRANSansWeb(FaNum)_Medium.ttf" },
|
||||||
|
{ weight: 700, src: "Persian Font/IRANSansWeb(FaNum)_Bold.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sahel",
|
||||||
|
label: "ساحل",
|
||||||
|
cssFamily: "editor-sahel",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/Sahel-VF.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "yekan",
|
||||||
|
label: "یکان",
|
||||||
|
cssFamily: "editor-yekan",
|
||||||
|
category: "persian",
|
||||||
|
faces: [
|
||||||
|
{ weight: 400, src: "Persian Font/Yekan.ttf" },
|
||||||
|
{ weight: 700, src: "Persian Font/BYekan.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "btehran",
|
||||||
|
label: "تهران",
|
||||||
|
cssFamily: "editor-btehran",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BTehran.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "byagut",
|
||||||
|
label: "یاقوت",
|
||||||
|
cssFamily: "editor-byagut",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BYagut.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "byas",
|
||||||
|
label: "یاس",
|
||||||
|
cssFamily: "editor-byas",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BYas.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bzar",
|
||||||
|
label: "زر",
|
||||||
|
cssFamily: "editor-bzar",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 700, src: "Persian Font/BZarBd.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bkoodak",
|
||||||
|
label: "کودک",
|
||||||
|
cssFamily: "editor-bkoodak",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 700, src: "Persian Font/BKoodakBold.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bbaran",
|
||||||
|
label: "باران",
|
||||||
|
cssFamily: "editor-bbaran",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BBaran.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "broya",
|
||||||
|
label: "رویا",
|
||||||
|
cssFamily: "editor-broya",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BRoya.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bmahsa",
|
||||||
|
label: "مهسا",
|
||||||
|
cssFamily: "editor-bmahsa",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BMahsa.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bmorvarid",
|
||||||
|
label: "مروارید",
|
||||||
|
cssFamily: "editor-bmorvarid",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BMorvarid.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bbc-nassim",
|
||||||
|
label: "نسیم",
|
||||||
|
cssFamily: "editor-bbc-nassim",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/BBCNassim.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "far-titr",
|
||||||
|
label: "تیتر",
|
||||||
|
cssFamily: "editor-far-titr",
|
||||||
|
category: "persian",
|
||||||
|
faces: [{ weight: 400, src: "Persian Font/Far_TitrDF.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "roboto",
|
||||||
|
label: "Roboto",
|
||||||
|
cssFamily: "editor-roboto",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 100, src: "English Font/Roboto-Thin-webfont.ttf" },
|
||||||
|
{ weight: 300, src: "English Font/Roboto-Light-webfont.ttf" },
|
||||||
|
{ weight: 400, src: "English Font/Roboto-Regular-webfont.ttf" },
|
||||||
|
{ weight: 500, src: "English Font/Roboto-Medium-webfont.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/Roboto-Bold-webfont.ttf" },
|
||||||
|
{ weight: 900, src: "English Font/Roboto-Black-webfont.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "roboto-condensed",
|
||||||
|
label: "Roboto Condensed",
|
||||||
|
cssFamily: "editor-roboto-condensed",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 400, src: "English Font/Roboto-Condensed-webfont.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/Roboto-BoldCondensed-webfont.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "vollkorn",
|
||||||
|
label: "Vollkorn",
|
||||||
|
cssFamily: "editor-vollkorn",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 400, src: "English Font/Vollkorn-Regular.ttf" },
|
||||||
|
{ weight: 500, src: "English Font/Vollkorn-Medium.ttf" },
|
||||||
|
{ weight: 600, src: "English Font/Vollkorn-Semibold.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/Vollkorn-Bold.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "caviar-dreams",
|
||||||
|
label: "Caviar Dreams",
|
||||||
|
cssFamily: "editor-caviar-dreams",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 400, src: "English Font/CaviarDreams.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/CaviarDreams_Bold.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "raleway",
|
||||||
|
label: "Raleway",
|
||||||
|
cssFamily: "editor-raleway",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 200, src: "English Font/raleway-extralight-webfont.ttf" },
|
||||||
|
{ weight: 400, src: "English Font/raleway-regular-webfont.ttf" },
|
||||||
|
{ weight: 500, src: "English Font/raleway-medium-webfont.ttf" },
|
||||||
|
{ weight: 600, src: "English Font/raleway-semibold-webfont.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/raleway-bold-webfont.ttf" },
|
||||||
|
{ weight: 800, src: "English Font/raleway-extrabold-webfont.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "proxima-nova",
|
||||||
|
label: "Proxima Nova",
|
||||||
|
cssFamily: "editor-proxima-nova",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 300, src: "English Font/proximanova-light-webfont.ttf" },
|
||||||
|
{ weight: 400, src: "English Font/proximanova-reg-webfont.ttf" },
|
||||||
|
{ weight: 600, src: "English Font/proximanova-semibold-webfont.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/proximanova-bold-webfont.ttf" },
|
||||||
|
{ weight: 800, src: "English Font/proximanova-extrabold-webfont.ttf" },
|
||||||
|
{ weight: 900, src: "English Font/proximanova-black-webfont.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "proxima-nova-condensed",
|
||||||
|
label: "Proxima Nova Condensed",
|
||||||
|
cssFamily: "editor-proxima-nova-condensed",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 300, src: "English Font/proximanovacond-light-webfont.ttf" },
|
||||||
|
{ weight: 600, src: "English Font/proximanovacond-semiboldit-webfont.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "din-pro",
|
||||||
|
label: "DIN Pro",
|
||||||
|
cssFamily: "editor-din-pro",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 400, src: "English Font/dinpro-regular.ttf" },
|
||||||
|
{ weight: 500, src: "English Font/dinpro-medium.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/dinpro-bold.ttf" },
|
||||||
|
{ weight: 900, src: "English Font/dinpro-black.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "din-alternate",
|
||||||
|
label: "DIN Alternate",
|
||||||
|
cssFamily: "editor-din-alternate",
|
||||||
|
category: "english",
|
||||||
|
faces: [
|
||||||
|
{ weight: 300, src: "English Font/din-lightalternate.ttf" },
|
||||||
|
{ weight: 400, src: "English Font/din-regularalternate.ttf" },
|
||||||
|
{ weight: 500, src: "English Font/din-mediumalternate.ttf" },
|
||||||
|
{ weight: 700, src: "English Font/din-boldalternate.ttf" },
|
||||||
|
{ weight: 900, src: "English Font/din-blackalternate.ttf" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "abril-fatface",
|
||||||
|
label: "Abril Fatface",
|
||||||
|
cssFamily: "editor-abril-fatface",
|
||||||
|
category: "english",
|
||||||
|
faces: [{ weight: 400, src: "English Font/AbrilFatface-Regular.otf", format: "opentype" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "anton",
|
||||||
|
label: "Anton",
|
||||||
|
cssFamily: "editor-anton",
|
||||||
|
category: "english",
|
||||||
|
faces: [{ weight: 400, src: "English Font/Anton.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bebas-neue",
|
||||||
|
label: "Bebas Neue",
|
||||||
|
cssFamily: "editor-bebas-neue",
|
||||||
|
category: "english",
|
||||||
|
faces: [{ weight: 400, src: "English Font/Bebas Neue.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "kepler-std",
|
||||||
|
label: "Kepler Std",
|
||||||
|
cssFamily: "editor-kepler-std",
|
||||||
|
category: "english",
|
||||||
|
faces: [{ weight: 400, src: "English Font/kepler-std.ttf" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "playfair-display",
|
||||||
|
label: "Playfair Display",
|
||||||
|
cssFamily: "editor-playfair-display",
|
||||||
|
category: "english",
|
||||||
|
faces: [{ weight: 900, src: "English Font/playfairdisplay-black-webfont.ttf" }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function encodeUrlSegment(segment) {
|
||||||
|
return segment
|
||||||
|
.split("/")
|
||||||
|
.map((part) => encodeURIComponent(part))
|
||||||
|
.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCss(families) {
|
||||||
|
const blocks = families.flatMap((family) =>
|
||||||
|
family.faces.map((face) => {
|
||||||
|
const formatSuffix = face.format ? ` format("${face.format}")` : "";
|
||||||
|
const url = encodeUrlSegment(face.src);
|
||||||
|
return `@font-face {
|
||||||
|
font-family: "${family.cssFamily}";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: ${face.weight};
|
||||||
|
src: url("${url}")${formatSuffix};
|
||||||
|
}`;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return `/* Auto-generated by scripts/generate-editor-fonts.mjs — do not edit manually */\n\n${blocks.join("\n\n")}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildJson(families) {
|
||||||
|
const fonts = families.map((family) => {
|
||||||
|
const srcToFace = new Map();
|
||||||
|
for (const face of family.faces) {
|
||||||
|
const existing = srcToFace.get(face.src);
|
||||||
|
if (!existing || face.weight > existing.weight) {
|
||||||
|
srcToFace.set(face.src, face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueWeightFaces = [...srcToFace.values()].sort(
|
||||||
|
(a, b) => a.weight - b.weight,
|
||||||
|
);
|
||||||
|
|
||||||
|
const uniqueWeights = uniqueWeightFaces.map((f) => f.weight);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: family.id,
|
||||||
|
label: family.label,
|
||||||
|
cssFamily: family.cssFamily,
|
||||||
|
category: family.category,
|
||||||
|
defaultWeight: family.defaultWeight
|
||||||
|
? String(family.defaultWeight)
|
||||||
|
: uniqueWeights.includes(400)
|
||||||
|
? "400"
|
||||||
|
: String(uniqueWeights[0]),
|
||||||
|
weights: uniqueWeightFaces.map((face) => ({
|
||||||
|
value: String(face.weight),
|
||||||
|
label: WEIGHT_LABELS[face.weight] || String(face.weight),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: 1,
|
||||||
|
fonts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifySources(families) {
|
||||||
|
const missing = [];
|
||||||
|
for (const family of families) {
|
||||||
|
for (const face of family.faces) {
|
||||||
|
const resolved = face.src.startsWith("../")
|
||||||
|
? path.join(ROOT, face.src)
|
||||||
|
: path.join(ROOT, face.src);
|
||||||
|
if (!fs.existsSync(resolved)) {
|
||||||
|
missing.push(resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missing.length) {
|
||||||
|
console.error("Missing font files:\n" + missing.map((m) => ` - ${m}`).join("\n"));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifySources(FONT_FAMILIES);
|
||||||
|
|
||||||
|
const json = buildJson(FONT_FAMILIES);
|
||||||
|
const css = buildCss(FONT_FAMILIES);
|
||||||
|
|
||||||
|
fs.writeFileSync(OUT_JSON, JSON.stringify(json, null, 2) + "\n");
|
||||||
|
fs.writeFileSync(OUT_CSS, css);
|
||||||
|
|
||||||
|
console.log(`Wrote ${OUT_JSON}`);
|
||||||
|
console.log(`Wrote ${OUT_CSS}`);
|
||||||
|
console.log(`Fonts: ${json.fonts.length}`);
|
||||||
+29
-69
@@ -1,81 +1,41 @@
|
|||||||
import { useEffect, useState, type FC } from 'react'
|
import FaJson from "@/langs/fa.json";
|
||||||
import './assets/fonts/irancell/style.css'
|
import { AuthProvider } from "@/context/AuthContext";
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import MainRouter from './router/MainRouter'
|
import i18next from "i18next";
|
||||||
import i18next from 'i18next'
|
import { type FC } from "react";
|
||||||
import FaJson from '@/langs/fa.json'
|
import { I18nextProvider } from "react-i18next";
|
||||||
import { I18nextProvider } from 'react-i18next'
|
import "react-multi-date-picker/styles/layouts/mobile.css";
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import ToastContainer from './components/Toast'
|
import "./assets/fonts/irancell/style.css";
|
||||||
import { getToken, setRefreshToken, setToken } from './config/func'
|
import "./assets/fonts/irancell/fonts-editor/fonts.css";
|
||||||
import "react-multi-date-picker/styles/layouts/mobile.css"
|
import ToastContainer from "./components/Toast";
|
||||||
|
import MainRouter from "./router/MainRouter";
|
||||||
|
|
||||||
i18next.init({
|
i18next.init({
|
||||||
interpolation: { escapeValue: false },
|
interpolation: { escapeValue: false },
|
||||||
lng: 'fa',
|
lng: "fa",
|
||||||
resources: {
|
resources: {
|
||||||
fa: {
|
fa: {
|
||||||
global: FaJson
|
global: FaJson,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const queryClient = new QueryClient()
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const App: FC = () => {
|
const App: FC = () => {
|
||||||
|
|
||||||
const [isLogin, setIsLogin] = useState<'checking' | 'isLogin' | 'isNotLogin'>('checking')
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// استخراج توکنها از URL در صورت وجود
|
|
||||||
const urlParams = new URLSearchParams(window.location.search)
|
|
||||||
const tokenFromUrl = urlParams.get('token')
|
|
||||||
const refreshTokenFromUrl = urlParams.get('refreshToken')
|
|
||||||
|
|
||||||
if (tokenFromUrl && refreshTokenFromUrl) {
|
|
||||||
// ذخیره توکنها (جایگزین کردن توکنهای قبلی اگر وجود داشته باشند)
|
|
||||||
setToken(tokenFromUrl)
|
|
||||||
setRefreshToken(refreshTokenFromUrl)
|
|
||||||
|
|
||||||
// حذف پارامترها از URL
|
|
||||||
const newUrl = window.location.pathname
|
|
||||||
window.history.replaceState({}, '', newUrl)
|
|
||||||
|
|
||||||
// تنظیم وضعیت لاگین و هدایت به صفحه اصلی
|
|
||||||
setIsLogin('isLogin')
|
|
||||||
// if (window.location.pathname === Paths.auth.login) {
|
|
||||||
// window.location.href = Paths.home
|
|
||||||
// }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = getToken()
|
|
||||||
if (token) {
|
|
||||||
setIsLogin('isLogin')
|
|
||||||
} else {
|
|
||||||
setIsLogin('isNotLogin')
|
|
||||||
if (window.location.href.split('auth').length === 1) {
|
|
||||||
// window.location.href = Paths.auth.login
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
console.log('isLogin', isLogin);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<I18nextProvider i18n={i18next}>
|
<AuthProvider>
|
||||||
<QueryClientProvider client={queryClient}>
|
<I18nextProvider i18n={i18next}>
|
||||||
<MainRouter />
|
<QueryClientProvider client={queryClient}>
|
||||||
<ToastContainer />
|
<MainRouter />
|
||||||
</QueryClientProvider>
|
<ToastContainer />
|
||||||
</I18nextProvider>
|
</QueryClientProvider>
|
||||||
|
</I18nextProvider>
|
||||||
|
</AuthProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user