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 index e9ab022..e2bd34f 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,5 @@ export default defineConfig([ ]) ``` "# thinktank" +"# thinktank" git init git add git commit -m "first commit" git branch -M main git remote add origin https://github.com/alirezaameria2-dev/thinktank.git git push -u origin main +"# thinktank" git init git add git commit -m "first commit" git branch -M main git remote add origin https://github.com/alirezaameria2-dev/thinktank.git git push -u origin main diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..ef614d2 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,22 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..25aab4c --- /dev/null +++ b/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + اندیشکده فولاد ایران — تحلیل راهبردی صنعت فولاد + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..699cda7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3448 @@ +{ + "name": "andishkade-foolad", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "andishkade-foolad", + "version": "0.0.0", + "dependencies": { + "@tanstack/react-query": "^5.100.14", + "apexcharts": "^5.13.0", + "clsx": "^2.1.1", + "dotted-map": "^3.1.0", + "framer-motion": "^12.40.0", + "jalaali-js": "^1.2.8", + "lucide-react": "^1.16.0", + "react": "^19.2.6", + "react-apexcharts": "^2.1.0", + "react-dom": "^19.2.6", + "react-router-dom": "^7.15.1", + "tailwind-merge": "^3.6.0", + "vazir-font": "^30.1.0", + "zustand": "^5.0.13" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@tailwindcss/vite": "^4.3.0", + "@types/jalaali-js": "^1.2.0", + "@types/node": "^24.12.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "tailwindcss": "^4.3.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.132.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", + "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", + "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", + "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.100.14", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.14.tgz", + "integrity": "sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.100.14", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.14.tgz", + "integrity": "sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.100.14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.3.5.tgz", + "integrity": "sha512-ba7+B0wzaS9GtERZOoXUZ6oW8IcIJHNQZf3c+tiD9ESjcsPO1Q/4qIJGTKl92nBLhhracHJxMWBM/U6hAVkaRg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.5", + "@turf/invariant": "7.3.5", + "@types/geojson": "^7946.0.10", + "point-in-polygon-hao": "^1.1.0", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.5.tgz", + "integrity": "sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.3.5.tgz", + "integrity": "sha512-ZVIvsBvjr8lO7WxC5zYNjRsjSDvyGvWkJMjuWaJjTU8x+1tmfNnw3gDX/TI2Sit83gcRYLYkNo23lB/udqx/Hg==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.5", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/jalaali-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/jalaali-js/-/jalaali-js-1.2.0.tgz", + "integrity": "sha512-DRQKjbfFe0/v3siCou9eFzYjDxJItkJpxbU+/CSptvoRqq2/VmTA6y8QvqK/VELKW6xX3qb174nLZlrFV9OT3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", + "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/apexcharts": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.13.0.tgz", + "integrity": "sha512-PJuXT6zdiCbv0IkX5cqkKFVIIh+9v3kqP9zsOHEGpIWi7DfTgzvfOKc8icw6G3/ulR3V1alDDUtOVH0zWCWGEQ==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotted-map": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/dotted-map/-/dotted-map-3.1.0.tgz", + "integrity": "sha512-E0z9o5IaTf44FnWHvbyg4QQcBwXgzZJr82HJASWb1dKUCULHfnlWKRpfe5EFHTk/yaoS1sQSLMyMrL6o74/sIw==", + "license": "MIT", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.3.4", + "proj4": "^2.20.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", + "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/framer-motion": { + "version": "12.40.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz", + "integrity": "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.40.0", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jalaali-js": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.8.tgz", + "integrity": "sha512-Jl/EwY84JwjW2wsWqeU4pNd22VNQ7EkjI36bDuLw31wH98WQW4fPjD0+mG7cdCK+Y8D6s9R3zLiQ3LaKu6bD8A==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.16.0.tgz", + "integrity": "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mgrs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", + "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/motion-dom": { + "version": "12.40.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz", + "integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/point-in-polygon-hao": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", + "license": "MIT", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proj4": { + "version": "2.20.8", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.20.8.tgz", + "integrity": "sha512-1C8sfT4xY4PAPwk0MroFBTGF4R4bzDXdmPQTGYVLsoNssrZ9odzObxS2dTeGBty8jW8KO7h16C1Hs2JP+ctfFw==", + "license": "MIT", + "dependencies": { + "mgrs": "1.0.0", + "wkt-parser": "^1.5.5" + }, + "funding": { + "url": "https://github.com/sponsors/ahocevar" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-apexcharts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-2.1.0.tgz", + "integrity": "sha512-xrmeTKRKHh3cvvLc8SasqFjlOgIqGpyHc81qjnRtcjUM0Fu7qEjgVRWGPokGFjqhwRZVgEym8zmuEyYr5LMYIg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "apexcharts": ">=5.10.1", + "react": ">=16.8.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz", + "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.1.tgz", + "integrity": "sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg==", + "license": "MIT", + "dependencies": { + "react-router": "7.15.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/rolldown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.132.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.2", + "@rolldown/binding-darwin-arm64": "1.0.2", + "@rolldown/binding-darwin-x64": "1.0.2", + "@rolldown/binding-freebsd-x64": "1.0.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", + "@rolldown/binding-linux-arm64-gnu": "1.0.2", + "@rolldown/binding-linux-arm64-musl": "1.0.2", + "@rolldown/binding-linux-ppc64-gnu": "1.0.2", + "@rolldown/binding-linux-s390x-gnu": "1.0.2", + "@rolldown/binding-linux-x64-gnu": "1.0.2", + "@rolldown/binding-linux-x64-musl": "1.0.2", + "@rolldown/binding-openharmony-arm64": "1.0.2", + "@rolldown/binding-wasm32-wasi": "1.0.2", + "@rolldown/binding-win32-arm64-msvc": "1.0.2", + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vazir-font": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/vazir-font/-/vazir-font-30.1.0.tgz", + "integrity": "sha512-XN2Uprw/Q3QhAAApITykf+v0l4p4FpqvIh0h2XDSJRjwUiYeDpwQxECQF/93ZOCDO2t8haBi6BZZqO8sifYNRg==", + "deprecated": "vazir-font no longer supported. Use vazirmatn instead.", + "license": "OFL" + }, + "node_modules/vite": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", + "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.2", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wkt-parser": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.5.5.tgz", + "integrity": "sha512-/zMYi94/7D7fxcOSlVmWn6vnOMj3Gq5d1xvVjaYOS9n6h0qOJ4I7YYVxBWYcH1vq9+suhqzXkn05Yx47zQNUIA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ahocevar" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.13.tgz", + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9594e4d --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "andishkade-foolad", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "^5.100.14", + "apexcharts": "^5.13.0", + "clsx": "^2.1.1", + "dotted-map": "^3.1.0", + "framer-motion": "^12.40.0", + "jalaali-js": "^1.2.8", + "lucide-react": "^1.16.0", + "react": "^19.2.6", + "react-apexcharts": "^2.1.0", + "react-dom": "^19.2.6", + "react-router-dom": "^7.15.1", + "tailwind-merge": "^3.6.0", + "vazir-font": "^30.1.0", + "zustand": "^5.0.13" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@tailwindcss/vite": "^4.3.0", + "@types/jalaali-js": "^1.2.0", + "@types/node": "^24.12.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "tailwindcss": "^4.3.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..51bb508 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/fonts/Vazir-Black.woff2 b/public/fonts/Vazir-Black.woff2 new file mode 100644 index 0000000..d6f74d4 Binary files /dev/null and b/public/fonts/Vazir-Black.woff2 differ diff --git a/public/fonts/Vazir-Bold.woff2 b/public/fonts/Vazir-Bold.woff2 new file mode 100644 index 0000000..f678973 Binary files /dev/null and b/public/fonts/Vazir-Bold.woff2 differ diff --git a/public/fonts/Vazir-Light.woff2 b/public/fonts/Vazir-Light.woff2 new file mode 100644 index 0000000..62ff68e Binary files /dev/null and b/public/fonts/Vazir-Light.woff2 differ diff --git a/public/fonts/Vazir-Medium.woff2 b/public/fonts/Vazir-Medium.woff2 new file mode 100644 index 0000000..b6bdfa9 Binary files /dev/null and b/public/fonts/Vazir-Medium.woff2 differ diff --git a/public/fonts/Vazir-Regular.woff2 b/public/fonts/Vazir-Regular.woff2 new file mode 100644 index 0000000..cb95c4f Binary files /dev/null and b/public/fonts/Vazir-Regular.woff2 differ diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..52bf47d --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,4 @@ +// App.tsx is no longer the entry point. +// Routing is handled by src/app/router.tsx → src/main.tsx +// This file is kept as a placeholder. +export default function App() { return null } diff --git a/src/app/layout/Footer.tsx b/src/app/layout/Footer.tsx new file mode 100644 index 0000000..7c9018b --- /dev/null +++ b/src/app/layout/Footer.tsx @@ -0,0 +1,196 @@ +import { useState } from 'react' + +function SocialIcon({ label }: { label: string }) { + const [hovered, setHovered] = useState(false) + return ( +
setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={{ + width: 28, height: 28, + border: `1px solid ${hovered ? 'rgba(255,255,255,0.4)' : 'rgba(255,255,255,0.12)'}`, + display: 'flex', alignItems: 'center', justifyContent: 'center', + fontSize: 10, fontWeight: 700, + color: hovered ? '#fff' : 'rgba(255,255,255,0.4)', + cursor: 'pointer', + transition: 'border-color 150ms, color 150ms', + flexShrink: 0, + }} + > + {label} +
+ ) +} + +function FooterLink({ href, label }: { href: string; label: string }) { + const [hovered, setHovered] = useState(false) + return ( +
  • + setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={{ + fontSize: 13, + color: hovered ? '#fff' : 'rgba(255,255,255,0.55)', + textDecoration: 'none', + cursor: 'pointer', + transition: 'color 150ms', + display: 'inline-block', + lineHeight: 1.4, + }} + > + {label} + +
  • + ) +} + +const COLUMNS = [ + { + heading: 'گزارش‌ها', + links: [ + { label: 'گزارش‌های فصلی', href: '#' }, + { label: 'Flash Reports', href: '#' }, + { label: 'گزارش‌های ریسک', href: '#' }, + { label: 'گزارش رایگان', href: '#' }, + { label: 'آرشیو کامل', href: '#' }, + ], + }, + { + heading: 'اندیشکده', + links: [ + { label: 'درباره ما', href: '#' }, + { label: 'تیم تحریریه', href: '#' }, + { label: 'اشتراک سازمانی', href: '#' }, + { label: 'همکاری با ما', href: '#' }, + { label: 'تماس', href: '#' }, + ], + }, + { + heading: 'منابع', + links: [ + { label: 'داشبورد بازار', href: '#' }, + { label: 'رویدادها', href: '#' }, + { label: 'اسکنر جهانی', href: '#' }, + { label: 'خبرنامه', href: '#' }, + { label: 'درگاه پرداخت', href: '#' }, + ], + }, +] + +export function Footer() { + return ( + + ) +} diff --git a/src/app/layout/Header.tsx b/src/app/layout/Header.tsx new file mode 100644 index 0000000..fc57a07 --- /dev/null +++ b/src/app/layout/Header.tsx @@ -0,0 +1,815 @@ +import { useState, useEffect, useRef } from 'react' +import type { CSSProperties } from 'react' +import { NavLink } from 'react-router-dom' +import { Search, Menu, X } from 'lucide-react' +import { motion, AnimatePresence } from 'framer-motion' + +/* ───────────────────────────────────────────────────────── + DESIGN TOKENS (mirrors CSS variables for inline styles) +───────────────────────────────────────────────────────── */ +const T = { + paper: 'var(--paper)', + paper2: 'var(--paper-2)', + ink: 'var(--ink)', + ink2: 'var(--ink-2)', + ink3: 'var(--ink-3)', + ink4: 'var(--ink-4)', + ink5: 'var(--ink-5)', + ruleThin: 'var(--rule-thin)', + red: 'var(--red)', +} as const + +/* ───────────────────────────────────────────────────────── + DATA +───────────────────────────────────────────────────────── */ +const NAV_ITEMS = [ + { label: 'گزارش‌ها', to: '/reports' }, + { label: 'گزارش ویژه', to: '/special' }, + { label: 'ریسک‌ها', to: '/risks' }, + { label: 'رویدادها', to: '/events' }, + { label: 'اسکنر جهانی', to: '/scanner' }, + { label: 'تیم تحریریه', to: '/team' }, + { label: 'داشبورد بازار', to: '/market' }, + { label: 'آرشیو', to: '/archive' }, +] + +const COMPACT_NAV = [ + { label: 'گزارش‌ها', to: '/reports' }, + { label: 'بازار', to: '/market' }, + { label: 'رویدادها', to: '/events' }, + { label: 'ریسک', to: '/risks' }, +] + +const META_LEFT = ['درباره ما', 'کارشناسان', 'همکاری', 'رسانه'] + +/* ───────────────────────────────────────────────────────── + HELPERS +───────────────────────────────────────────────────────── */ + +/** Social icon box */ +function SocialIcon({ label, href }: { label: string; href: string }) { + const [hovered, setHovered] = useState(false) + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {label} + + ) +} + +/** Full nav link (expanded navbar) */ +function FullNavItem({ + to, + label, + isLast, +}: { + to: string + label: string + isLast: boolean +}) { + const [hovered, setHovered] = useState(false) + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={({ isActive }: { isActive: boolean }): CSSProperties => ({ + padding: '12px 20px', + fontSize: 12, + fontWeight: 600, + color: isActive || hovered ? T.paper : T.ink, + background: isActive ? T.red : hovered ? T.ink : 'transparent', + textDecoration: 'none', + borderRight: !isLast ? `1px solid ${T.ruleThin}` : 'none', + display: 'flex', + alignItems: 'center', + transition: 'background 150ms, color 150ms', + whiteSpace: 'nowrap', + letterSpacing: '0.2px', + })} + > + {label} + + ) +} + +/** Compact nav link */ +function CompactNavItem({ + to, + label, + isFirst, +}: { + to: string + label: string + isFirst: boolean +}) { + const [hovered, setHovered] = useState(false) + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={({ isActive }: { isActive: boolean }): CSSProperties => ({ + fontSize: 12, + fontWeight: 600, + color: isActive || hovered ? T.paper : T.ink, + background: isActive ? T.ink : hovered ? T.paper2 : 'transparent', + textDecoration: 'none', + padding: '0 14px', + height: 56, + display: 'flex', + alignItems: 'center', + borderRight: `1px solid ${T.ruleThin}`, + borderLeft: isFirst ? `1px solid ${T.ruleThin}` : 'none', + whiteSpace: 'nowrap', + transition: 'background 120ms, color 120ms', + })} + > + {label} + + ) +} + +/** Drawer nav link */ +function DrawerNavItem({ + to, + label, + onClose, +}: { + to: string + label: string + onClose: () => void +}) { + const [hovered, setHovered] = useState(false) + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={({ isActive }: { isActive: boolean }): CSSProperties => ({ + display: 'block', + padding: '13px 20px', + fontSize: 14, + fontWeight: 600, + color: isActive || hovered ? T.paper : T.ink, + background: isActive ? T.ink : hovered ? T.paper2 : 'transparent', + textDecoration: 'none', + borderBottom: `1px solid ${T.ruleThin}`, + transition: 'background 120ms, color 120ms', + })} + > + {label} + + ) +} + +/* ───────────────────────────────────────────────────────── + MAIN COMPONENT +───────────────────────────────────────────────────────── */ +export default function Header() { + const [compact, setCompact] = useState(false) + const [mobileOpen, setMobileOpen] = useState(false) + const [isMobile, setIsMobile] = useState(false) + const searchRef = useRef(null) + + /* scroll listener */ + useEffect(() => { + const onScroll = () => setCompact(window.scrollY > 80) + window.addEventListener('scroll', onScroll, { passive: true }) + return () => window.removeEventListener('scroll', onScroll) + }, []) + + /* viewport listener */ + useEffect(() => { + const mq = window.matchMedia('(max-width: 767px)') + const handler = (e: MediaQueryListEvent | MediaQueryList) => setIsMobile(e.matches) + handler(mq) + mq.addEventListener('change', handler) + return () => mq.removeEventListener('change', handler) + }, []) + + /* body scroll lock */ + useEffect(() => { + document.body.style.overflow = mobileOpen ? 'hidden' : '' + return () => { document.body.style.overflow = '' } + }, [mobileOpen]) + + const showCompact = compact || isMobile + + return ( +
    + {/* ════════════════════════════════════════════ + EXPANDED STATE + ════════════════════════════════════════════ */} + + {!showCompact && ( + + + {/* ── TOP META BAR ─────────────────────────── */} +
    + {/* Right: login / language */} + + + {/* Center: date */} + + شنبه ۲۱ بهمن ۱۴۰۳ · دوره دوازدهم + + + {/* Left: secondary nav */} + +
    + + {/* ── MASTHEAD MAIN ────────────────────────── */} +
    + {/* Left col */} +
    +
    +

    + تحلیل مستقل · سیاست‌گذاری +

    +

    + ۲۸۰ گزارش منتشرشده · ۴۵ متخصص +

    +
    +
    + + + +
    +
    + + {/* Center col */} +
    +

    + اندیشکده فولاد ایران +

    + +

    + IRAN STEEL POLICY INSTITUTE +

    +
    + + {/* Right col */} +
    +
    + +
    +
    + اشتراک سازمانی + ورود +
    +
    +
    + + {/* ── NAV BAR ──────────────────────────────── */} + +
    + )} +
    + + {/* ════════════════════════════════════════════ + COMPACT STATE + ════════════════════════════════════════════ */} + + {showCompact && ( + +
    + {/* Brand */} +
    + + اندیشکده فولاد ایران + + + IRAN STEEL POLICY INSTITUTE + +
    + + {/* Desktop compact nav */} + {!isMobile ? ( + + ) : ( + /* Mobile hamburger */ + + )} +
    +
    + )} +
    + + {/* ════════════════════════════════════════════ + MOBILE DRAWER + ════════════════════════════════════════════ */} + + {mobileOpen && ( + <> + {/* Overlay */} + setMobileOpen(false)} + style={{ + position: 'fixed', + inset: 0, + background: 'rgba(26,23,18,0.55)', + zIndex: 100, + }} + /> + + {/* Drawer panel */} + + {/* Drawer header */} +
    + + اندیشکده فولاد ایران + + +
    + + {/* Nav list */} + + + {/* Footer CTAs */} +
    + اشتراک سازمانی + ورود +
    + {META_LEFT.map((item) => ( + + {item} + + ))} +
    +
    +
    + + )} +
    +
    + ) +} + +/* ───────────────────────────────────────────────────────── + MICRO BUTTON COMPONENTS (hover via useState — clean) +───────────────────────────────────────────────────────── */ +function FilledButton({ + children, + style: extraStyle, +}: { + children: React.ReactNode + style?: CSSProperties +}) { + const [hovered, setHovered] = useState(false) + return ( + + ) +} + +function OutlineButton({ + children, + style: extraStyle, +}: { + children: React.ReactNode + style?: CSSProperties +}) { + const [hovered, setHovered] = useState(false) + return ( + + ) +} + +function RedCtaButton({ + children, + style: extraStyle, +}: { + children: React.ReactNode + style?: CSSProperties +}) { + const [hovered, setHovered] = useState(false) + return ( + + ) +} diff --git a/src/app/layout/MarketStrip.tsx b/src/app/layout/MarketStrip.tsx new file mode 100644 index 0000000..cf65c9e --- /dev/null +++ b/src/app/layout/MarketStrip.tsx @@ -0,0 +1,135 @@ +import { marketPrices } from '@/data/market'; +import type { MarketPrice } from '@/data/market'; + +function formatValue(item: MarketPrice): string { + return item.value.toLocaleString('fa-IR'); +} + +function formatChange(item: MarketPrice): string { + const abs = Math.abs(item.change).toLocaleString('fa-IR'); + return `${abs} (${Math.abs(item.changePercent).toFixed(1)}٪)`; +} + +function TickerItem({ item }: { item: MarketPrice }) { + const changeColor = + item.trend === 'up' + ? '#7dbf8a' + : item.trend === 'down' + ? '#e07070' + : 'rgba(255,255,255,0.5)'; + + const arrow = + item.trend === 'up' ? '↑' : item.trend === 'down' ? '↓' : '—'; + + return ( + + + {item.name} + + + {formatValue(item)}{' '} + + {item.unit} + + + + {arrow} {formatChange(item)} + + + ); +} + +export function MarketStrip() { + return ( +
    + {/* Live tag */} +
    + بازار زنده +
    + + {/* Ticker area */} +
    +
    + {/* Original set */} + {marketPrices.map((item, i) => ( + + ))} + {/* Duplicate for seamless loop */} + {marketPrices.map((item, i) => ( + + ))} +
    +
    +
    + ); +} diff --git a/src/app/layout/RootLayout.tsx b/src/app/layout/RootLayout.tsx new file mode 100644 index 0000000..7b1718b --- /dev/null +++ b/src/app/layout/RootLayout.tsx @@ -0,0 +1,17 @@ +import { Outlet } from 'react-router-dom' +import Header from './Header' +import { Footer } from './Footer' +import { MarketStrip } from './MarketStrip' + +export default function RootLayout() { + return ( +
    +
    + +
    + +
    +
    +
    + ) +} diff --git a/src/app/router.tsx b/src/app/router.tsx new file mode 100644 index 0000000..9f07893 --- /dev/null +++ b/src/app/router.tsx @@ -0,0 +1,33 @@ +import { createBrowserRouter } from 'react-router-dom' +import RootLayout from './layout/RootLayout' +import Home from '@/pages/Home/Home' +import Reports from '@/pages/Reports/Reports' +import ReportDetail from '@/pages/ReportDetail/ReportDetail' +import Market from '@/pages/Market/Market' +import Subscribe from '@/pages/Subscribe/Subscribe' +import Events from '@/pages/Events/Events' +import Team from '@/pages/Team/Team' +import Archive from '@/pages/Archive/Archive' +import Special from '@/pages/Special/Special' +import Risks from '@/pages/Risks/Risks' +import Scanner from '@/pages/Scanner/Scanner' + +export const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { index: true, element: }, + { path: 'reports', element: }, + { path: 'reports/:id', element: }, + { path: 'market', element: }, + { path: 'subscribe', element: }, + { path: 'events', element: }, + { path: 'team', element: }, + { path: 'archive', element: }, + { path: 'special', element: }, + { path: 'risks', element: }, + { path: 'scanner', element: }, + ], + }, +]) diff --git a/src/assets/hero.png b/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/src/assets/hero.png differ 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/assets/vite.svg b/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx new file mode 100644 index 0000000..3f1cff3 --- /dev/null +++ b/src/components/ui/Badge.tsx @@ -0,0 +1,56 @@ + +type BadgeVariant = 'default' | 'red' | 'amber' | 'green' | 'outline'; + +interface BadgeProps { + children: React.ReactNode; + variant?: BadgeVariant; + className?: string; +} + +const variantStyles: Record = { + default: { + backgroundColor: 'var(--ink)', + color: 'var(--paper)', + border: '1px solid var(--ink)', + }, + red: { + backgroundColor: 'var(--red)', + color: 'var(--paper)', + border: '1px solid var(--red)', + }, + amber: { + backgroundColor: 'var(--amber)', + color: 'var(--ink)', + border: '1px solid var(--amber)', + }, + green: { + backgroundColor: 'var(--green-ink)', + color: 'var(--paper)', + border: '1px solid var(--green-ink)', + }, + outline: { + backgroundColor: 'transparent', + color: 'var(--ink)', + border: '1px solid var(--ink)', + }, +}; + +export function Badge({ children, variant = 'default', className = '' }: BadgeProps) { + return ( + + {children} + + ); +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000..f35eda6 --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react' +import type { CSSProperties, ReactNode, MouseEvent } from 'react' + +type ButtonVariant = 'filled' | 'outline' | 'paper' | 'ghost'; +type ButtonSize = 'sm' | 'md' | 'lg'; + +interface ButtonProps { + children: ReactNode; + variant?: ButtonVariant; + size?: ButtonSize; + className?: string; + onClick?: (e: MouseEvent) => void; + type?: 'button' | 'submit' | 'reset'; + disabled?: boolean; + asChild?: boolean; +} + +const sizeStyles: Record = { + sm: { padding: '9px 20px', fontSize: '9px' }, + md: { padding: '11px 24px', fontSize: '11px' }, + lg: { padding: '12px 28px', fontSize: '12px' }, +}; + +const baseStyle: CSSProperties = { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + gap: '6px', + borderRadius: 0, + fontFamily: 'Vazir, sans-serif', + fontWeight: 700, + letterSpacing: '0.5px', + textTransform: 'uppercase', + cursor: 'pointer', + transition: 'background-color 200ms, color 200ms, border-color 200ms', + outline: 'none', + border: '1px solid transparent', + whiteSpace: 'nowrap', +}; + +const variantBase: Record = { + filled: { + backgroundColor: 'var(--ink)', + color: 'var(--paper)', + borderColor: 'var(--ink)', + }, + outline: { + backgroundColor: 'transparent', + color: 'var(--ink)', + borderColor: 'var(--ink)', + }, + paper: { + backgroundColor: 'var(--paper)', + color: 'var(--ink)', + borderColor: 'var(--paper)', + }, + ghost: { + backgroundColor: 'transparent', + color: 'var(--ink)', + borderColor: 'transparent', + }, +}; + +export function Button({ + children, + variant = 'filled', + size = 'md', + className = '', + onClick, + type = 'button', + disabled = false, +}: ButtonProps) { + const [hovered, setHovered] = useState(false); + + const getHoverStyle = (): CSSProperties => { + if (!hovered || disabled) return {}; + switch (variant) { + case 'filled': + return { backgroundColor: 'var(--ink-2)' }; + case 'outline': + return { backgroundColor: 'var(--ink)', color: 'var(--paper)' }; + case 'paper': + return { backgroundColor: 'var(--paper-2)' }; + case 'ghost': + return { backgroundColor: 'var(--paper-2)' }; + default: + return {}; + } + }; + + return ( + + ); +} diff --git a/src/components/ui/PullQuote.tsx b/src/components/ui/PullQuote.tsx new file mode 100644 index 0000000..4fe9e09 --- /dev/null +++ b/src/components/ui/PullQuote.tsx @@ -0,0 +1,44 @@ + +interface PullQuoteProps { + text: React.ReactNode; + source?: string; + className?: string; +} + +export function PullQuote({ text, source, className = '' }: PullQuoteProps) { + return ( +
    +

    + {text} +

    + {source && ( +
    + {source} +
    + )} +
    + ); +} diff --git a/src/components/ui/Rule.tsx b/src/components/ui/Rule.tsx new file mode 100644 index 0000000..5cba6fb --- /dev/null +++ b/src/components/ui/Rule.tsx @@ -0,0 +1,44 @@ + +type RuleWeight = 'heavy' | 'mid' | 'light' | 'red'; + +interface RuleProps { + weight?: RuleWeight; + className?: string; +} + +const styles: Record = { + heavy: { + borderTop: '3px solid var(--ink)', + borderBottom: 'none', + margin: 0, + width: '100%', + }, + mid: { + borderTop: '1px solid var(--ink)', + borderBottom: 'none', + margin: 0, + width: '100%', + }, + light: { + borderTop: '1px solid var(--rule-thin)', + borderBottom: 'none', + margin: 0, + width: '100%', + }, + red: { + borderTop: '3px solid var(--red)', + borderBottom: 'none', + margin: 0, + width: '100%', + }, +}; + +export function Rule({ weight = 'mid', className = '' }: RuleProps) { + return ( + + ); +} diff --git a/src/components/ui/SectionBanner.tsx b/src/components/ui/SectionBanner.tsx new file mode 100644 index 0000000..41401eb --- /dev/null +++ b/src/components/ui/SectionBanner.tsx @@ -0,0 +1,91 @@ +import { ArrowLeft } from 'lucide-react'; + +type LabelVariant = 'ink' | 'red'; + +interface SectionBannerLink { + text: string; + to: string; +} + +interface SectionBannerProps { + label: string; + labelVariant?: LabelVariant; + link?: SectionBannerLink; +} + +export function SectionBanner({ + label, + labelVariant = 'ink', + link, +}: SectionBannerProps) { + const labelBg = labelVariant === 'red' ? 'var(--red)' : 'var(--ink)'; + + return ( +
    + {/* Label block */} + + {label} + + + {/* Rule fill */} + + ); +} diff --git a/src/components/ui/Stamp.tsx b/src/components/ui/Stamp.tsx new file mode 100644 index 0000000..c88bc0e --- /dev/null +++ b/src/components/ui/Stamp.tsx @@ -0,0 +1,37 @@ + +type StampColor = 'red' | 'ink'; + +interface StampProps { + children: React.ReactNode; + color?: StampColor; + tilt?: boolean; + className?: string; +} + +export function Stamp({ + children, + color = 'red', + tilt = true, + className = '', +}: StampProps) { + const colorValue = color === 'red' ? 'var(--red)' : 'var(--ink)'; + + return ( + + {children} + + ); +} diff --git a/src/components/ui/StatCard.tsx b/src/components/ui/StatCard.tsx new file mode 100644 index 0000000..b1c5118 --- /dev/null +++ b/src/components/ui/StatCard.tsx @@ -0,0 +1,40 @@ + +interface StatCardProps { + number: string; + label: string; + className?: string; +} + +export function StatCard({ number, label, className = '' }: StatCardProps) { + return ( +
    +
    + {number} +
    +
    + {label} +
    +
    + ); +} diff --git a/src/components/ui/apple-cards-carousel.tsx b/src/components/ui/apple-cards-carousel.tsx new file mode 100644 index 0000000..14d6b40 --- /dev/null +++ b/src/components/ui/apple-cards-carousel.tsx @@ -0,0 +1,280 @@ +import { + useEffect, + useRef, + useState, + createContext, + useContext, +} from 'react' +import type { ReactNode } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { X } from 'lucide-react' +import { clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +function cn(...inputs: Parameters) { + return twMerge(clsx(inputs)) +} + +/* ─── types ─────────────────────────────────────────── */ +export interface CardData { + src: string + title: string + category: string + content: ReactNode +} + +interface CarouselContextType { + onCardClose: (index: number) => void + currentIndex: number +} + +/* ─── context ───────────────────────────────────────── */ +const CarouselContext = createContext({ + onCardClose: () => {}, + currentIndex: 0, +}) + +/* ─── Carousel ──────────────────────────────────────── */ +export function Carousel({ + items, + initialScroll = 0, +}: { + items: ReactNode[] + initialScroll?: number +}) { + const carouselRef = useRef(null) + const [canScrollLeft, setCanScrollLeft] = useState(false) + const [canScrollRight, setCanScrollRight] = useState(true) + const [currentIndex, setCurrentIndex] = useState(0) + + useEffect(() => { + if (carouselRef.current) { + carouselRef.current.scrollLeft = initialScroll + checkScrollability() + } + }, [initialScroll]) + + function checkScrollability() { + if (!carouselRef.current) return + const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current + setCanScrollLeft(scrollLeft > 0) + setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 1) + } + + function scrollLeft() { carouselRef.current?.scrollBy({ left: -340, behavior: 'smooth' }) } + function scrollRight() { carouselRef.current?.scrollBy({ left: 340, behavior: 'smooth' }) } + + function handleCardClose(index: number) { + if (!carouselRef.current) return + const cardWidth = window.innerWidth < 768 ? 230 : 350 + carouselRef.current.scrollTo({ left: (cardWidth + 16) * (index + 1), behavior: 'smooth' }) + setCurrentIndex(index) + } + + return ( + +
    + {/* Scrollable track */} +
    +
    + {items.map((item, index) => ( + + {item} + + ))} +
    +
    + + {/* Arrow buttons */} +
    + + +
    +
    +
    + ) +} + +/* ─── Card ──────────────────────────────────────────── */ +export function Card({ + card, + index, + layout = false, +}: { + card: CardData + index: number + layout?: boolean +}) { + const [open, setOpen] = useState(false) + const { onCardClose } = useContext(CarouselContext) + + useEffect(() => { + function onKey(e: KeyboardEvent) { if (e.key === 'Escape') handleClose() } + document.body.style.overflow = open ? 'hidden' : 'auto' + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [open]) + + function handleOpen() { setOpen(true) } + function handleClose() { setOpen(false); onCardClose(index) } + + return ( + <> + {/* ── Modal ── */} + + {open && ( + /* Outer: scroll host */ +
    + {/* Backdrop — click to close */} + + + {/* Centering wrapper */} +
    + e.stopPropagation()} + > + {/* Close button */} + + + {/* Hero image strip */} +
    + {card.title} +
    + {/* inset-x-0 ensures text never overflows past modal edges */} +
    +

    + {card.category} +

    +

    + {card.title} +

    +
    +
    + + {/* Body */} +
    + {card.content} +
    + +
    +
    + )} + + + {/* ── Card thumbnail ── */} + + {/* Bottom-to-top gradient so text is readable — tall enough to cover 2 lines */} +
    + + {/* Text — bottom of card, small */} +
    +

    + {card.category} +

    +

    + {card.title} +

    +
    + + {/* Background image */} + + + + ) +} + +/* ─── BlurImage ─────────────────────────────────────── */ +function BlurImage({ src, alt, className }: { src: string; alt: string; className?: string }) { + const [loaded, setLoaded] = useState(false) + return ( + {alt} setLoaded(true)} + className={cn('transition-[filter] duration-500', loaded ? 'blur-0' : 'blur-md', className)} + /> + ) +} + +/* ─── useOutsideClick ───────────────────────────────── */ +export function useOutsideClick( + ref: React.RefObject, + callback: () => void, +) { + useEffect(() => { + function listener(e: MouseEvent | TouchEvent) { + if (!ref.current || ref.current.contains(e.target as Node)) return + callback() + } + document.addEventListener('mousedown', listener) + document.addEventListener('touchstart', listener) + return () => { + document.removeEventListener('mousedown', listener) + document.removeEventListener('touchstart', listener) + } + }, [ref, callback]) +} diff --git a/src/components/ui/bento-grid.tsx b/src/components/ui/bento-grid.tsx new file mode 100644 index 0000000..3f21e97 --- /dev/null +++ b/src/components/ui/bento-grid.tsx @@ -0,0 +1,131 @@ +import type { ReactNode, CSSProperties } from 'react' +import { clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +function cn(...inputs: Parameters) { + return twMerge(clsx(inputs)) +} + +/* ─── BentoGrid ─────────────────────────────────────────── */ +export function BentoGrid({ + children, + className, +}: { + children: ReactNode + className?: string +}) { + return ( +
    + {children} +
    + ) +} + +/* ─── BentoGridItem ──────────────────────────────────────── */ +export interface BentoGridItemProps { + title: string + description?: string + header?: ReactNode /* visual block above content */ + icon?: ReactNode + tag?: string + tagColor?: string /* CSS color value */ + accentLine?: boolean /* left/top colored accent rule */ + className?: string + style?: CSSProperties + onClick?: () => void +} + +export function BentoGridItem({ + title, + description, + header, + icon, + tag, + tagColor = 'var(--red)', + accentLine = false, + className, + style, + onClick, +}: BentoGridItemProps) { + return ( +
    + {/* Accent bar */} + {accentLine && ( +
    + )} + + {/* Optional visual header block */} + {header &&
    {header}
    } + + {/* Tag */} + {tag && ( +
    + {tag} +
    + )} + + {/* Icon + body */} +
    + {icon &&
    {icon}
    } + +

    + {title} +

    + + {description && ( +

    + {description} +

    + )} +
    +
    + ) +} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts new file mode 100644 index 0000000..67fffda --- /dev/null +++ b/src/components/ui/index.ts @@ -0,0 +1,7 @@ +export { Rule } from './Rule'; +export { Stamp } from './Stamp'; +export { Badge } from './Badge'; +export { Button } from './Button'; +export { SectionBanner } from './SectionBanner'; +export { PullQuote } from './PullQuote'; +export { StatCard } from './StatCard'; diff --git a/src/components/ui/infinite-moving-cards.tsx b/src/components/ui/infinite-moving-cards.tsx new file mode 100644 index 0000000..56674be --- /dev/null +++ b/src/components/ui/infinite-moving-cards.tsx @@ -0,0 +1,156 @@ +import { useEffect, useRef, useState } from 'react' +import { clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +function cn(...inputs: Parameters) { + return twMerge(clsx(inputs)) +} + +export interface MovingCardItem { + quote: string + name: string + title: string + level?: 'critical' | 'high' | 'medium' | 'low' | 'opportunity' +} + +interface InfiniteMovingCardsProps { + items: MovingCardItem[] + direction?: 'left' | 'right' + speed?: 'fast' | 'normal' | 'slow' + pauseOnHover?: boolean + className?: string +} + +const levelConfig = { + critical: { dot: '#7f1d1d', label: 'بحرانی', text: '#7f1d1d', bg: 'rgba(127,29,29,0.08)' }, + high: { dot: '#9b1c1c', label: 'بالا', text: '#9b1c1c', bg: 'rgba(155,28,28,0.07)' }, + medium: { dot: '#92400e', label: 'متوسط', text: '#92400e', bg: 'rgba(146,64,14,0.07)' }, + low: { dot: '#166534', label: 'پایین', text: '#166534', bg: 'rgba(22,101,52,0.07)' }, + opportunity: { dot: '#1e40af', label: 'فرصت', text: '#1e40af', bg: 'rgba(30,64,175,0.07)' }, +} + +export function InfiniteMovingCards({ + items, + direction = 'left', + speed = 'slow', + pauseOnHover = true, + className, +}: InfiniteMovingCardsProps) { + const containerRef = useRef(null) + const scrollerRef = useRef(null) + const [ready, setReady] = useState(false) + + useEffect(() => { + if (!containerRef.current || !scrollerRef.current) return + + /* Clone items for seamless loop */ + const items = Array.from(scrollerRef.current.children) + items.forEach(item => { + const clone = item.cloneNode(true) + scrollerRef.current!.appendChild(clone) + }) + + /* Set CSS custom properties */ + const dur = speed === 'fast' ? '25s' : speed === 'normal' ? '45s' : '70s' + containerRef.current.style.setProperty('--animation-duration', dur) + containerRef.current.style.setProperty('--animation-direction', + direction === 'right' ? 'reverse' : 'forwards') + + setReady(true) + }, [direction, speed]) + + return ( +
    +
      + {items.map((item, idx) => { + const cfg = item.level ? levelConfig[item.level] : levelConfig.high + return ( +
    • + {/* Level badge */} +
      +
      + + {cfg.label} + +
      + + {/* Quote / description */} +

      + {item.quote} +

      + + {/* Footer */} +
      +
      +
      + {item.name} +
      +
      + {item.title} +
      +
      +
      + {cfg.label} +
      +
      +
    • + ) + })} +
    +
    + ) +} diff --git a/src/components/ui/world-map.tsx b/src/components/ui/world-map.tsx new file mode 100644 index 0000000..74e5d4e --- /dev/null +++ b/src/components/ui/world-map.tsx @@ -0,0 +1,191 @@ +/** + * WorldMap — Aceternity-style dotted world map with animated arc routes. + * + * DottedMap (height=60, grid='diagonal') emits viewBox="0 0 119 60". + * We project lat/lng into that same coordinate space so the arcs sit + * exactly on the map dots. + */ +import { useRef, useMemo, memo } from 'react' +import { motion } from 'framer-motion' +import DottedMap from 'dotted-map' + +/* ─── Public types ───────────────────────────────────── */ +export interface MapDot { + start: { lat: number; lng: number; label?: string } + end: { lat: number; lng: number; label?: string } +} + +interface WorldMapProps { + dots?: MapDot[] + lineColor?: string + dotColor?: string +} + +/* ─── Map geometry constants ─────────────────────────── */ +const VB_W = 119 // DottedMap viewBox width (height=60 → width=119) +const VB_H = 60 // DottedMap viewBox height + +/* Equirectangular projection matching DottedMap's output */ +function project(lat: number, lng: number) { + const x = (lng + 180) / 360 * VB_W + const y = (90 - lat) / 180 * VB_H + return { x, y } +} + +/* Quadratic bezier — control point lifted proportional to distance */ +function arcPath( + s: { x: number; y: number }, + e: { x: number; y: number }, +) { + const dx = e.x - s.x + const dy = e.y - s.y + const dist = Math.sqrt(dx * dx + dy * dy) + const mx = (s.x + e.x) / 2 + const my = (s.y + e.y) / 2 - dist * 0.35 // lift proportional to length + return `M ${s.x} ${s.y} Q ${mx} ${my} ${e.x} ${e.y}` +} + +/* ─── Memoised base map (expensive, only computed once) ─ */ +const BaseMap = memo(function BaseMap({ dotColor }: { dotColor: string }) { + const map = new DottedMap({ height: 60, grid: 'diagonal' }) + const svg = map.getSVG({ + color: dotColor, + radius: 0.22, + backgroundColor: 'transparent', + }) + return ( +
    + ) +}) + +/* ─── Component ──────────────────────────────────────── */ +export default function WorldMap({ + dots = [], + lineColor = '#9b1c1c', // var(--red) + dotColor = '#d1cdc7', // muted cream-grey +}: WorldMapProps) { + const svgRef = useRef(null) + + /* Pre-compute projected coordinates */ + const projected = useMemo( + () => + dots.map(d => ({ + start: project(d.start.lat, d.start.lng), + end: project(d.end.lat, d.end.lng), + startLabel: d.start.label, + endLabel: d.end.label, + })), + [dots], + ) + + return ( + /* aspect-ratio 119:60 ≈ 2:1 keeps the map undistorted */ +
    + {/* Dotted world map base */} + + + {/* Arc + dot overlay — same viewBox as DottedMap */} + + + {projected.map((_, i) => ( + + + + + + + ))} + + + {projected.map((p, i) => { + const d = arcPath(p.start, p.end) + return ( + + {/* Ghost — very faint, instant, gives the "track" feel */} + + + {/* Animated arc */} + + + {/* Start endpoint */} + + {/* End endpoint */} + + + ) + })} + +
    + ) +} + +/* ─── Pulsing dot ────────────────────────────────────── */ +function PulsingDot({ + cx, cy, color, delay, +}: { + cx: number; cy: number; color: string; delay: number +}) { + return ( + + {/* Outer ring — pulses outward */} + + {/* Solid centre */} + + + + ) +} diff --git a/src/data/events.ts b/src/data/events.ts new file mode 100644 index 0000000..451a4aa --- /dev/null +++ b/src/data/events.ts @@ -0,0 +1,95 @@ +export type EventType = 'conference' | 'exhibition' | 'seminar' | 'international'; + +export interface Event { + id: string; + title: string; + date: number; + month: string; + year: number; + type: EventType; + location: string; + city: string; + registrationOpen: boolean; + description: string; +} + +export const events: Event[] = [ + { + id: 'iran-steel-congress-1403', + title: 'کنگره بین‌المللی فولاد ایران ۱۴۰۳', + date: 12, + month: 'اسفند', + year: 1403, + type: 'conference', + location: 'مرکز همایش‌های بین‌المللی تهران', + city: 'تهران', + registrationOpen: true, + description: + 'بزرگ‌ترین گردهمایی سالانه متخصصان، سیاست‌گذاران و فعالان صنعت فولاد ایران با حضور ۱۲۰۰ نفر شرکت‌کننده از ۲۵ کشور.', + }, + { + id: 'middle-east-steel-metals-2024', + title: 'Middle East Steel & Metals 2024', + date: 25, + month: 'اسفند', + year: 1403, + type: 'exhibition', + location: 'مرکز نمایشگاهی دبی ورلد ترید سنتر', + city: 'دبی', + registrationOpen: true, + description: + 'نمایشگاه تخصصی فولاد و فلزات خاورمیانه با حضور بیش از ۳۰۰ شرکت از ۴۰ کشور؛ فرصتی برای معرفی ظرفیت‌های صادراتی صنعت فولاد ایران.', + }, + { + id: 'steel-policy-seminar-1404', + title: 'نشست تخصصی سیاست‌گذاری فولاد ۱۴۰۴', + date: 8, + month: 'فروردین', + year: 1404, + type: 'seminar', + location: 'پژوهشکده توسعه صنعتی ایران', + city: 'تهران', + registrationOpen: true, + description: + 'نشست تخصصی با حضور نمایندگان وزارت صمت، سازمان ایمیدرو و انجمن‌های تخصصی برای بررسی سیاست‌های سال ۱۴۰۴ در صنعت فولاد.', + }, + { + id: 'world-steel-forum-2025', + title: 'World Steel Forum 2025', + date: 20, + month: 'فروردین', + year: 1404, + type: 'international', + location: 'مرکز کنفرانس برلین', + city: 'برلین', + registrationOpen: false, + description: + 'اجلاس سالانه انجمن جهانی فولاد با تمرکز بر کربن‌زدایی، تجارت جهانی و تحولات ساختاری صنعت فولاد تا افق ۲۰۳۰.', + }, + { + id: 'iran-metallurgy-expo-1404', + title: 'نمایشگاه متالورژی ایران', + date: 15, + month: 'اردیبهشت', + year: 1404, + type: 'exhibition', + location: 'نمایشگاه بین‌المللی اصفهان', + city: 'اصفهان', + registrationOpen: false, + description: + 'نمایشگاه تخصصی متالورژی، فولاد و فلزات غیرآهنی ایران با محوریت فناوری‌های نوین تولید و تجهیزات صنعتی.', + }, + { + id: 'oecd-steel-summit-2025', + title: 'اجلاس OECD فولاد', + date: 3, + month: 'خرداد', + year: 1404, + type: 'international', + location: 'دفتر مرکزی OECD', + city: 'پاریس', + registrationOpen: false, + description: + 'نشست سالانه کمیته فولاد OECD برای بررسی مازاد ظرفیت جهانی، سیاست‌های تجاری و استانداردهای پایداری در صنعت فولاد.', + }, +]; diff --git a/src/data/market.ts b/src/data/market.ts new file mode 100644 index 0000000..40158d1 --- /dev/null +++ b/src/data/market.ts @@ -0,0 +1,179 @@ +export type PriceTrend = 'up' | 'down' | 'flat'; + +export interface MarketPrice { + id: string; + name: string; + value: number; + unit: string; + change: number; + changePercent: number; + trend: PriceTrend; +} + +export interface PriceHistoryPoint { + month: string; + price: number; +} + +export interface GlobalComparison { + country: string; + flag: string; + price: number; + unit: string; + currency: string; +} + +export interface ExportData { + month: string; + tonnage: number; +} + +export interface InstituteStats { + totalReports: number; + years: number; + experts: number; + memberOrgs: number; + subscribers: number; +} + +export const marketPrices: MarketPrice[] = [ + { + id: 'steel-billet', + name: 'شمش فولاد', + value: 18400, + unit: 'تومان/کیلو', + change: 220, + changePercent: 1.2, + trend: 'up', + }, + { + id: 'rebar-a3', + name: 'میلگرد A3', + value: 17800, + unit: 'تومان/کیلو', + change: -90, + changePercent: -0.5, + trend: 'down', + }, + { + id: 'beam-14', + name: 'تیرآهن ۱۴', + value: 16900, + unit: 'تومان/کیلو', + change: 135, + changePercent: 0.8, + trend: 'up', + }, + { + id: 'scrap-iron', + name: 'آهن قراضه', + value: 550, + unit: 'دلار/تن', + change: -11, + changePercent: -2.0, + trend: 'down', + }, + { + id: 'cold-rolled-sheet', + name: 'ورق سرد', + value: 22100, + unit: 'تومان/کیلو', + change: 66, + changePercent: 0.3, + trend: 'up', + }, + { + id: 'angle-bar-80', + name: 'نبشی ۸۰', + value: 15600, + unit: 'تومان/کیلو', + change: -172, + changePercent: -1.1, + trend: 'down', + }, + { + id: 'mobarakeh-steel', + name: 'فولاد مبارکه', + value: 12840, + unit: 'ریال', + change: -143, + changePercent: -1.1, + trend: 'down', + }, +]; + +export const priceHistory: PriceHistoryPoint[] = [ + { month: 'خرداد ۱۴۰۳', price: 15200 }, + { month: 'تیر ۱۴۰۳', price: 15600 }, + { month: 'مرداد ۱۴۰۳', price: 16100 }, + { month: 'شهریور ۱۴۰۳', price: 15800 }, + { month: 'مهر ۱۴۰۳', price: 16400 }, + { month: 'آبان ۱۴۰۳', price: 17000 }, + { month: 'آذر ۱۴۰۳', price: 17500 }, + { month: 'دی ۱۴۰۳', price: 17200 }, + { month: 'بهمن ۱۴۰۳', price: 17800 }, + { month: 'اسفند ۱۴۰۳', price: 18100 }, + { month: 'فروردین ۱۴۰۴', price: 18600 }, + { month: 'اردیبهشت ۱۴۰۴', price: 18400 }, +]; + +export const globalComparison: GlobalComparison[] = [ + { + country: 'ایران', + flag: '🇮🇷', + price: 18400, + unit: 'تومان/کیلو', + currency: 'IRR', + }, + { + country: 'ترکیه', + flag: '🇹🇷', + price: 530, + unit: 'دلار/تن', + currency: 'USD', + }, + { + country: 'هند', + flag: '🇮🇳', + price: 510, + unit: 'دلار/تن', + currency: 'USD', + }, + { + country: 'چین', + flag: '🇨🇳', + price: 460, + unit: 'دلار/تن', + currency: 'USD', + }, + { + country: 'روسیه', + flag: '🇷🇺', + price: 485, + unit: 'دلار/تن', + currency: 'USD', + }, +]; + +export const monthlyExport: ExportData[] = [ + { month: 'خرداد ۱۴۰۳', tonnage: 285000 }, + { month: 'تیر ۱۴۰۳', tonnage: 310000 }, + { month: 'مرداد ۱۴۰۳', tonnage: 295000 }, + { month: 'شهریور ۱۴۰۳', tonnage: 320000 }, + { month: 'مهر ۱۴۰۳', tonnage: 350000 }, + { month: 'آبان ۱۴۰۳', tonnage: 375000 }, + { month: 'آذر ۱۴۰۳', tonnage: 390000 }, + { month: 'دی ۱۴۰۳', tonnage: 360000 }, + { month: 'بهمن ۱۴۰۳', tonnage: 415000 }, + { month: 'اسفند ۱۴۰۳', tonnage: 420000 }, + { month: 'فروردین ۱۴۰۴', tonnage: 400000 }, + { month: 'اردیبهشت ۱۴۰۴', tonnage: 410000 }, +]; + +export const stats: InstituteStats = { + totalReports: 280, + years: 12, + experts: 45, + memberOrgs: 60, + subscribers: 2800, +}; diff --git a/src/data/plans.ts b/src/data/plans.ts new file mode 100644 index 0000000..03e4025 --- /dev/null +++ b/src/data/plans.ts @@ -0,0 +1,74 @@ +export type PlanCtaVariant = 'outline' | 'filled' | 'paper'; + +export interface Plan { + id: string; + name: string; + price: number; + priceLabel: string; + period: string; + features: string[]; + badge?: string; + featured: boolean; + ctaLabel: string; + ctaVariant: PlanCtaVariant; +} + +export const plans: Plan[] = [ + { + id: 'individual', + name: 'فردی', + price: 350000, + priceLabel: '۳۵۰,۰۰۰ تومان', + period: 'ماهانه', + features: [ + 'دسترسی به تمام گزارش‌های پایه', + 'دانلود ۵ گزارش در ماه', + 'دریافت خبرنامه هفتگی', + 'دسترسی به داده‌های قیمت لحظه‌ای', + 'آرشیو ۱۲ ماه گذشته', + ], + featured: false, + ctaLabel: 'شروع اشتراک', + ctaVariant: 'outline', + }, + { + id: 'organizational', + name: 'سازمانی', + price: 1200000, + priceLabel: '۱,۲۰۰,۰۰۰ تومان', + period: 'ماهانه', + features: [ + 'دسترسی کامل به تمام گزارش‌ها', + 'دانلود نامحدود گزارش', + 'دسترسی برای ۵ کاربر همزمان', + 'گزارش‌های ویژه و تحلیل‌های فوری', + 'آرشیو کامل از ابتدای تأسیس', + 'مشاوره تلفنی ماهانه با تحلیلگران', + 'داشبورد اختصاصی تحلیل بازار', + ], + badge: 'پرطرفدار', + featured: true, + ctaLabel: 'شروع اشتراک', + ctaVariant: 'filled', + }, + { + id: 'ministry', + name: 'وزارتخانه', + price: 0, + priceLabel: 'تماس بگیرید', + period: 'سالانه', + features: [ + 'دسترسی نامحدود برای تمام کارکنان', + 'سفارش گزارش‌های تحلیلی اختصاصی', + 'نشست‌های تخصصی حضوری با تیم پژوهشی', + 'پشتیبانی اولویت‌دار ۲۴/۷', + 'داشبورد تصمیم‌گیری یکپارچه', + 'API اتصال به سیستم‌های داخلی', + 'آموزش و کارگاه‌های تخصصی دوره‌ای', + 'گزارش‌دهی سفارشی برای سیاست‌گذاران', + ], + featured: false, + ctaLabel: 'درخواست مشاوره', + ctaVariant: 'paper', + }, +]; diff --git a/src/data/reports.ts b/src/data/reports.ts new file mode 100644 index 0000000..7b7e724 --- /dev/null +++ b/src/data/reports.ts @@ -0,0 +1,335 @@ +export type ReportCategory = + | 'بازار جهانی' + | 'سیاست‌گذاری' + | 'انرژی و ESG' + | 'صادرات' + | 'تولید داخلی' + | 'ریسک و بحران'; + +export type ReportType = 'quarterly' | 'flash' | 'risk' | 'free' | 'special'; + +export interface Report { + id: string; + title: string; + category: ReportCategory; + type: ReportType; + author: string; + authorRole: string; + authorInitial: string; + publishDate: string; + pages: number; + price: number; + isFree: boolean; + summary: string; + tags: string[]; + featured: boolean; +} + +export const reports: Report[] = [ + { + id: 'iran-steel-global-trade-scenarios-1404', + title: 'ایران در نقشه جدید تجارت فولاد جهان: چهار سناریو برای ۱۴۰۴', + category: 'بازار جهانی', + type: 'special', + author: 'دکتر علی محمدی', + authorRole: 'مدیر ارشد پژوهش', + authorInitial: 'م', + publishDate: 'بهمن ۱۴۰۳', + pages: 84, + price: 850000, + isFree: false, + summary: + 'این گزارش ویژه با تحلیل روندهای کلان اقتصاد جهانی، چهار سناریوی محتمل برای جایگاه صادرات فولاد ایران در سال ۱۴۰۴ را بررسی می‌کند. مدل‌سازی کمی بر اساس داده‌های تجارت ۱۵ کشور رقیب انجام شده و توصیه‌های سیاست‌گذاری مشخص ارائه می‌دهد. این گزارش مرجع اصلی تصمیم‌گیران و مدیران ارشد صنعت فولاد کشور است.', + tags: ['تجارت جهانی', 'سناریوسازی', 'صادرات', 'سیاست‌گذاری', '۱۴۰۴'], + featured: true, + }, + { + id: 'steel-energy-consumption-efficiency-1403', + title: 'مصرف انرژی در فولادسازی ایران: مقایسه با استانداردهای جهانی', + category: 'انرژی و ESG', + type: 'free', + author: 'مهندس سارا رضایی', + authorRole: 'پژوهشگر ارشد انرژی', + authorInitial: 'ر', + publishDate: 'دی ۱۴۰۳', + pages: 28, + price: 0, + isFree: true, + summary: + 'این گزارش رایگان مصرف انرژی واحدهای فولادسازی ایران را با میانگین جهانی و استانداردهای IEA مقایسه می‌کند. شکاف بهره‌وری انرژی در کوره‌های قوس الکتریکی و کنورتور شناسایی شده است. راهکارهای کاهش ۱۵ تا ۲۵ درصدی مصرف انرژی نیز پیشنهاد شده است.', + tags: ['انرژی', 'بهره‌وری', 'ESG', 'کوره قوس الکتریکی', 'استاندارد جهانی'], + featured: false, + }, + { + id: 'global-steel-market-q3-1403', + title: 'گزارش فصلی بازار جهانی فولاد: پاییز ۱۴۰۳', + category: 'بازار جهانی', + type: 'quarterly', + author: 'دکتر حسین کریمی', + authorRole: 'تحلیلگر ارشد بازار', + authorInitial: 'ک', + publishDate: 'آذر ۱۴۰۳', + pages: 52, + price: 450000, + isFree: false, + summary: + 'تحلیل جامع قیمت‌ها، تقاضا و عرضه در بازار جهانی فولاد طی فصل پاییز ۱۴۰۳ ارائه شده است. روند کاهش تقاضای چین و افزایش ظرفیت تولید هند به‌تفصیل بررسی شده است. چشم‌انداز قیمتی برای زمستان ۱۴۰۳ نیز ارائه می‌شود.', + tags: ['بازار جهانی', 'قیمت‌گذاری', 'چین', 'هند', 'پاییز ۱۴۰۳'], + featured: false, + }, + { + id: 'iran-steel-export-sanctions-impact-1403', + title: 'اثر تحریم‌های جدید بر مسیرهای صادراتی فولاد ایران', + category: 'صادرات', + type: 'risk', + author: 'دکتر فاطمه احمدی', + authorRole: 'مدیر تحقیقات ریسک', + authorInitial: 'ا', + publishDate: 'آذر ۱۴۰۳', + pages: 36, + price: 380000, + isFree: false, + summary: + 'این گزارش اثرات بسته تحریمی اخیر بر کانال‌های صادرات فولاد ایران به بازارهای منطقه‌ای را ارزیابی می‌کند. ریسک‌های لجستیکی، بانکی و قراردادی مستند شده است. راهکارهای جایگزین برای حفظ جریان صادرات پیشنهاد می‌شود.', + tags: ['تحریم', 'صادرات', 'ریسک', 'لجستیک', 'بازارهای منطقه‌ای'], + featured: false, + }, + { + id: 'flash-iron-ore-price-surge-1403', + title: 'فلش: جهش قیمت سنگ‌آهن و تأثیر بر هزینه تمام‌شده فولاد ایران', + category: 'بازار جهانی', + type: 'flash', + author: 'مهندس رضا توکلی', + authorRole: 'تحلیلگر بازار کامودیتی', + authorInitial: 'ت', + publishDate: 'آبان ۱۴۰۳', + pages: 12, + price: 120000, + isFree: false, + summary: + 'گزارش فوری درباره افزایش ۱۸ درصدی قیمت سنگ‌آهن در بورس دالیان و اثر فوری آن بر هزینه تمام‌شده تولید فولاد در ایران. برآورد اثر بر حاشیه سود کارخانه‌های داخلی ارائه شده است. توصیه‌های پوشش ریسک قیمتی برای مدیران خرید نیز مطرح است.', + tags: ['سنگ‌آهن', 'قیمت', 'هزینه تمام‌شده', 'فوری'], + featured: false, + }, + { + id: 'domestic-production-dri-outlook-1403', + title: 'چشم‌انداز تولید آهن احیای مستقیم ایران تا افق ۱۴۰۵', + category: 'تولید داخلی', + type: 'quarterly', + author: 'دکتر علی محمدی', + authorRole: 'مدیر ارشد پژوهش', + authorInitial: 'م', + publishDate: 'مهر ۱۴۰۳', + pages: 44, + price: 420000, + isFree: false, + summary: + 'ایران با بیش از ۳۰ میلیون تن ظرفیت DRI، رتبه اول جهان را دارد؛ این گزارش مسیر توسعه این صنعت تا ۱۴۰۵ را مدل‌سازی می‌کند. بررسی پروژه‌های در دست احداث و موانع تأمین گاز به‌تفصیل آمده است. ریسک‌های بهره‌برداری و پیشنهادهای بهینه‌سازی نیز ارائه شده است.', + tags: ['DRI', 'آهن احیای مستقیم', 'ظرفیت', 'تولید', 'گاز طبیعی'], + featured: false, + }, + { + id: 'esg-carbon-emissions-steel-iran-1403', + title: 'کربن‌زدایی در صنعت فولاد ایران: الزامات و فرصت‌ها', + category: 'انرژی و ESG', + type: 'special', + author: 'مهندس سارا رضایی', + authorRole: 'پژوهشگر ارشد انرژی', + authorInitial: 'ر', + publishDate: 'شهریور ۱۴۰۳', + pages: 58, + price: 520000, + isFree: false, + summary: + 'الزامات آینده CBAM اتحادیه اروپا و اثر آن بر رقابت‌پذیری صادرات فولاد ایران تحلیل شده است. مسیرهای فناوری کربن‌زدایی شامل هیدروژن سبز و CCS برای شرایط ایران ارزیابی شده‌اند. برنامه عملیاتی ده‌ساله برای کاهش شدت کربن پیشنهاد می‌شود.', + tags: ['ESG', 'کربن', 'CBAM', 'هیدروژن سبز', 'محیط زیست'], + featured: false, + }, + { + id: 'policy-steel-pricing-reform-1403', + title: 'اصلاح ساختار قیمت‌گذاری فولاد: درس‌هایی از تجربه ترکیه و هند', + category: 'سیاست‌گذاری', + type: 'quarterly', + author: 'دکتر فاطمه احمدی', + authorRole: 'مدیر تحقیقات ریسک', + authorInitial: 'ا', + publishDate: 'مرداد ۱۴۰۳', + pages: 46, + price: 390000, + isFree: false, + summary: + 'مقایسه تطبیقی ساختار قیمت‌گذاری فولاد در ایران، ترکیه و هند با تمرکز بر نقش دولت و مکانیسم‌های بازار. اثربخشی سیاست‌های قیمت‌گذاری دستوری در حفظ تعادل عرضه-تقاضا ارزیابی شده است. توصیه‌های سیاستی برای کاهش اعوجاج قیمتی ارائه می‌شود.', + tags: ['سیاست‌گذاری', 'قیمت‌گذاری', 'ترکیه', 'هند', 'اصلاح ساختار'], + featured: false, + }, + { + id: 'flash-china-steel-overcapacity-1403', + title: 'فلش: مازاد ظرفیت فولاد چین و موج صادراتی به آسیای غربی', + category: 'بازار جهانی', + type: 'flash', + author: 'دکتر حسین کریمی', + authorRole: 'تحلیلگر ارشد بازار', + authorInitial: 'ک', + publishDate: 'تیر ۱۴۰۳', + pages: 10, + price: 110000, + isFree: false, + summary: + 'چین در نیمه اول ۲۰۲۴ رکورد صادرات فولاد را با ۵۴ میلیون تن شکست. این گزارش فوری تأثیر مستقیم این موج صادراتی بر بازارهای ایران، عراق و امارات را تحلیل می‌کند. توصیه اقدام فوری برای کنسرسیوم‌های صادراتی ایران ارائه شده است.', + tags: ['چین', 'مازاد ظرفیت', 'صادرات', 'آسیای غربی', 'رقابت'], + featured: false, + }, + { + id: 'risk-geopolitical-middle-east-steel-1403', + title: 'ریسک‌های ژئوپلیتیک خاورمیانه و زنجیره تأمین فولاد ایران', + category: 'ریسک و بحران', + type: 'risk', + author: 'دکتر فاطمه احمدی', + authorRole: 'مدیر تحقیقات ریسک', + authorInitial: 'ا', + publishDate: 'خرداد ۱۴۰۳', + pages: 40, + price: 360000, + isFree: false, + summary: + 'تحلیل ریسک ژئوپلیتیک منطقه خاورمیانه بر امنیت زنجیره تأمین فولاد ایران از زاویه واردات مواد اولیه و صادرات محصول نهایی. سناریوهای بحران در مسیرهای دریایی خلیج فارس و دریای سرخ مدل‌سازی شده‌اند. برنامه تداوم کسب‌وکار برای تولیدکنندگان ارائه می‌شود.', + tags: ['ژئوپلیتیک', 'خاورمیانه', 'زنجیره تأمین', 'ریسک', 'دریای سرخ'], + featured: false, + }, + { + id: 'export-iraq-market-analysis-1403', + title: 'تحلیل بازار فولاد عراق: فرصت‌های صادراتی ایران', + category: 'صادرات', + type: 'quarterly', + author: 'مهندس رضا توکلی', + authorRole: 'تحلیلگر بازار کامودیتی', + authorInitial: 'ت', + publishDate: 'اردیبهشت ۱۴۰۳', + pages: 38, + price: 340000, + isFree: false, + summary: + 'عراق با تقاضای سالانه بیش از ۵ میلیون تن فولاد، مهم‌ترین بازار صادراتی ایران است. این گزارش رقابت‌پذیری فولاد ایران در برابر رقبای ترکیه، چین و اوکراین در بازار عراق را ارزیابی می‌کند. استراتژی‌های بازاریابی و توسعه بازار پیشنهاد شده است.', + tags: ['عراق', 'صادرات', 'بازار', 'رقابت‌پذیری', 'میلگرد'], + featured: false, + }, + { + id: 'domestic-capacity-utilization-1402', + title: 'ظرفیت بهره‌برداری واحدهای فولادسازی ایران: آسیب‌شناسی و راهکار', + category: 'تولید داخلی', + type: 'quarterly', + author: 'دکتر علی محمدی', + authorRole: 'مدیر ارشد پژوهش', + authorInitial: 'م', + publishDate: 'فروردین ۱۴۰۳', + pages: 50, + price: 410000, + isFree: false, + summary: + 'میانگین ضریب بهره‌برداری کارخانه‌های فولادسازی ایران به ۶۸ درصد رسیده که پایین‌تر از میانگین جهانی ۷۶ درصد است. این گزارش موانع عملیاتی، تأمین مواد اولیه و بازار را شناسایی کرده است. برنامه‌های بهبود بهره‌برداری در کوتاه‌مدت و میان‌مدت ارائه می‌شود.', + tags: ['ظرفیت', 'بهره‌برداری', 'کارایی', 'تولید', 'کارخانه'], + featured: false, + }, + { + id: 'flash-rebar-demand-construction-1402', + title: 'فلش: کاهش تقاضای میلگرد در بخش ساختمانی و اثر بر کارخانه‌ها', + category: 'تولید داخلی', + type: 'flash', + author: 'مهندس سارا رضایی', + authorRole: 'پژوهشگر ارشد انرژی', + authorInitial: 'ر', + publishDate: 'اسفند ۱۴۰۲', + pages: 14, + price: 115000, + isFree: false, + summary: + 'رکود در بازار مسکن منجر به کاهش ۱۲ درصدی تقاضای میلگرد در زمستان ۱۴۰۲ شده است. این گزارش فوری اثر این کاهش بر درآمد و برنامه تولید کارخانه‌های میلگردساز را ارزیابی می‌کند. توصیه تنظیم پورتفولیوی محصول برای جبران افت تقاضا مطرح شده است.', + tags: ['میلگرد', 'ساختمان', 'تقاضا', 'رکود', 'تولید داخلی'], + featured: false, + }, + { + id: 'policy-privatization-steel-companies-1402', + title: 'واگذاری شرکت‌های فولادی دولتی: ارزیابی تجربه یک دهه خصوصی‌سازی', + category: 'سیاست‌گذاری', + type: 'special', + author: 'دکتر حسین کریمی', + authorRole: 'تحلیلگر ارشد بازار', + authorInitial: 'ک', + publishDate: 'بهمن ۱۴۰۲', + pages: 62, + price: 560000, + isFree: false, + summary: + 'ارزیابی جامع نتایج خصوصی‌سازی شرکت‌های فولادی بزرگ ایران در یک دهه گذشته با شاخص‌های بهره‌وری، اشتغال و سرمایه‌گذاری. تجربه‌های موفق و ناموفق واگذاری مستند شده است. الگوی بهینه مشارکت دولتی-خصوصی در صنعت فولاد پیشنهاد می‌شود.', + tags: ['خصوصی‌سازی', 'سیاست‌گذاری', 'دولت', 'مدیریت', 'بهره‌وری'], + featured: false, + }, + { + id: 'risk-water-scarcity-steel-industry-1402', + title: 'بحران آب و تهدیدات پایداری صنعت فولاد ایران', + category: 'ریسک و بحران', + type: 'risk', + author: 'مهندس سارا رضایی', + authorRole: 'پژوهشگر ارشد انرژی', + authorInitial: 'ر', + publishDate: 'دی ۱۴۰۲', + pages: 42, + price: 370000, + isFree: false, + summary: + 'استرس آبی در کلاستر فولادسازی اصفهان-یزد-خراسان به نقطه بحرانی رسیده است. این گزارش ریسک تعطیلی یا کاهش تولید ناشی از کمبود آب را برای ۱۴ واحد تولیدی ارزیابی می‌کند. راهکارهای فناوری و مدیریتی برای کاهش مصرف آب پیشنهاد شده است.', + tags: ['آب', 'بحران', 'پایداری', 'اصفهان', 'ریسک محیط زیستی'], + featured: false, + }, + { + id: 'global-market-q1-1402', + title: 'گزارش فصلی بازار جهانی فولاد: بهار ۱۴۰۲', + category: 'بازار جهانی', + type: 'quarterly', + author: 'دکتر علی محمدی', + authorRole: 'مدیر ارشد پژوهش', + authorInitial: 'م', + publishDate: 'تیر ۱۴۰۲', + pages: 54, + price: 440000, + isFree: false, + summary: + 'بررسی روندهای بازار جهانی فولاد در بهار ۱۴۰۲ با تمرکز بر بازگشایی اقتصاد چین و اثرات آن بر قیمت‌ها. تأثیر افزایش نرخ بهره فدرال رزرو بر سرمایه‌گذاری در ساختمان و زیرساخت تحلیل شده است. پیش‌بینی روند قیمتی تا پایان ۱۴۰۲ ارائه می‌شود.', + tags: ['بازار جهانی', 'چین', 'نرخ بهره', 'تقاضا', 'بهار ۱۴۰۲'], + featured: false, + }, + { + id: 'export-africa-emerging-markets-1402', + title: 'فرصت‌های صادرات فولاد ایران به بازارهای نوظهور آفریقا', + category: 'صادرات', + type: 'special', + author: 'دکتر فاطمه احمدی', + authorRole: 'مدیر تحقیقات ریسک', + authorInitial: 'ا', + publishDate: 'آبان ۱۴۰۲', + pages: 48, + price: 480000, + isFree: false, + summary: + 'آفریقا با رشد سالانه ۶ درصد در تقاضای فولاد، بازار استراتژیک بلندمدت برای صادرکنندگان ایران محسوب می‌شود. این گزارش ۸ بازار هدف اولویت‌دار آفریقایی را با تحلیل ریسک و فرصت معرفی می‌کند. مدل ورود به بازار و شرکای تجاری محتمل نیز پیشنهاد شده است.', + tags: ['آفریقا', 'بازارهای نوظهور', 'صادرات', 'تنوع‌سازی', 'بلندمدت'], + featured: false, + }, + { + id: 'flash-gas-price-hike-steel-cost-1403', + title: 'فلش: اثر افزایش قیمت گاز بر قیمت تمام‌شده فولاد ایران', + category: 'انرژی و ESG', + type: 'flash', + author: 'مهندس رضا توکلی', + authorRole: 'تحلیلگر بازار کامودیتی', + authorInitial: 'ت', + publishDate: 'شهریور ۱۴۰۳', + pages: 11, + price: 105000, + isFree: false, + summary: + 'افزایش ۳۰ درصدی قیمت گاز صنعتی در مهر ۱۴۰۳ هزینه تولید هر تن DRI را حدود ۱۲۰۰ تومان افزایش می‌دهد. این گزارش فوری اثر این شوک هزینه‌ای بر حاشیه سود تولیدکنندگان DRI و فولاد را محاسبه می‌کند. راهکارهای کوتاه‌مدت برای مدیریت این افزایش هزینه پیشنهاد شده است.', + tags: ['گاز', 'انرژی', 'هزینه تولید', 'DRI', 'قیمت‌گذاری'], + featured: false, + }, +]; diff --git a/src/data/team.ts b/src/data/team.ts new file mode 100644 index 0000000..5ac274c --- /dev/null +++ b/src/data/team.ts @@ -0,0 +1,109 @@ +export interface TeamMember { + id: string; + name: string; + role: string; + initial: string; + bio: string; + expertise: string[]; + reportCount: number; + email?: string; +} + +export const team: TeamMember[] = [ + { + id: 'ali-mohammadi', + name: 'دکتر علی محمدی', + role: 'مدیر ارشد پژوهش', + initial: 'م', + bio: 'دکتری اقتصاد صنعتی از دانشگاه تهران با بیش از ۱۸ سال تجربه در تحلیل بازارهای فولاد و فلزات. پیش از پیوستن به اندیشکده، مشاور ارشد سازمان توسعه و نوسازی معادن و صنایع معدنی ایران (ایمیدرو) بوده است. بیش از ۵۰ گزارش تحلیلی و ۱۲ مقاله علمی در حوزه اقتصاد فولاد منتشر کرده است.', + expertise: [ + 'اقتصاد صنعتی', + 'تحلیل بازار فولاد', + 'سیاست‌گذاری صنعتی', + 'مدل‌سازی اقتصادی', + 'تجارت بین‌الملل', + ], + reportCount: 52, + email: 'mohammadi@andishkade-foolad.ir', + }, + { + id: 'fateme-ahmadi', + name: 'دکتر فاطمه احمدی', + role: 'مدیر تحقیقات ریسک', + initial: 'ا', + bio: 'دکتری مدیریت ریسک از دانشگاه علم و صنعت ایران با تخصص در ارزیابی ریسک‌های ژئوپلیتیک و زنجیره تأمین. سابقه مشاوره به چندین شرکت بزرگ فولادی کشور در حوزه مدیریت ریسک تجاری و قراردادی را دارد. نویسنده کتاب «مدیریت ریسک در صنایع معدنی و فلزی» است.', + expertise: [ + 'مدیریت ریسک', + 'تحریم‌پژوهی', + 'ژئوپلیتیک انرژی', + 'تأمین مالی پروژه', + 'مذاکرات تجاری', + ], + reportCount: 38, + email: 'ahmadi@andishkade-foolad.ir', + }, + { + id: 'hosein-karimi', + name: 'دکتر حسین کریمی', + role: 'تحلیلگر ارشد بازار', + initial: 'ک', + bio: 'دکتری مهندسی مواد از دانشگاه صنعتی اصفهان با تخصص ترکیبی در متالورژی و اقتصاد بازار کامودیتی‌ها. پنج سال سابقه کار در واحد پژوهش و توسعه فولاد مبارکه اصفهان دارد. به‌طور منظم گزارش‌های فصلی بازار جهانی فولاد را تهیه و منتشر می‌کند.', + expertise: [ + 'بازار جهانی فولاد', + 'قیمت‌گذاری کامودیتی', + 'متالورژی صنعتی', + 'پیش‌بینی تقاضا', + 'بازارهای آسیایی', + ], + reportCount: 41, + email: 'karimi@andishkade-foolad.ir', + }, + { + id: 'sara-rezaei', + name: 'مهندس سارا رضایی', + role: 'پژوهشگر ارشد انرژی', + initial: 'ر', + bio: 'کارشناسی ارشد مهندسی انرژی از دانشگاه شریف با تمرکز بر بهینه‌سازی مصرف انرژی در صنایع سنگین. تجربه عملیاتی در حسابرسی انرژی کارخانه‌های فولادی در اصفهان، خراسان و هرمزگان دارد. پژوهش‌های او در حوزه ESG و کربن‌زدایی صنعت فولاد ایران از مراجع اصلی این حوزه است.', + expertise: [ + 'انرژی صنعتی', + 'ESG و پایداری', + 'کربن‌زدایی', + 'بهره‌وری انرژی', + 'مقررات زیست‌محیطی', + ], + reportCount: 29, + email: 'rezaei@andishkade-foolad.ir', + }, + { + id: 'reza-tavakoli', + name: 'مهندس رضا توکلی', + role: 'تحلیلگر بازار کامودیتی', + initial: 'ت', + bio: 'کارشناسی ارشد اقتصاد از دانشگاه تهران با تخصص در تحلیل بنیادی و تکنیکال بازارهای کامودیتی. سابقه فعالیت در کارگزاری‌های بورس کالا و تهیه گزارش‌های فوری برای معامله‌گران فولاد را دارد. مسئول پوشش خبری و تحلیلی بازارهای بورس دالیان، LME و بازار داخلی است.', + expertise: [ + 'بورس کالا', + 'تحلیل تکنیکال', + 'بازارهای آتی', + 'قیمت‌گذاری سنگ‌آهن', + 'بازار داخلی فولاد', + ], + reportCount: 34, + email: 'tavakoli@andishkade-foolad.ir', + }, + { + id: 'maryam-hosseini', + name: 'دکتر مریم حسینی', + role: 'پژوهشگر ارشد صادرات', + initial: 'ح', + bio: 'دکتری بازاریابی بین‌الملل از دانشگاه الزهرا با تجربه گسترده در مطالعات بازارهای صادراتی آسیا، آفریقا و اروپای شرقی. پیش از این در اتاق بازرگانی ایران و کمیته فولاد آن مشغول به فعالیت بوده است. حوزه تخصصی او توسعه بازارهای صادراتی جدید و استراتژی‌های نفوذ به بازار است.', + expertise: [ + 'بازاریابی بین‌الملل', + 'توسعه صادرات', + 'بازار آفریقا و آسیا', + 'استراتژی بازار', + 'مذاکرات تجاری', + ], + reportCount: 22, + email: 'hosseini@andishkade-foolad.ir', + }, +]; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..7fda02b --- /dev/null +++ b/src/index.css @@ -0,0 +1,239 @@ +@import "tailwindcss"; + +/* ═══════════════════════════════════════════════ + VAZIR FONT FACES +═══════════════════════════════════════════════ */ +@font-face { + font-family: 'Vazir'; + src: url('/fonts/Vazir-Light.woff2') format('woff2'); + font-weight: 300; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Vazir'; + src: url('/fonts/Vazir-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Vazir'; + src: url('/fonts/Vazir-Medium.woff2') format('woff2'); + font-weight: 500; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Vazir'; + src: url('/fonts/Vazir-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Vazir'; + src: url('/fonts/Vazir-Black.woff2') format('woff2'); + font-weight: 900; + font-style: normal; + font-display: swap; +} + +/* ═══════════════════════════════════════════════ + DESIGN TOKENS +═══════════════════════════════════════════════ */ +:root { + --paper: #f4efe7; + --paper-2: #ede8de; + --paper-3: #e4ddd0; + --ink: #1a1712; + --ink-2: #2e2a25; + --ink-3: #4a4540; + --ink-4: #6b6560; + --ink-5: #766e68; + --ink-6: #c8c2bc; + --rule-thin: rgba(26,23,18,0.12); + --red: #9b1c1c; + --red-2: #b52020; + --amber: #92400e; + --green-ink: #166534; +} + +/* ═══════════════════════════════════════════════ + TYPOGRAPHY SYSTEM + + Scale (clamp: mobile → desktop) + --fs-display hero headlines 32 → 72px + --fs-h1 page-level headings 26 → 52px + --fs-h2 section headings 20 → 38px + --fs-h3 card/sub headings 15 → 22px + --fs-h4 small headings 13 → 17px + --fs-body-lg lead paragraphs 14 → 16px + --fs-body body text 13 → 15px + --fs-sm secondary / meta 11 → 13px + --fs-xs labels / captions 9 → 11px +═══════════════════════════════════════════════ */ +:root { + /* Font sizes */ + --fs-display: clamp(32px, 5.5vw, 72px); + --fs-h1: clamp(26px, 3.8vw, 52px); + --fs-h2: clamp(20px, 2.8vw, 38px); + --fs-h3: clamp(15px, 1.5vw, 22px); + --fs-h4: clamp(13px, 1.1vw, 17px); + --fs-body-lg: clamp(14px, 1.1vw, 16px); + --fs-body: clamp(13px, 0.9vw, 15px); + --fs-sm: clamp(11px, 0.75vw, 13px); + --fs-xs: clamp(9px, 0.6vw, 11px); + + /* Letter spacing */ + --ls-display: -2.5px; + --ls-h1: -2px; + --ls-h2: -1px; + --ls-h3: -0.3px; + --ls-body: 0px; + --ls-label: 1.5px; + --ls-overline: 2.5px; + --ls-widest: 3px; + + /* Line heights */ + --lh-display: 1.04; + --lh-heading: 1.12; + --lh-subhead: 1.3; + --lh-body: 1.8; + --lh-relaxed: 1.85; + --lh-label: 1.3; +} + +/* ── Base element styles ──────────────────────── */ +h1 { + font-size: var(--fs-h1); + font-weight: 900; + line-height: var(--lh-heading); + letter-spacing: var(--ls-h1); + color: var(--ink); + margin: 0; +} +h2 { + font-size: var(--fs-h2); + font-weight: 900; + line-height: var(--lh-heading); + letter-spacing: var(--ls-h2); + color: var(--ink); + margin: 0; +} +h3 { + font-size: var(--fs-h3); + font-weight: 700; + line-height: var(--lh-subhead); + letter-spacing: var(--ls-h3); + color: var(--ink); + margin: 0; +} +h4 { + font-size: var(--fs-h4); + font-weight: 700; + line-height: var(--lh-subhead); + color: var(--ink); + margin: 0; +} +p { + font-size: var(--fs-body); + font-weight: 400; /* was 300 — too thin on most screens */ + line-height: var(--lh-body); + color: var(--ink-3); + margin: 0; +} + +/* ═══════════════════════════════════════════════ + BASE +═══════════════════════════════════════════════ */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { scroll-behavior: smooth; direction: rtl; } + +body { + font-family: 'Vazir', system-ui, sans-serif; + background: var(--paper); + color: var(--ink); + font-size: var(--fs-body); + line-height: var(--lh-body); + overflow-x: hidden; + -webkit-font-smoothing: antialiased; +} + +#root { min-height: 100dvh; display: flex; flex-direction: column; } + +::-webkit-scrollbar { width: 3px; height: 3px; } +::-webkit-scrollbar-thumb { background: var(--ink); } + +:focus-visible { + outline: 2px solid var(--red); + outline-offset: 2px; +} + +::selection { background: var(--ink); color: var(--paper); } + +/* ═══════════════════════════════════════════════ + TYPOGRAPHY UTILITIES +═══════════════════════════════════════════════ */ +/* Overline — small uppercase tracking label */ +.t-overline { + font-size: var(--fs-xs); + font-weight: 700; + letter-spacing: var(--ls-overline); + text-transform: uppercase; + line-height: var(--lh-label); +} + +/* Caption / meta text */ +.t-caption { + font-size: var(--fs-sm); + font-weight: 500; + line-height: var(--lh-label); + color: var(--ink-4); +} + +/* Lead paragraph */ +.t-lead { + font-size: var(--fs-body-lg); + font-weight: 400; + line-height: var(--lh-relaxed); + color: var(--ink-3); +} + +/* ═══════════════════════════════════════════════ + TICKER ANIMATION (RTL direction) +═══════════════════════════════════════════════ */ +@keyframes ticker-rtl { + 0% { transform: translateX(50%); } + 100% { transform: translateX(-50%); } +} +.ticker-track { animation: ticker-rtl 34s linear infinite; white-space: nowrap; } +@media (prefers-reduced-motion: reduce) { .ticker-track { animation: none; } } + +/* ═══════════════════════════════════════════════ + STAMP TILT +═══════════════════════════════════════════════ */ +.stamp-tilt { transform: rotate(-1.5deg); } + +/* ═══════════════════════════════════════════════ + INFINITE SCROLL ANIMATION +═══════════════════════════════════════════════ */ +@keyframes scroll { + to { transform: translateX(calc(-50% - 0.5rem)); } +} +.animate-scroll { + animation: scroll var(--animation-duration, 60s) var(--animation-direction, forwards) linear infinite; +} +.animate-scroll-risk { + animation: scroll 72s forwards linear infinite; +} +@media (prefers-reduced-motion: reduce) { + .animate-scroll { animation: none; } + .animate-scroll-risk { animation: none; } +} + +/* ═══════════════════════════════════════════════ + PRINT +═══════════════════════════════════════════════ */ +@media print { .no-print { display: none !important; } } diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..53e0bd5 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,96 @@ +/** + * Shared utility functions — Persian/Jalali formatting & helpers + */ + +/** + * Format a number as Persian locale (e.g. 18400 → "۱۸,۴۰۰") + */ +export function formatPersianNumber(n: number): string { + return n.toLocaleString('fa-IR') +} + +/** + * Format a Toman price (e.g. 850000 → "۸۵۰,۰۰۰ تومان") + */ +export function formatPrice(n: number): string { + if (n === 0) return 'رایگان' + return `${n.toLocaleString('fa-IR')} تومان` +} + +/** + * Format a price compactly (e.g. 1200000 → "۱.۲ م.ت") + */ +export function formatPriceCompact(n: number): string { + if (n === 0) return 'رایگان' + if (n >= 1_000_000) { + const m = n / 1_000_000 + return `${m.toLocaleString('fa-IR')} م.ت` + } + if (n >= 1_000) { + const k = n / 1_000 + return `${k.toLocaleString('fa-IR')} ه.ت` + } + return `${n.toLocaleString('fa-IR')} تومان` +} + +/** + * Get CSS color for trend direction + */ +export function trendColor(trend: 'up' | 'down' | 'flat'): string { + if (trend === 'up') return 'var(--green-ink)' + if (trend === 'down') return 'var(--red)' + return 'var(--ink-4)' +} + +/** + * Trend arrow character + */ +export function trendArrow(trend: 'up' | 'down' | 'flat'): string { + if (trend === 'up') return '↑' + if (trend === 'down') return '↓' + return '—' +} + +/** + * Truncate string to max length with ellipsis + */ +export function truncate(str: string, max: number): string { + if (str.length <= max) return str + return str.slice(0, max).trimEnd() + '...' +} + +/** + * Get category dot color CSS variable + */ +export function categoryColor(category: string): string { + const map: Record = { + 'بازار جهانی': 'var(--ink)', + 'سیاست‌گذاری': 'var(--ink-3)', + 'انرژی و ESG': 'var(--amber)', + 'صادرات': 'var(--ink-2)', + 'تولید داخلی': 'var(--ink)', + 'ریسک و بحران': 'var(--red)', + } + return map[category] ?? 'var(--ink-5)' +} + +/** + * Get report type label in Persian + */ +export function reportTypeLabel(type: string): string { + const map: Record = { + quarterly: 'گزارش فصلی', + flash: 'Flash Report', + risk: 'گزارش ریسک', + free: 'رایگان', + special: 'گزارش ویژه', + } + return map[type] ?? type +} + +/** + * Clamp a value between min and max + */ +export function clamp(val: number, min: number, max: number): number { + return Math.min(Math.max(val, min), max) +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..83e8d37 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { RouterProvider } from 'react-router-dom' +import './index.css' +import { router } from './app/router' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/pages/Archive/Archive.tsx b/src/pages/Archive/Archive.tsx new file mode 100644 index 0000000..7591b54 --- /dev/null +++ b/src/pages/Archive/Archive.tsx @@ -0,0 +1,430 @@ +import { useState, useMemo } from 'react' +import { Link } from 'react-router-dom' +import { reports } from '@/data/reports' +import type { ReportCategory, ReportType } from '@/data/reports' + +/* ─── constants ──────────────────────────────────────────── */ +const TYPE_LABELS: Record = { + quarterly: 'فصلی', + flash: 'فلش', + risk: 'ریسک', + free: 'رایگان', + special: 'ویژه', +} + +const TYPE_COLORS: Record = { + quarterly: '#166534', + flash: '#9b1c1c', + risk: '#92400e', + free: '#1d4ed8', + special: '#4a1d96', +} + +/* Persian month order for sorting date groups */ +const MONTH_ORDER: Record = { + 'فروردین': 1, 'اردیبهشت': 2, 'خرداد': 3, 'تیر': 4, + 'مرداد': 5, 'شهریور': 6, 'مهر': 7, 'آبان': 8, + 'آذر': 9, 'دی': 10, 'بهمن': 11, 'اسفند': 12, +} + +function parseDateGroup(dateStr: string): number { + const parts = dateStr.trim().split(' ') + if (parts.length < 2) return 0 + const month = MONTH_ORDER[parts[0]] ?? 0 + const year = parseInt(parts[1]) || 0 + return year * 100 + month +} + +/* ─── unique categories from data ───────────────────────── */ +const ALL_CATEGORIES: (ReportCategory | 'همه')[] = [ + 'همه', + ...Array.from(new Set(reports.map((r) => r.category))) as ReportCategory[], +] + +/* ─── price formatter ────────────────────────────────────── */ +function formatPrice(price: number): string { + if (price === 0) return 'رایگان' + return price.toLocaleString('fa-IR') + ' ت' +} + +/* ─── report row ─────────────────────────────────────────── */ +function ReportRow({ report }: { report: (typeof reports)[0] }) { + const [hovered, setHovered] = useState(false) + const typeColor = TYPE_COLORS[report.type] + const typeLabel = TYPE_LABELS[report.type] + + return ( +
    setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {/* type label */} +
    + + {typeLabel} + +
    + + {/* title */} +
    + + {report.title} + +
    + + {/* author */} +
    + + {report.author} + +
    + + {/* pages */} +
    + + {report.pages.toLocaleString('fa-IR')} ص + +
    + + {/* price */} +
    + + {formatPrice(report.price)} + +
    +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Archive() { + const [search, setSearch] = useState('') + const [activeCategory, setActiveCategory] = useState('همه') + + const filtered = useMemo(() => { + let result = [...reports] + + if (activeCategory !== 'همه') { + result = result.filter((r) => r.category === activeCategory) + } + + if (search.trim()) { + const q = search.trim().toLowerCase() + result = result.filter( + (r) => + r.title.toLowerCase().includes(q) || + r.author.toLowerCase().includes(q) || + r.category.toLowerCase().includes(q) || + r.tags.some((t) => t.toLowerCase().includes(q)) + ) + } + + return result + }, [search, activeCategory]) + + /* group by publishDate, sorted newest first */ + const groups = useMemo(() => { + const map = new Map() + for (const report of filtered) { + const key = report.publishDate + if (!map.has(key)) map.set(key, []) + map.get(key)!.push(report) + } + return Array.from(map.entries()).sort( + ([a], [b]) => parseDateGroup(b) - parseDateGroup(a) + ) + }, [filtered]) + + return ( +
    + {/* ─── Page header ───────────────────────────────────── */} +
    +
    +

    + آرشیو گزارش‌ها +

    + + {reports.length.toLocaleString('fa-IR')} گزارش + +
    +
    +
    + + {/* ─── Search ────────────────────────────────────────── */} +
    + setSearch(e.target.value)} + style={{ + width: '100%', + border: '1px solid var(--ink)', + borderRadius: 0, + padding: '12px 18px', + fontSize: 14, + fontFamily: 'inherit', + background: 'transparent', + color: 'var(--ink)', + outline: 'none', + direction: 'rtl', + }} + /> +
    + + {/* ─── Category filter chips ─────────────────────────── */} +
    + {ALL_CATEGORIES.map((cat) => { + const isActive = activeCategory === cat + return ( + + ) + })} +
    + + {/* ─── Date groups ───────────────────────────────────── */} + {groups.length > 0 ? ( +
    + {groups.map(([date, groupReports]) => ( +
    + {/* section divider */} +
    + + {date} + +
    + + {groupReports.length.toLocaleString('fa-IR')} گزارش + +
    + + {/* column headers */} +
    + {[ + { label: 'نوع', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } }, + { label: 'عنوان گزارش', style: { padding: '6px 16px', borderLeft: '1px solid var(--rule-thin)' } }, + { label: 'نویسنده', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } }, + { label: 'صفحه', style: { padding: '6px 10px', borderLeft: '1px solid var(--rule-thin)', textAlign: 'center' as const } }, + { label: 'قیمت', style: { padding: '6px 12px', textAlign: 'left' as const } }, + ].map((col) => ( +
    + + {col.label} + +
    + ))} +
    + + {/* report rows */} +
    + {groupReports.map((report) => ( + + ))} +
    +
    + ))} +
    + ) : ( +
    +

    نتیجه‌ای یافت نشد

    +

    + عبارت جستجو یا فیلتر دسته‌بندی را تغییر دهید +

    +
    + )} + + {/* responsive: hide author/pages columns on small screens */} + +
    + ) +} diff --git a/src/pages/Events/Events.tsx b/src/pages/Events/Events.tsx new file mode 100644 index 0000000..6ad07f2 --- /dev/null +++ b/src/pages/Events/Events.tsx @@ -0,0 +1,367 @@ +import { useState } from 'react' +import { events } from '@/data/events' +import type { EventType } from '@/data/events' + +/* ─── constants ──────────────────────────────────────────── */ +type FilterValue = 'همه' | EventType + +const FILTERS: { label: string; value: FilterValue }[] = [ + { label: 'همه', value: 'همه' }, + { label: 'کنگره', value: 'conference' }, + { label: 'نمایشگاه', value: 'exhibition' }, + { label: 'سمینار', value: 'seminar' }, + { label: 'جهانی', value: 'international' }, +] + +const TYPE_LABELS: Record = { + conference: 'کنگره', + exhibition: 'نمایشگاه', + seminar: 'سمینار', + international: 'جهانی', +} + +const TYPE_BORDER_COLORS: Record = { + conference: 'var(--ink)', + exhibition: '#6b4a00', + seminar: '#1a4a2a', + international: 'var(--red)', +} + +/* ─── event card ─────────────────────────────────────────── */ +function EventCard({ event }: { event: (typeof events)[0] }) { + const [hovered, setHovered] = useState(false) + const borderColor = TYPE_BORDER_COLORS[event.type] + + return ( +
    setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {/* date + type header */} +
    + {/* date block */} +
    + + {event.date.toLocaleString('fa-IR')} + + + {event.month} {event.year.toLocaleString('fa-IR')} + +
    + + {/* type badge + title area top */} +
    + + {TYPE_LABELS[event.type]} + + +

    + {event.title} +

    +
    +
    + + {/* body */} +
    + {/* location */} +
    + + + {event.location}، {event.city} + +
    + + {/* description */} +

    + {event.description} +

    +
    + + {/* footer */} +
    + {event.registrationOpen ? ( + + ثبت‌نام + + + ) : ( + + به‌زودی + + )} +
    +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Events() { + const [activeFilter, setActiveFilter] = useState('همه') + + const filtered = activeFilter === 'همه' + ? events + : events.filter((e) => e.type === activeFilter) + + return ( +
    + {/* ─── Page title banner ─────────────────────────────── */} +
    +
    +

    + رویدادهای پیش‌رو +

    +
    + +
    + +
    + + ۱۴۰۳–۱۴۰۴ + +
    +
    + + {/* ─── Filter tabs ───────────────────────────────────── */} +
    + {FILTERS.map((f) => { + const isActive = activeFilter === f.value + return ( + + ) + })} +
    + + {/* ─── Events grid ───────────────────────────────────── */} +
    + {filtered.map((event) => ( + + ))} + {/* fill odd trailing cell */} + {filtered.length % 2 !== 0 && ( +
    + )} +
    + + {filtered.length === 0 && ( +
    +

    رویدادی در این دسته‌بندی یافت نشد

    +
    + )} + + {/* responsive */} + +
    + ) +} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx new file mode 100644 index 0000000..700356d --- /dev/null +++ b/src/pages/Home/Home.tsx @@ -0,0 +1,27 @@ +import HeroSection from './sections/HeroSection' +import SnapshotBar from './sections/SnapshotBar' +import GlobalScannerSection from './sections/GlobalScannerSection' +import ReportsCarouselSection from './sections/ReportsCarouselSection' +import LatestNewsBentoSection from './sections/LatestNewsBentoSection' +import EventsSection from './sections/EventsSection' +import RiskSection from './sections/RiskSection' +import PlansSection from './sections/PlansSection' +import TeamSection from './sections/TeamSection' +import NewsletterSection from './sections/NewsletterSection' + +export default function Home() { + return ( +
    + + + + + + + + + + +
    + ) +} diff --git a/src/pages/Home/sections/EventsSection.tsx b/src/pages/Home/sections/EventsSection.tsx new file mode 100644 index 0000000..4922c22 --- /dev/null +++ b/src/pages/Home/sections/EventsSection.tsx @@ -0,0 +1,104 @@ +import { Link } from 'react-router-dom' +import { events } from '@/data/events' +import type { Event } from '@/data/events' + +const typeLabel: Record = { + conference: 'کنگره', + exhibition: 'نمایشگاه', + seminar: 'سمینار', + international: 'جهانی', +} + +const typeBorderColor: Record = { + conference: 'var(--ink)', + exhibition: '#6b4a00', + seminar: '#1a4a2a', + international: 'var(--red)', +} + +const typeTextColor: Record = { + conference: 'var(--ink)', + exhibition: '#6b4a00', + seminar: '#1a4a2a', + international: 'var(--red)', +} + +export default function EventsSection() { + const displayed = events.slice(0, 4) + + return ( +
    +
    + {/* Banner */} +
    +
    رویدادهای پیش‌رو
    +
    + تقویم کامل ← +
    + + {/* 4-col grid */} +
    + {displayed.map((event, idx) => ( +
    + {/* Date block */} +
    +
    + {event.date.toLocaleString('fa-IR')} +
    +
    + {event.month} {event.year.toLocaleString('fa-IR')} +
    +
    + + {/* Type pill */} +
    + + {typeLabel[event.type]} + +
    + + {/* Title */} +
    {event.title}
    + + {/* Location */} +
    {event.location} · {event.city}
    + + {/* CTA */} + {event.registrationOpen ? ( + + ثبت‌نام ← + + ) : ( + + به‌زودی + + )} +
    + ))} +
    +
    +
    + ) +} diff --git a/src/pages/Home/sections/GlobalScannerSection.tsx b/src/pages/Home/sections/GlobalScannerSection.tsx new file mode 100644 index 0000000..cf8709a --- /dev/null +++ b/src/pages/Home/sections/GlobalScannerSection.tsx @@ -0,0 +1,180 @@ +import { motion } from 'framer-motion' +import WorldMap from '@/components/ui/world-map' + +/* ─── Steel trade routes ───────────────────────────────────────────── + Arcs show geopolitically relevant steel flows affecting Iran's market +──────────────────────────────────────────────────────────────────── */ +const ROUTES = [ + // ── Iran's trade flows (2 routes) ─────────────────────────────── + // Iran → Iraq (main export market) + { start: { lat: 35.69, lng: 51.39 }, end: { lat: 33.31, lng: 44.36 } }, + // Iran → Central Asia (Uzbekistan) + { start: { lat: 35.69, lng: 51.39 }, end: { lat: 41.30, lng: 69.24 } }, + + // ── China's global reach (3 routes) ───────────────────────────── + // Shanghai → Rotterdam (European market dumping) + { start: { lat: 31.23, lng: 121.47 }, end: { lat: 51.92, lng: 4.47 } }, + // Shanghai → Lagos (China–Africa infrastructure push) + { start: { lat: 31.23, lng: 121.47 }, end: { lat: 6.45, lng: 3.39 } }, + // Shanghai → Singapore (SE Asia dominance) + { start: { lat: 31.23, lng: 121.47 }, end: { lat: 1.35, lng: 103.82 } }, + + // ── Russia flows (2 routes) ────────────────────────────────────── + // Moscow → Beijing (raw material / billet supply) + { start: { lat: 55.75, lng: 37.62 }, end: { lat: 39.91, lng: 116.39 } }, + // Moscow → Tehran (billet supply to Iran) + { start: { lat: 55.75, lng: 37.62 }, end: { lat: 35.69, lng: 51.39 } }, + + // ── India's competing routes (2 routes) ────────────────────────── + // Mumbai → Dubai (competing with Iran in Gulf) + { start: { lat: 19.07, lng: 72.88 }, end: { lat: 25.20, lng: 55.27 } }, + // Mumbai → Mombasa (East Africa — directly competes with Iran) + { start: { lat: 19.07, lng: 72.88 }, end: { lat: -4.05, lng: 39.67 } }, + + // ── Americas → Asia (iron ore flows) ──────────────────────────── + // São Paulo → Shanghai (Brazilian iron ore) + { start: { lat: -23.55, lng: -46.63 }, end: { lat: 31.23, lng: 121.47 } }, + + // ── South Korea → SE Asia ─────────────────────────────────────── + { start: { lat: 37.56, lng: 126.97 }, end: { lat: 1.35, lng: 103.82 } }, + + // ── Turkey → Europe ────────────────────────────────────────────── + // Istanbul → Berlin (Turkey as EU steel supplier) + { start: { lat: 41.01, lng: 28.97 }, end: { lat: 52.52, lng: 13.40 } }, + + // ── Australia → China (iron ore) ──────────────────────────────── + { start: { lat: -33.87, lng: 151.21 }, end: { lat: 31.23, lng: 121.47 } }, +] + +/* ─── Section ────────────────────────────────────────── */ +export default function GlobalScannerSection() { + return ( +
    + {/* ── Text header ────────────────────────────────── */} +
    + {/* Overline */} +
    +
    + + اسکنر جهانی · پوشش ۲۴ ساعته + +
    +
    + + {/* Headline */} +

    + تمام رویدادهای فولادی دنیا{' '} + + را رصد می‌کنیم + +

    + + {/* Subhead */} +

    + از بورس دالیان چین تا گمرک بصره عراق، از تصمیمات کمیته فولاد OECD + تا ظرفیت‌سازی‌های هند — هر سیگنالی که بر رقابت‌پذیری ایران اثر + می‌گذارد، پیش از بقیه به دست شما می‌رسد. +

    +
    + + {/* ── World map (full width, no side padding) ─── */} +
    + +
    + + {/* ── Stats strip ──────────────────────────────── */} +
    + {[ + { n: '۱۸۰+', label: 'بازار رصد‌شده', sub: 'در ۵ قاره' }, + { n: '۷/۲۴', label: 'پایش لحظه‌ای', sub: 'قیمت و حجم' }, + { n: '۴۰+', label: 'منبع داده', sub: 'بورس‌ها و گمرک‌ها' }, + { n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذره‌بین' }, + ].map((s, i) => ( + 0 ? '1px solid var(--rule-thin)' : 'none', + textAlign: 'center', + }} + className="max-md:border-r-0 max-md:border-b max-md:border-[var(--rule-thin)]" + > +
    + {s.n} +
    +
    + {s.label} +
    +
    + {s.sub} +
    +
    + ))} +
    +
    + ) +} diff --git a/src/pages/Home/sections/HeroSection.tsx b/src/pages/Home/sections/HeroSection.tsx new file mode 100644 index 0000000..1d96fa7 --- /dev/null +++ b/src/pages/Home/sections/HeroSection.tsx @@ -0,0 +1,315 @@ +import { useState } from 'react' +import { motion } from 'framer-motion' +import { reports } from '@/data/reports' + +const featured = reports.find(r => r.featured)! + +/* Dramatic steel mill / industrial image */ +const BG_IMAGE = + 'https://images.unsplash.com/photo-1504328345606-18bbc8c9d7d1?q=85&w=2400&auto=format&fit=crop' + +const STATS = [ + { n: '۴', label: 'سناریو محتمل' }, + { n: '۱۵', label: 'کشور رقیب' }, + { n: '۸۴', label: 'صفحه تحلیلی' }, + { n: '۱۲', label: 'توصیه سیاستی' }, +] + +export default function HeroSection() { + const [buyHov, setBuyHov] = useState(false) + const [freeHov, setFreeHov] = useState(false) + + return ( +
    + {/* ── Background image ── */} + + + {/* ── Gradient overlay — dark vignette from bottom-right ── */} +
    + + {/* ── Top-right badge ── */} +
    +
    + گزارش ویژه +
    + + بهمن ۱۴۰۳ + +
    + + {/* ── Main content — anchored bottom ── */} +
    + + {/* ── Left: headline + meta ── */} +
    + {/* Overline */} + +
    + + گزارش راهبردی · تحلیل سناریو + + + + {/* Headline */} + + {featured.title.split(':').map((part, i) => ( + + {i === 0 ? part + ':' : ( + {part} + )} + {i === 0 &&
    } +
    + ))} +
    + + {/* Summary — one line */} + + مدل‌سازی کمی بر اساس داده‌های تجارت ۱۵ کشور رقیب — از بدبینانه تا خوش‌بینانه، + با توصیه‌های سیاستی مستقیم برای وزارت صمت و سازمان بنادر. + + + {/* Divider */} +
    + + {/* Author + Price + CTA in one bar */} + + {/* Author */} +
    +
    + {featured.authorInitial} +
    +
    +
    + {featured.author} +
    +
    + {featured.authorRole} +
    +
    +
    + + {/* Separator */} +
    + + {/* Price */} +
    + + {featured.price.toLocaleString('fa-IR')} + + تومان +
    + + {/* Buttons */} +
    + + +
    + +
    + + {/* ── Right: floating stats panel ── */} + +
    + در این گزارش +
    + {STATS.map((s, i) => ( +
    + + {s.label} + + + {s.n} + +
    + ))} +
    + +
    +
    + ) +} diff --git a/src/pages/Home/sections/LatestNewsBentoSection.tsx b/src/pages/Home/sections/LatestNewsBentoSection.tsx new file mode 100644 index 0000000..d81eb99 --- /dev/null +++ b/src/pages/Home/sections/LatestNewsBentoSection.tsx @@ -0,0 +1,240 @@ +import type { CSSProperties } from 'react' +import { motion } from 'framer-motion' +import { reports } from '@/data/reports' + +const featured = reports.find(r => r.featured)! +const flashReport = reports.find(r => r.type === 'flash')! + +/* ─── Price snapshot ────────────────────────────────────── */ +const PRICES = [ + { label: 'HRC آسیا', value: '۵۹۰', delta: '+۳.۲٪', up: true }, + { label: 'میلگرد ایران', value: '۲۱,۸۰۰', delta: '+۰.۸٪', up: true }, + { label: 'سنگ‌آهن دالیان', value: '۱۰۸', delta: '+۱۸٪', up: true }, + { label: 'HRC اروپا', value: '۶۲۴', delta: '−۱.۴٪', up: false }, +] + +/* ─── Shared card style (NO gridColumn here — set via className) */ +const CARD: CSSProperties = { + background: 'var(--paper)', + padding: '28px 28px 24px', + display: 'flex', + flexDirection: 'column', + direction: 'rtl', + fontFamily: 'Vazir, sans-serif', + position: 'relative', +} + +function Tag({ text, color = 'var(--red)' }: { text: string; color?: string }) { + return ( +
    + {text} +
    + ) +} + +function Accent({ color = 'var(--red)' }: { color?: string }) { + return ( +
    + ) +} + +/* ─── Section ────────────────────────────────────────────── */ +export default function LatestNewsBentoSection() { + return ( +
    + + {/* ── Header ── */} +
    +
    +
    + آخرین اخبار +
    +

    + رصد روزانه بازار و صنعت +

    +
    + بهمن ۱۴۰۳ +
    +
    + + {/* ── Bento grid ── */} + + {/* + 3-col grid on desktop, 1-col on mobile. + Items ① and ⑥ use col-span-1 md:col-span-2. + NO gridColumn in inline style — inline style overrides Tailwind. + */} +
    + + {/* ① Featured report — col-span-2 on desktop ─── */} +
    + + + {/* Ghost number */} +
    01
    +

    + {featured.title} +

    +

    + {featured.summary} +

    + {/* Footer */} +
    +
    + {featured.authorInitial} +
    +
    +
    {featured.author}
    +
    {featured.publishDate} · {featured.pages} صفحه
    +
    +
    + خواندن ← +
    +
    + + {/* ② Market prices ─────────────────────────────── */} +
    + + +
    + آخرین نرخ‌های بازار +
    +
    + {PRICES.map(p => ( +
    + {p.label} +
    + {p.value} + {p.delta} +
    +
    + ))} +
    +
    + + {/* ③ Flash report ──────────────────────────────── */} +
    + + +
    + +۱۸٪ +
    +

    + جهش قیمت سنگ‌آهن در بورس دالیان +

    +

    + برآورد اثر بر هزینه تمام‌شده تولیدکنندگان DRI ایران +

    +
    + + {/* ④ Event ─────────────────────────────────────── */} +
    + + +
    +
    +
    ۲۵
    +
    بهمن
    +
    +
    +
    تهران
    +
    هتل اسپیناس پالاس
    +
    +
    +

    + همایش ملی فولاد ایران ۱۴۰۳ +

    +

    + نشست سالانه تولیدکنندگان، صادرکنندگان و سیاست‌گذاران +

    +
    + + {/* ⑤ Risk alert ────────────────────────────────── */} +
    + +
    +
    + + هشدار ریسک · بحرانی + +
    +
    + دی ۱۴۰۳ +
    +

    + مسدود شدن ۳ مسیر پرداخت صادراتی +

    +

    + بسته تحریمی جدید مسیرهای بانکی صادرات فولاد را محدود کرده است +

    +
    + + {/* ⑥ Expert quote — col-span-2 on desktop ────── */} +
    + + +
    "
    +

    + فولاد ایران در آستانه تصمیمی تاریخی است: یا باید بازارهای جایگزین را با + جدیت دنبال کند، یا شاهد کاهش تدریجی سهم صادراتی خود در خاورمیانه باشد. +

    +
    +
    م
    +
    +
    دکتر علی محمدی
    +
    مدیر ارشد پژوهش · اندیشکده فولاد
    +
    +
    +
    + + {/* ⑦ Key stat ──────────────────────────────────── */} +
    + + +
    + ۵۴M +
    +
    + تن — صادرات فولاد چین +
    +

    + در ۶ ماه اول ۲۰۲۴ — رکوردی که قیمت‌های آسیای غربی را ۱۲٪ کاهش داد +

    +
    + +
    + +
    + ) +} diff --git a/src/pages/Home/sections/LatestReportsSection.tsx b/src/pages/Home/sections/LatestReportsSection.tsx new file mode 100644 index 0000000..75b2df8 --- /dev/null +++ b/src/pages/Home/sections/LatestReportsSection.tsx @@ -0,0 +1,251 @@ +import { useState } from 'react' +import { Link } from 'react-router-dom' +import { SectionBanner } from '@/components/ui/SectionBanner' +import { reports, type Report } from '@/data/reports' + +const displayReports = reports.filter(r => !r.featured).slice(0, 6) + +const typeLabel: Record = { + quarterly: 'گزارش فصلی', + flash: 'فوری', + risk: 'ریسک', + free: 'رایگان', + special: 'ویژه', +} + +const dotColor: Record = { + quarterly: 'var(--ink)', + flash: 'var(--red)', + risk: 'var(--red)', + free: 'var(--green-ink)', + special: 'var(--amber)', +} + +function formatPrice(n: number) { + return n.toLocaleString('fa-IR') +} + +interface ReportCardProps { + report: Report + colIndex: number + rowIndex: number + totalRows: number +} + +function ReportCard({ report, colIndex, rowIndex, totalRows }: ReportCardProps) { + const [hovered, setHovered] = useState(false) + const isLastRow = rowIndex === totalRows - 1 + const isRightCol = colIndex === 0 // RTL: right col is index 0 + + return ( +
    setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {/* Type badge */} +
    +
    + + {typeLabel[report.type] ?? report.type} · {report.category} + +
    + + {/* Title */} + +

    + {report.title} +

    + + + {/* Deck */} +

    + {report.summary.slice(0, 140)} + {report.summary.length > 140 ? '…' : ''} +

    + + {/* Divider */} +
    + + {/* Footer row */} +
    + {/* Price block */} +
    + {report.isFree ? ( + + دانلود رایگان + + ) : ( + + {formatPrice(report.price)} + + تومان + + + )} +
    + {report.pages} صفحه · {report.publishDate} +
    +
    + + {/* Buy link */} + + (e.currentTarget.style.textDecoration = 'underline') + } + onMouseLeave={e => (e.currentTarget.style.textDecoration = 'none')} + > + {report.isFree ? 'دانلود' : 'خرید گزارش'} ← + +
    +
    + ) +} + +export default function LatestReportsSection() { + const rows = Math.ceil(displayReports.length / 2) + + return ( +
    + {/* Section header */} +
    + +
    + + {/* 2-col grid */} +
    + {displayReports.map((report, idx) => { + const colIndex = idx % 2 + const rowIndex = Math.floor(idx / 2) + return ( + + ) + })} +
    +
    + ) +} diff --git a/src/pages/Home/sections/NewsletterSection.tsx b/src/pages/Home/sections/NewsletterSection.tsx new file mode 100644 index 0000000..fd6c249 --- /dev/null +++ b/src/pages/Home/sections/NewsletterSection.tsx @@ -0,0 +1,103 @@ +import { useState } from 'react' + +export default function NewsletterSection() { + const [email, setEmail] = useState('') + const [submitted, setSubmitted] = useState(false) + + function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (email.trim()) { + setSubmitted(true) + setEmail('') + } + } + + return ( +
    + {/* Outer grid — strip inline padding, use Tailwind */} +
    + {/* Left: description */} +
    +
    + خبرنامه اندیشکده +
    +

    + هر هفته: خلاصه تحولات بازار فولاد، گزارش‌های جدید و هشدارهای فوری — مستقیم در صندوق ورودی شما +

    +
    + ↑ بیش از ۲,۸۰۰ مدیر، مقام دولتی و کارشناس عضو +
    +
    + + {/* Divider */} +
    + + {/* Right: form */} +
    + {submitted ? ( +
    + ✓ عضویت شما با موفقیت ثبت شد. از همراهی شما متشکریم. +
    + ) : ( +
    + + setEmail(e.target.value)} + placeholder="ایمیل سازمانی شما" + required + autoComplete="email" + style={{ + flex: 1, + padding: '12px 14px', + border: '1px solid var(--ink)', + borderLeft: 'none', + fontFamily: 'Vazir, sans-serif', + fontSize: 13, + background: 'var(--paper)', + color: 'var(--ink)', + outline: 'none', + borderRadius: 0, + minWidth: 0, + }} + /> + +
    + )} +

    اطلاعات شما محرمانه است و فروخته نمی‌شود

    +
    +
    +
    + ) +} diff --git a/src/pages/Home/sections/PlansSection.tsx b/src/pages/Home/sections/PlansSection.tsx new file mode 100644 index 0000000..31a99a5 --- /dev/null +++ b/src/pages/Home/sections/PlansSection.tsx @@ -0,0 +1,338 @@ +import { useState } from 'react' +import { plans } from '@/data/plans' + +/* ─── Checkmark icon ─────────────────────────────────── */ +function Check({ featured }: { featured: boolean }) { + return ( + + + + + ) +} + +/* ─── Individual plan card ───────────────────────────── */ +function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boolean }) { + const [hovered, setHovered] = useState(false) + const featured = plan.featured + + return ( +
    + {/* Badge */} + {plan.badge && ( +
    + {plan.badge} +
    + )} + + {/* Plan name */} +
    + {plan.name} +
    + + {/* Tagline */} +
    + {plan.id === 'individual' && 'برای کارشناسان و محققان'} + {plan.id === 'organizational' && 'برای شرکت‌ها و سازمان‌ها'} + {plan.id === 'ministry' && 'برای نهادهای دولتی'} +
    + + {/* Price */} +
    + + {plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')} + + {plan.price > 0 && ( + + تومان + + )} +
    +
    + {plan.period} +
    + + {/* CTA button */} + + + {/* Divider */} +
    + + {/* Features */} +
      + {plan.features.map(f => ( +
    • + + + {f} + +
    • + ))} +
    +
    + ) +} + +/* ─── Enterprise bottom card ─────────────────────────── */ +const enterpriseFeatures = [ + 'پروژه‌های نامحدود', + 'پشتیبانی تیمی اختصاصی', + 'فاکتور رسمی سازمانی', + 'بدون سقف توکن یا داده', + 'دعوت اعضای نامحدود', + 'قرارداد SLA معتبر', + 'نقش‌های سفارشی کاربری', + 'امنیت پیشرفته سازمانی', + 'API اتصال به سیستم داخلی', + 'گزارش‌دهی اختصاصی', + 'کارگاه‌های تخصصی دوره‌ای', + 'مشاوره راهبردی مستقیم', +] + +function EnterpriseCard() { + const [hov, setHov] = useState(false) + return ( +
    + {/* Left: description */} +
    +
    + وزارتخانه / سازمان دولتی +
    +

    + راهکار سازمانی کامل +

    +

    + به راهکارهای سفارشی، پشتیبانی اختصاصی یا قیمت‌گذاری حجمی نیاز دارید؟ + با کارشناسان ما درباره پلن منحصربه‌فرد سازمانتان صحبت کنید. +

    + +
    + + {/* Right: feature grid */} +
    + {enterpriseFeatures.map(f => ( +
    + + + {f} + +
    + ))} +
    +
    + ) +} + +/* ─── Section ────────────────────────────────────────── */ +export default function PlansSection() { + const allThree = plans + + return ( +
    +
    + + {/* Header */} +
    +
    + اشتراک و دسترسی +
    +

    + پلن مناسب خود را انتخاب کنید +

    +

    + از دسترسی پایه برای پژوهشگران تا راهکار کامل برای وزارتخانه‌ها — + هر سطحی از نیاز را پوشش می‌دهیم. +

    +
    + + {/* 3-col plan cards */} +
    + {allThree.map((plan, idx) => ( + + ))} +
    + + {/* Enterprise bottom card */} + + + {/* Money-back note */} +
    + ✓   ضمانت بازگشت وجه ۱۴ روزه  ·  بدون قرارداد اجباری +
    +
    +
    + ) +} diff --git a/src/pages/Home/sections/ReportsCarouselSection.tsx b/src/pages/Home/sections/ReportsCarouselSection.tsx new file mode 100644 index 0000000..e6ea3fd --- /dev/null +++ b/src/pages/Home/sections/ReportsCarouselSection.tsx @@ -0,0 +1,279 @@ +import { Link } from 'react-router-dom' +import { Carousel, Card } from '@/components/ui/apple-cards-carousel' +import type { CardData } from '@/components/ui/apple-cards-carousel' +import { reports } from '@/data/reports' +import type { Report } from '@/data/reports' + +/* ─── Per-report images (industrial, steel-relevant) ─ */ +const reportImage: Record = { + 'iran-steel-global-trade-scenarios-1404': + 'https://images.unsplash.com/photo-1578662996442-48f60103fc96?q=80&w=1600&fit=crop', + 'steel-energy-consumption-efficiency-1403': + 'https://images.unsplash.com/photo-1473341304170-971dccb5ac1e?q=80&w=1600&fit=crop', + 'global-steel-market-q3-1403': + 'https://images.unsplash.com/photo-1504328345606-18bbc8c9d7d1?q=80&w=1600&fit=crop', + 'iran-steel-export-sanctions-impact-1403': + 'https://images.unsplash.com/photo-1494412519320-aa613dfb7738?q=80&w=1600&fit=crop', + 'flash-iron-ore-price-surge-1403': + 'https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3?q=80&w=1600&fit=crop', + 'domestic-production-dri-outlook-1403': + 'https://images.unsplash.com/photo-1567789884554-0b844b597180?q=80&w=1600&fit=crop', + 'esg-carbon-emissions-steel-iran-1403': + 'https://images.unsplash.com/photo-1466611653911-95081537e5b7?q=80&w=1600&fit=crop', + 'policy-steel-pricing-reform-1403': + 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=1600&fit=crop', + 'flash-china-steel-overcapacity-1403': + 'https://images.unsplash.com/photo-1570168007204-dfb528c6958f?q=80&w=1600&fit=crop', +} + +const fallbackImage = + 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?q=80&w=1600&fit=crop' + +const typeLabel: Record = { + quarterly: 'گزارش فصلی', + flash: 'گزارش فوری', + risk: 'گزارش ریسک', + free: 'رایگان', + special: 'گزارش ویژه', +} + +const typeColor: Record = { + flash: 'var(--red)', + risk: '#92400e', + free: '#166534', + quarterly: 'var(--ink-4)', + special: 'var(--ink)', +} + +/* ─── Key findings per category (mock but plausible) ─ */ +const keyFindings: Record = { + 'بازار جهانی': [ + 'کاهش ۱۲٪ تقاضای چین در نیمه دوم ۲۰۲۴ فشار قیمتی جهانی ایجاد کرده', + 'هند با رشد ۱۸٪ ظرفیت تولید، رقیب اصلی ایران در بازارهای منطقه شده', + 'پیش‌بینی بازگشت تقاضا در Q2 2025 با ریکاوری بخش ساختمان آمریکا', + ], + 'انرژی و ESG': [ + 'شکاف مصرف انرژی واحدهای ایرانی ۲۵٪ بیشتر از میانگین جهانی است', + 'اجرای CBAM از ۲۰۲۶ می‌تواند ۱۵٪ رقابت‌پذیری صادرات را کاهش دهد', + 'مسیر هیدروژن سبز برای ایران تا ۱۴۱۰ اقتصادی نخواهد بود', + ], + 'صادرات': [ + 'عراق، افغانستان و پاکستان ۶۵٪ صادرات فولاد ایران را تشکیل می‌دهند', + 'مسیرهای جایگزین آفریقای شرقی پتانسیل ۲ میلیون تن اضافی دارد', + 'تحریم‌های جدید ۳ مسیر اصلی پرداخت را مسدود کرده است', + ], + 'تولید داخلی': [ + 'ایران با ۳۰ میلیون تن DRI رتبه اول جهانی را حفظ کرده است', + 'کمبود گاز زمستانی می‌تواند تا ۲۰٪ ظرفیت تولید را کاهش دهد', + '۸ پروژه در دست احداث تا ۱۴۰۵ به ظرفیت می‌رسند', + ], + 'سیاست‌گذاری': [ + 'قیمت‌گذاری دستوری هزینه ۱.۲ میلیارد دلار در سال به صنعت تحمیل می‌کند', + 'تجربه ترکیه نشان می‌دهد آزادسازی تدریجی قیمت بهترین مسیر است', + 'اصلاح تعرفه‌های سنگ‌آهن می‌تواند ۸٪ به حاشیه سود اضافه کند', + ], + 'ریسک و بحران': [ + 'احتمال تشدید تحریم‌ها در ۶ ماه آینده ۶۵٪ برآورد می‌شود', + 'ریسک کمبود ارز برای واردات مواد اولیه در سطح بالا قرار دارد', + 'شکست مذاکرات هسته‌ای می‌تواند ۳۰٪ صادرات را تحت تأثیر قرار دهد', + ], +} + +/* ─── Report contents (TOC-style) ───────────────────── */ +const reportContents: Record = { + quarterly: ['مروری بر بازار فصل', 'تحلیل قیمت‌ها و حجم معاملات', 'رویدادهای کلیدی', 'پیش‌بینی فصل آینده', 'داده‌های خام ضمیمه'], + flash: ['خلاصه اجرایی', 'رویداد و محرک', 'تأثیر بر بازار داخلی', 'توصیه‌های فوری'], + risk: ['ارزیابی ریسک', 'تحلیل سناریو', 'احتمال‌سنجی', 'راهکارهای پوشش ریسک', 'پیوست داده'], + free: ['خلاصه یافته‌ها', 'روش‌شناسی', 'مقایسه بین‌المللی', 'توصیه‌های سیاستی'], + special: ['تحلیل جامع', 'سناریوسازی کمی', 'مقایسه تطبیقی', 'توصیه‌های راهبردی', 'مدل Excel پیوست'], +} + +/* ─── Expanded card body ─────────────────────────────── */ +function ReportCardContent({ report }: { report: Report }) { + const findings = keyFindings[report.category] ?? [] + const contents = reportContents[report.type] ?? [] + + return ( +
    + + {/* Tags */} +
    + {report.tags.map(tag => ( + + {tag} + + ))} +
    + + {/* Summary */} +

    + {report.summary} +

    + + {/* Key findings */} + {findings.length > 0 && ( +
    +
    + یافته‌های کلیدی +
    +
    + {findings.map((f, i) => ( +
    +
    + {f} +
    + ))} +
    +
    + )} + + {/* Report contents */} + {contents.length > 0 && ( +
    +
    + محتوای این گزارش +
    +
    + {contents.map((item, i) => ( +
    + + {(i + 1).toLocaleString('fa-IR')} + + {item} +
    + ))} +
    +
    + )} + + {/* Meta grid */} +
    + {[ + { label: 'نویسنده', value: report.author }, + { label: 'صفحات', value: `${report.pages} صفحه` }, + { label: 'تاریخ انتشار', value: report.publishDate }, + ].map(item => ( +
    +
    + {item.label} +
    +
    {item.value}
    +
    + ))} +
    + + {/* Author */} +
    +
    + {report.authorInitial} +
    +
    +
    {report.author}
    +
    {report.authorRole}
    +
    +
    + + {/* CTA */} +
    + {report.isFree ? ( + + دانلود رایگان + + ) : ( + <> + + + مشاهده جزئیات ← + + + )} +
    +
    + ) +} + +/* ─── Section ────────────────────────────────────────── */ +export default function ReportsCarouselSection() { + const carouselReports = reports.filter(r => !r.featured).slice(0, 10) + + const cards: CardData[] = carouselReports.map(report => ({ + src: reportImage[report.id] ?? fallbackImage, + title: report.title, + category: typeLabel[report.type] ?? report.type, + content: , + })) + + const cardElements = cards.map((card, index) => ( + + )) + + return ( +
    + {/* Header */} +
    +
    +
    + تحلیل‌های اخیر +
    +
    + + همه گزارش‌ها ← + +
    +
    + + {/* Legend */} +
    +
    + {Object.entries(typeLabel).map(([key, label]) => ( +
    +
    + {label} +
    + ))} +
    +
    + + +
    + ) +} diff --git a/src/pages/Home/sections/RiskSection.tsx b/src/pages/Home/sections/RiskSection.tsx new file mode 100644 index 0000000..a934557 --- /dev/null +++ b/src/pages/Home/sections/RiskSection.tsx @@ -0,0 +1,264 @@ +import { useEffect, useRef, useState } from 'react' +import { Link } from 'react-router-dom' + +/* ─── Risk data ──────────────────────────────────────────── */ +const riskItems = [ + { + quote: 'چین در نیمه اول ۲۰۲۴ رکورد ۵۴ میلیون تن صادرات فولاد را شکست. این موج صادراتی قیمت بازارهای آسیای غربی را ۱۲٪ کاهش داده و مستقیماً سهم ایران در عراق را تهدید می‌کند.', + name: 'بازار جهانی — چین', + date: 'بهمن ۱۴۰۳', + level: 'high' as const, + }, + { + quote: 'اجرای مکانیسم CBAM اتحادیه اروپا از ۲۰۲۶ برای صنایع فولاد آغاز می‌شود. محصولات ایرانی با ضریب کربن بالاتر از میانگین جهانی، در بازار اروپا با افزایش هزینه ۱۵ تا ۲۲ دلار به ازای هر تن مواجه خواهند شد.', + name: 'مقررات — اتحادیه اروپا', + date: 'افق: ۲۰۲۶', + level: 'medium' as const, + }, + { + quote: 'بسته تحریمی جدید، ۳ مسیر اصلی پرداخت بین‌المللی شرکت‌های فولادی ایران را مسدود کرده است. ریسک تأخیر در دریافت وجوه صادراتی به بالاترین سطح در ۵ سال اخیر رسیده است.', + name: 'ریسک ژئوپلیتیک — تحریم', + date: 'دی ۱۴۰۳', + level: 'critical' as const, + }, + { + quote: 'هند با افزایش ۱۸٪ ظرفیت تولید فولاد در سال ۲۰۲۴، به سومین تولیدکننده بزرگ جهان تبدیل شد. رقابت هند در بازارهای هدف ایران شامل عراق، افغانستان و آفریقای شرقی به‌شدت تشدید شده.', + name: 'رقابت — هند', + date: 'افق میان‌مدت', + level: 'high' as const, + }, + { + quote: 'محدودیت‌های زمستانی گاز صنعتی در ۳ ماه گذشته باعث توقف موقت ۱۴ واحد تولیدی فولاد در اصفهان، خراسان و هرمزگان شده است. ریسک کمبود انرژی همچنان ادامه دارد.', + name: 'زیرساخت — انرژی داخلی', + date: 'پایش مستمر', + level: 'high' as const, + }, + { + quote: 'نرخ برابری ریال در برابر دلار در ۳ ماه اخیر ۸٪ کاهش یافته است. این نوسان از یک سو هزینه واردات مواد اولیه را افزایش و از سوی دیگر رقابت‌پذیری صادراتی را بهبود می‌بخشد.', + name: 'ریسک مالی — ارز', + date: 'بهمن ۱۴۰۳', + level: 'medium' as const, + }, + { + quote: 'کاهش واردات فولاد ترکیه از بازار عراق، فرصت افزایش سهم ایران از ۴۰٪ به ۵۵٪ را فراهم کرده است. پنجره فرصت تا اواخر سال ۱۴۰۴ باز است.', + name: 'فرصت — بازار عراق', + date: 'افق: ۱۴۰۴', + level: 'opportunity' as const, + }, + { + quote: 'کشورهای آفریقای شرقی (کنیا، اتیوپی، تانزانیا) در مجموع ۸ میلیون تن واردات فولاد در سال دارند. سهم فعلی ایران تنها ۳٪ است در حالی که زیرساخت لجستیکی برای افزایش به ۱۵٪ وجود دارد.', + name: 'فرصت — آفریقای شرقی', + date: 'بررسی استراتژیک', + level: 'opportunity' as const, + }, + { + quote: 'قیمت سنگ‌آهن در بورس دالیان در ماه اخیر ۱۸٪ جهش داشته است. این افزایش مستقیماً هزینه تمام‌شده واحدهای وابسته به واردات سنگ‌آهن را متأثر می‌کند.', + name: 'کامودیتی — سنگ‌آهن', + date: 'آبان ۱۴۰۳', + level: 'medium' as const, + }, + { + quote: 'روسیه پس از تحریم‌های غرب، مسیرهای صادراتی خود را به آسیا و خاورمیانه تغییر داده و با قیمت‌های پایین‌تر از نرخ جهانی وارد بازارهای هدف ایران شده است.', + name: 'رقابت — روسیه', + date: 'در حال پایش', + level: 'high' as const, + }, +] + +const LEVEL = { + critical: { dot: '#7f1d1d', label: 'بحرانی', bg: 'rgba(127,29,29,0.08)' }, + high: { dot: '#9b1c1c', label: 'بالا', bg: 'rgba(155,28,28,0.07)' }, + medium: { dot: '#92400e', label: 'متوسط', bg: 'rgba(146,64,14,0.07)' }, + low: { dot: '#166534', label: 'پایین', bg: 'rgba(22,101,52,0.07)' }, + opportunity: { dot: '#1e40af', label: 'فرصت', bg: 'rgba(30,64,175,0.07)' }, +} + +/* ─── Section ────────────────────────────────────────────── */ +export default function RiskSection() { + const containerRef = useRef(null) + const listRef = useRef(null) + const [ready, setReady] = useState(false) + + useEffect(() => { + if (!containerRef.current || !listRef.current) return + /* Clone all items for seamless loop */ + const original = Array.from(listRef.current.children) + original.forEach(item => { + listRef.current!.appendChild(item.cloneNode(true)) + }) + setReady(true) + }, []) + + return ( +
    +
    + + {/* ── Header bar ── */} +
    +
    + پایش ریسک · لحظه‌ای +
    +
    + {riskItems.length} سیگنال فعال +
    +
    + + همه ریسک‌ها ← + +
    + + {/* ── Legend ── */} +
    + {[ + { color: '#7f1d1d', label: 'بحرانی' }, + { color: '#9b1c1c', label: 'بالا' }, + { color: '#92400e', label: 'متوسط' }, + { color: '#166534', label: 'پایین' }, + { color: '#1e40af', label: 'فرصت' }, + ].map(l => ( +
    +
    + {l.label} +
    + ))} +
    +
    + + {/* ── Auto-scroll ticker ── */} +
    +
      (e.currentTarget.style.animationPlayState = 'paused')} + onMouseLeave={e => (e.currentTarget.style.animationPlayState = 'running')} + > + {riskItems.map((item, idx) => { + const cfg = LEVEL[item.level] + return ( +
    • + {/* Level badge */} +
      +
      + + {cfg.label} + +
      + + {/* Quote */} +

      + {item.quote} +

      + + {/* Footer */} +
      +
      +
      {item.name}
      +
      {item.date}
      +
      +
      + {cfg.label} +
      +
      +
    • + ) + })} +
    +
    +
    + ) +} diff --git a/src/pages/Home/sections/SnapshotBar.tsx b/src/pages/Home/sections/SnapshotBar.tsx new file mode 100644 index 0000000..5f4f967 --- /dev/null +++ b/src/pages/Home/sections/SnapshotBar.tsx @@ -0,0 +1,116 @@ +import { marketPrices } from '@/data/market' + +const prices = marketPrices.slice(0, 4) + +export default function SnapshotBar() { + return ( +
    +
    + {prices.map((price, idx) => { + const isLast = idx === prices.length - 1 + const isUp = price.trend === 'up' + const isDown = price.trend === 'down' + const arrow = isUp ? '↑' : isDown ? '↓' : '—' + const changeColor = isUp + ? 'var(--green-ink)' + : isDown + ? 'var(--red)' + : 'var(--ink-4)' + + return ( +
    + {/* Label */} +
    + {price.name} +
    + + {/* Value */} +
    + {price.value.toLocaleString('fa-IR')} +
    + + {/* Change row */} +
    + + {arrow}{' '} + {Math.abs(price.changePercent).toLocaleString('fa-IR', { + minimumFractionDigits: 1, + maximumFractionDigits: 1, + })} + ٪ + + + {price.unit} + +
    +
    + ) + })} +
    +
    + ) +} diff --git a/src/pages/Home/sections/SpecialSpreadSection.tsx b/src/pages/Home/sections/SpecialSpreadSection.tsx new file mode 100644 index 0000000..3533fb6 --- /dev/null +++ b/src/pages/Home/sections/SpecialSpreadSection.tsx @@ -0,0 +1,165 @@ +import { useState } from 'react' +import { reports } from '@/data/reports' + +const featured = reports.find(r => r.featured) ?? reports[0] + +const items = [ + { num: '۱', title: 'تحلیل ۴ سناریوی اصلی', desc: 'از بدبینانه تا خوش‌بینانه با احتمال‌سنجی کمی و مدل‌سازی قیمتی' }, + { num: '۲', title: 'نقشه بازارهای هدف جدید', desc: 'جایگزین‌های اروپا: آفریقای شرقی، آسیای مرکزی، عراق و افغانستان' }, + { num: '۳', title: '۱۲ توصیه سیاستی مستقیم', desc: 'برای وزارت صمت، گمرک، سازمان بنادر و بانک مرکزی' }, + { num: '۴', title: 'مقایسه تطبیقی با رقبا', desc: 'ایران در برابر ترکیه، هند، روسیه و برزیل در هر سناریو' }, + { num: '۵', title: 'مدل Excel پیوست', desc: 'داده‌های خام و مدل قابل شخصی‌سازی برای تیم‌های تحلیلی' }, +] + +function BuyButton({ label, dark }: { label: string; dark?: boolean }) { + const [hov, setHov] = useState(false) + return ( + + ) +} + +export default function SpecialSpreadSection() { + return ( +
    + {/* LEFT */} +
    +
    + {/* Overline */} +
    +
    + + گزارش استراتژیک · زمستان ۱۴۰۳ + +
    + + {/* Headline */} +

    + ایران در نقشه
    + جدید تجارت
    + فولاد جهان:
    + چهار سناریو
    + برای ۱۴۰۴ +

    + + {/* Description */} +

    + {featured.summary} +

    +
    + +
    + {/* Author */} +
    +
    + {featured.authorInitial} +
    +
    +
    {featured.author}
    +
    {featured.authorRole}
    +
    +
    + + {/* Price + CTA */} +
    +
    +
    قیمت گزارش
    +
    + {featured.price.toLocaleString('fa-IR')} تومان +
    +
    +
    + + +
    +
    +
    +
    + + {/* RIGHT */} +
    +
    + آنچه در این گزارش می‌یابید +
    + + {items.map((item, i) => ( +
    +
    + {item.num} +
    +
    +
    {item.title}
    +
    {item.desc}
    +
    +
    + ))} +
    +
    + ) +} diff --git a/src/pages/Home/sections/TeamSection.tsx b/src/pages/Home/sections/TeamSection.tsx new file mode 100644 index 0000000..91077a5 --- /dev/null +++ b/src/pages/Home/sections/TeamSection.tsx @@ -0,0 +1,51 @@ +import { Link } from 'react-router-dom' +import { team } from '@/data/team' + +function MemberCard({ member, idx }: { member: (typeof team)[0]; idx: number }) { + const borderRight = idx % 2 === 0 ? '1px solid var(--rule-thin)' : 'none' + const borderTop = idx >= 2 ? '1px solid var(--rule-thin)' : 'none' + + return ( +
    (e.currentTarget.style.background = 'var(--paper-2)')} + onMouseLeave={e => (e.currentTarget.style.background = 'transparent')} + className="max-md:border-r-0 max-md:border-t max-md:border-[var(--rule-thin)] max-md:first:border-t-0 max-md:p-6" + > +
    + {member.initial} +
    +
    {member.name}
    +
    {member.role}
    +

    {member.bio}

    +
    + {member.expertise.slice(0, 4).map(tag => ( + {tag} + ))} +
    +
    + {member.reportCount} + گزارش منتشرشده +
    +
    + ) +} + +export default function TeamSection() { + return ( +
    +
    +
    +
    تیم تحریریه
    +
    + همه کارشناسان ← +
    +
    + {team.slice(0, 4).map((member, idx) => ( + + ))} +
    +
    +
    + ) +} diff --git a/src/pages/Market/Market.tsx b/src/pages/Market/Market.tsx new file mode 100644 index 0000000..8c159f0 --- /dev/null +++ b/src/pages/Market/Market.tsx @@ -0,0 +1,757 @@ +import { useState, useMemo } from 'react' +import type React from 'react' +import ReactApexChart from 'react-apexcharts' +import type { ApexOptions } from 'apexcharts' +import { TrendingUp, TrendingDown, Minus } from 'lucide-react' +import { marketPrices, priceHistory, globalComparison, monthlyExport } from '@/data/market' +import type { MarketPrice, PriceTrend } from '@/data/market' + +/* ─── chart shared theme ─────────────────────────────────── */ +const CHART_FONT = 'Vazir, sans-serif' +const GRID_COLOR = 'rgba(26,23,18,0.08)' +const LABEL_COLOR = '#766e68' + +/* ─── time range filter ──────────────────────────────────── */ +type Range = '1M' | '3M' | '6M' | '1Y' +const RANGES: { label: string; value: Range; months: number }[] = [ + { label: '۱ ماه', value: '1M', months: 1 }, + { label: '۳ ماه', value: '3M', months: 3 }, + { label: '۶ ماه', value: '6M', months: 6 }, + { label: '۱ سال', value: '1Y', months: 12 }, +] + +/* ─── trend icon ─────────────────────────────────────────── */ +function TrendIcon({ trend }: { trend: PriceTrend }) { + if (trend === 'up') return + if (trend === 'down') return + return +} + +/* ─── price card ─────────────────────────────────────────── */ +function PriceCard({ item }: { item: MarketPrice }) { + const isUp = item.trend === 'up' + const isDown = item.trend === 'down' + const trendColor = isUp ? 'var(--green-ink)' : isDown ? 'var(--red)' : 'var(--ink-4)' + const trendBg = isUp + ? 'rgba(22,101,52,0.08)' + : isDown + ? 'rgba(155,28,28,0.08)' + : 'rgba(26,23,18,0.06)' + + return ( +
    +

    {item.name}

    +

    + {item.value.toLocaleString('fa-IR')} +

    +

    {item.unit}

    + + {/* Change badge */} +
    + + + {item.changePercent > 0 ? '+' : ''} + {item.changePercent.toFixed(1)}٪ + + + ({item.change > 0 ? '+' : ''} + {item.change.toLocaleString('fa-IR')}) + +
    +
    + ) +} + +/* ─── comparison tool ────────────────────────────────────── */ +function ComparisonTool() { + const [productA, setProductA] = useState(marketPrices[0].id) + const [productB, setProductB] = useState(marketPrices[1].id) + + const itemA = marketPrices.find((m) => m.id === productA)! + const itemB = marketPrices.find((m) => m.id === productB)! + + const selectStyle: React.CSSProperties = { + border: '1px solid var(--ink)', + padding: '8px 14px', + fontSize: 12, + fontFamily: 'inherit', + background: 'var(--paper)', + color: 'var(--ink)', + borderRadius: 0, + cursor: 'pointer', + outline: 'none', + direction: 'rtl', + flex: 1, + } + + return ( +
    +
    + + + در برابر + + +
    + + {/* Side-by-side comparison */} +
    +
    +

    {itemA.name}

    +

    + {itemA.value.toLocaleString('fa-IR')} +

    +

    {itemA.unit}

    +
    + + {itemA.changePercent > 0 ? '+' : ''} + {itemA.changePercent.toFixed(1)}٪ +
    +
    + + {/* VS divider */} +
    + + VS + +
    + +
    +

    {itemB.name}

    +

    + {itemB.value.toLocaleString('fa-IR')} +

    +

    {itemB.unit}

    +
    + + {itemB.changePercent > 0 ? '+' : ''} + {itemB.changePercent.toFixed(1)}٪ +
    +
    +
    + +

    + نمودار مقایسه‌ای در نسخه‌های آینده تکمیل می‌شود +

    +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Market() { + const [priceRange, setPriceRange] = useState('1Y') + + /* Filter price history based on selected range */ + const filteredHistory = useMemo(() => { + const range = RANGES.find((r) => r.value === priceRange)! + return priceHistory.slice(-range.months) + }, [priceRange]) + + /* Line chart options */ + const lineOptions: ApexOptions = { + chart: { + type: 'area', + toolbar: { show: false }, + fontFamily: CHART_FONT, + background: 'transparent', + animations: { enabled: true, speed: 400 }, + }, + colors: ['#9b1c1c'], + stroke: { width: 2, curve: 'smooth' }, + fill: { + type: 'gradient', + gradient: { + shadeIntensity: 1, + opacityFrom: 0.15, + opacityTo: 0, + stops: [0, 100], + }, + }, + grid: { + borderColor: GRID_COLOR, + strokeDashArray: 3, + }, + xaxis: { + categories: filteredHistory.map((p) => p.month), + labels: { + style: { fontFamily: CHART_FONT, colors: LABEL_COLOR, fontSize: '11px' }, + rotate: -35, + }, + axisBorder: { color: GRID_COLOR }, + axisTicks: { color: GRID_COLOR }, + }, + yaxis: { + opposite: true, + labels: { + style: { fontFamily: CHART_FONT, colors: LABEL_COLOR, fontSize: '11px' }, + formatter: (val: number) => val.toLocaleString('fa-IR'), + }, + }, + tooltip: { + theme: 'light', + style: { fontFamily: CHART_FONT, fontSize: '12px' }, + y: { formatter: (val: number) => val.toLocaleString('fa-IR') + ' تومان/کیلو' }, + }, + dataLabels: { enabled: false }, + } + + const lineSeries = [ + { + name: 'شمش فولاد', + data: filteredHistory.map((p) => p.price), + }, + ] + + /* Bar chart options */ + const barOptions: ApexOptions = { + chart: { + type: 'bar', + toolbar: { show: false }, + fontFamily: CHART_FONT, + background: 'transparent', + animations: { enabled: true, speed: 400 }, + }, + colors: ['#2e2a25'], + plotOptions: { + bar: { + borderRadius: 0, + columnWidth: '65%', + }, + }, + grid: { + borderColor: GRID_COLOR, + strokeDashArray: 3, + }, + xaxis: { + categories: monthlyExport.map((e) => e.month), + labels: { + style: { fontFamily: CHART_FONT, colors: LABEL_COLOR, fontSize: '11px' }, + rotate: -35, + }, + axisBorder: { color: GRID_COLOR }, + axisTicks: { color: GRID_COLOR }, + }, + yaxis: { + opposite: true, + labels: { + style: { fontFamily: CHART_FONT, colors: LABEL_COLOR, fontSize: '11px' }, + formatter: (val: number) => (val / 1000).toFixed(0) + 'k', + }, + }, + tooltip: { + theme: 'light', + style: { fontFamily: CHART_FONT, fontSize: '12px' }, + y: { + formatter: (val: number) => + (val / 1000).toLocaleString('fa-IR') + ' هزار تن', + }, + }, + dataLabels: { enabled: false }, + } + + const barSeries = [ + { + name: 'صادرات', + data: monthlyExport.map((e) => e.tonnage), + }, + ] + + /* Section heading style */ + const SectionHeading = ({ + title, + subtitle, + children, + }: { + title: string + subtitle?: string + children?: React.ReactNode + }) => ( +
    +
    +

    + {title} +

    + {subtitle && ( +

    {subtitle}

    + )} +
    + {children} +
    + ) + + return ( +
    + {/* ─── Page Header ───────────────────────────────── */} +
    +
    +
    +

    + داشبورد بازار +

    +

    + قیمت‌های لحظه‌ای، روند تاریخی و تحلیل صادرات فولاد ایران +

    +
    +
    + + آخرین بروزرسانی: ۴ خرداد ۱۴۰۴ +
    +
    +
    + + {/* ─── Section 1: Price Grid ──────────────────────── */} +
    +
    + +
    + {marketPrices.map((item) => ( + + ))} +
    +
    +
    + + {/* ─── Section 2: Price History Chart ────────────── */} +
    +
    + + {/* Range selector */} +
    + {RANGES.map((r, rIdx) => ( + + ))} +
    +
    + +
    + +
    +
    +
    + + {/* ─── Section 3: Global Comparison Table ─────────── */} +
    +
    + + + + + {['کشور', 'قیمت شمش (دلار/تن)', 'ارز', 'مقایسه با ایران'].map((h) => ( + + ))} + + + + {globalComparison.map((item, i) => { + const isIran = item.country === 'ایران' + return ( + + + + + + + ) + })} + +
    + {h} +
    +
    + {item.flag} + {item.country} + {isIran && ( + + ایران + + )} +
    +
    + {item.currency === 'USD' + ? `$${item.price.toLocaleString()}` + : item.price.toLocaleString('fa-IR') + ' تومان/کیلو'} + + {item.currency} + + {isIran ? ( + + مبنا + + ) : ( + 500 ? 'var(--green-ink)' : 'var(--red)', + }} + > + {item.price > 500 ? 'بالاتر از میانگین' : 'پایین‌تر از میانگین'} + + )} +
    +
    +
    + + {/* ─── Section 4: Export Bar Chart ─────────────────── */} +
    +
    + +
    + +
    +
    +
    + + {/* ─── Section 5: Comparison Tool ──────────────────── */} +
    +
    + + +
    +
    + + {/* ─── Responsive styles ─────────────────────────── */} + +
    + ) +} diff --git a/src/pages/ReportDetail/ReportDetail.tsx b/src/pages/ReportDetail/ReportDetail.tsx new file mode 100644 index 0000000..638152f --- /dev/null +++ b/src/pages/ReportDetail/ReportDetail.tsx @@ -0,0 +1,721 @@ +import { useState } from 'react' +import { Link, useParams, Navigate } from 'react-router-dom' +import { Lock, Download, FileText, ChevronRight, Tag, User } from 'lucide-react' +import { reports } from '@/data/reports' +import type { Report } from '@/data/reports' + +/* ─── helpers ────────────────────────────────────────────── */ +function formatPrice(price: number): string { + return price.toLocaleString('fa-IR') + ' تومان' +} + +const TYPE_LABELS: Record = { + quarterly: 'گزارش فصلی', + flash: 'Flash Report', + risk: 'گزارش ریسک', + free: 'گزارش رایگان', + special: 'گزارش ویژه', +} + +/* ─── Persian lorem paragraphs ──────────────────────────── */ +const LOREM_PARAGRAPHS = [ + 'صنعت فولاد ایران در دوره‌ای از تحولات عمیق ساختاری قرار دارد. ظرفیت تولیدی کشور در سال‌های اخیر به رکوردهای تاریخی دست یافته، اما همزمان چالش‌های پیچیده‌ای در حوزه صادرات، تأمین مالی و دسترسی به بازارهای جهانی در برابر صنعت قرار گرفته است. این گزارش با بهره‌گیری از داده‌های اولیه و تحلیل‌های کمی، وضعیت کنونی را از زوایای مختلف بررسی می‌کند.', + 'در این مطالعه، رویکرد ترکیبی از تحلیل سری‌های زمانی قیمتی، مدل‌سازی اقتصادسنجی و تحلیل سناریو به‌کار رفته است. داده‌های مورد استفاده از منابع معتبر بین‌المللی از جمله World Steel Association، Platts و گزارش‌های رسمی وزارت صنعت، معدن و تجارت جمع‌آوری شده‌اند. دوره تحلیل پنج سال اخیر را در بر می‌گیرد.', + 'یافته‌های کلیدی این گزارش نشان می‌دهد که تقاضای داخلی فولاد در بخش ساختمان و زیرساخت در میان‌مدت روند کاهشی دارد، در حالی که تقاضای صنعتی و ماشین‌سازی رشد ملایمی را تجربه خواهد کرد. این تحول در ترکیب تقاضا مستلزم بازنگری در استراتژی محصول کارخانه‌های فولادی کشور است.', + 'توصیه‌های سیاست‌گذاری این گزارش در سه محور اصلی ارائه می‌شود: اول، اصلاح ساختار قیمت‌گذاری انرژی و مواد اولیه برای بهبود رقابت‌پذیری صادراتی؛ دوم، توسعه مسیرهای دسترسی به بازارهای هدف جایگزین در آفریقا و آسیای جنوب‌شرقی؛ سوم، تقویت ظرفیت مذاکراتی بخش خصوصی در قراردادهای بین‌المللی.', +] + +const TOC_ITEMS = [ + 'خلاصه مدیریتی', + 'بررسی وضعیت کنونی بازار', + 'تحلیل روندهای عرضه و تقاضا', + 'مدل‌سازی سناریوهای آینده', + 'تأثیرات ژئوپلیتیک و محیط کسب‌وکار', + 'تحلیل رقابت‌پذیری و موقعیت ایران', + 'توصیه‌های سیاست‌گذاری و اجرایی', + 'پیوست‌ها: داده‌ها و روش‌شناسی', +] + +/* ─── related reports sidebar ────────────────────────────── */ +function RelatedReports({ current }: { current: Report }) { + const related = reports + .filter((r) => r.id !== current.id && r.category === current.category) + .slice(0, 3) + + if (related.length === 0) return null + + return ( +
    +

    + گزارش‌های مرتبط +

    + {related.map((r, i) => ( +
    + + {r.title} + + + {r.isFree ? 'رایگان' : formatPrice(r.price)} + +
    + ))} +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function ReportDetail() { + const { id } = useParams<{ id: string }>() + const [buyHovered, setBuyHovered] = useState(false) + const [dlHovered, setDlHovered] = useState(false) + + const report = reports.find((r) => r.id === id) + + if (!report) return + + return ( +
    + {/* ─── Breadcrumb ────────────────────────────────── */} + + + {/* ─── Two-column layout ─────────────────────────── */} +
    + {/* ═══ LEFT: Main content ══════════════════════════ */} +
    + {/* Kicker */} +
    + + {TYPE_LABELS[report.type] || report.type} · {report.category} + +
    +
    + + {/* Headline */} +

    + {report.title} +

    + + {/* Meta row */} +
    + {/* Author initial */} +
    + {report.authorInitial} +
    +
    +

    + {report.author} +

    +

    + {report.authorRole} +

    +
    +
    + {report.publishDate} +
    + + + {report.pages} صفحه + +
    + + {/* Thick rule */} +
    + + {/* Abstract */} +
    +

    + چکیده +

    + {LOREM_PARAGRAPHS.map((para, i) => ( +

    + {i === 0 ? report.summary : para} +

    + ))} +
    + + {/* Table of Contents */} +
    +
    +

    + فهرست مطالب +

    +
    +
      + {TOC_ITEMS.map((item, i) => ( +
    1. + + {(i + 1).toLocaleString('fa-IR')} + + {item} + + + {((i + 1) * 8 + 3).toLocaleString('fa-IR')} + +
    2. + ))} +
    +
    + + {/* Free preview + paywall */} + {!report.isFree && ( +
    +
    +

    + پیش‌نمایش رایگان +

    + + ۳ صفحه اول + +
    +
    + {/* Fake preview content */} +
    + {[90, 100, 75, 100, 60, 100, 85, 40].map((width, i) => ( +
    + ))} +
    + {/* Gradient overlay */} +
    + +

    + برای دسترسی کامل اشتراک بگیرید +

    + + مشاهده طرح‌های اشتراک + +
    +
    +
    + )} + + {/* Tags */} +
    + + {report.tags.map((tag) => ( + + {tag} + + ))} +
    +
    + + {/* ═══ RIGHT: Sidebar ══════════════════════════════ */} +
    + {/* Price card */} +
    +

    + قیمت گزارش +

    + + {/* Price */} +

    + {report.isFree ? 'رایگان' : formatPrice(report.price)} +

    + + {/* Meta */} +
    +
    + تعداد صفحات + + {report.pages} صفحه + +
    +
    + تاریخ انتشار + + {report.publishDate} + +
    +
    + فرمت + PDF +
    +
    + + {/* Buy button */} + + + {/* Download summary button */} + + + {/* Note */} +

    + فاکتور رسمی صادر می‌شود +

    +
    + + {/* Author card */} +
    +
    +
    + {report.authorInitial} +
    +
    +

    + {report.author} +

    +

    + {report.authorRole} +

    +
    +
    +

    + متخصص در تحلیل بازارهای فولاد با بیش از یک دهه تجربه در پژوهش و سیاست‌گذاری صنعتی. +

    + + + مشاهده پروفایل کامل + +
    + + {/* Related reports */} +
    + +
    +
    +
    + + {/* ─── Responsive styles ─────────────────────────── */} + +
    + ) +} diff --git a/src/pages/Reports/Reports.tsx b/src/pages/Reports/Reports.tsx new file mode 100644 index 0000000..66631cd --- /dev/null +++ b/src/pages/Reports/Reports.tsx @@ -0,0 +1,581 @@ +import { useState, useMemo } from 'react' +import { Link } from 'react-router-dom' +import { Search, ChevronRight, ChevronLeft } from 'lucide-react' +import { reports } from '@/data/reports' +import type { ReportCategory, ReportType } from '@/data/reports' + +/* ─── constants ──────────────────────────────────────────── */ +const CATEGORIES: { label: string; value: ReportCategory | 'همه' }[] = [ + { label: 'همه', value: 'همه' }, + { label: 'بازار جهانی', value: 'بازار جهانی' }, + { label: 'سیاست‌گذاری', value: 'سیاست‌گذاری' }, + { label: 'انرژی و ESG', value: 'انرژی و ESG' }, + { label: 'صادرات', value: 'صادرات' }, + { label: 'تولید داخلی', value: 'تولید داخلی' }, + { label: 'ریسک و بحران', value: 'ریسک و بحران' }, +] + +const TYPES: { label: string; value: ReportType | 'همه' | 'رایگان' }[] = [ + { label: 'همه', value: 'همه' }, + { label: 'رایگان', value: 'رایگان' }, + { label: 'Flash Report', value: 'flash' }, + { label: 'فصلی', value: 'quarterly' }, + { label: 'گزارش ویژه', value: 'special' }, + { label: 'ریسک', value: 'risk' }, +] + +const SORT_OPTIONS = [ + { label: 'جدیدترین', value: 'newest' }, + { label: 'قدیمی‌ترین', value: 'oldest' }, + { label: 'گران‌ترین', value: 'price-desc' }, + { label: 'ارزان‌ترین', value: 'price-asc' }, +] + +const TYPE_COLORS: Record = { + quarterly: '#166534', + flash: '#9b1c1c', + risk: '#92400e', + free: '#1d4ed8', + special: '#4a1d96', +} + +const TYPE_LABELS: Record = { + quarterly: 'فصلی', + flash: 'فلش', + risk: 'ریسک', + free: 'رایگان', + special: 'ویژه', +} + +const PAGE_SIZE = 9 + +/* ─── price formatter ────────────────────────────────────── */ +function formatPrice(price: number): string { + if (price === 0) return 'رایگان' + return price.toLocaleString('fa-IR') + ' تومان' +} + +/* ─── report card ────────────────────────────────────────── */ +function ReportCard({ report }: { report: (typeof reports)[0] }) { + const [hovered, setHovered] = useState(false) + + return ( +
    + {/* top meta */} +
    + + + {report.category} + + {report.isFree && ( + + رایگان + + )} + + {TYPE_LABELS[report.type]} + +
    + + {/* title */} + setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {report.title} + + + {/* meta row */} +
    + {report.author} + · + {report.publishDate} + · + {report.pages} صفحه +
    + + {/* divider */} +
    + + {/* price row */} +
    + + {formatPrice(report.price)} + + + {report.isFree ? 'دانلود' : 'خرید'} + +
    +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Reports() { + const [activeCategory, setActiveCategory] = useState('همه') + const [activeType, setActiveType] = useState('همه') + const [sortBy, setSortBy] = useState('newest') + const [searchQuery, setSearchQuery] = useState('') + const [page, setPage] = useState(1) + + const filtered = useMemo(() => { + let result = [...reports] + + if (activeCategory !== 'همه') { + result = result.filter((r) => r.category === activeCategory) + } + + if (activeType !== 'همه') { + if (activeType === 'رایگان') { + result = result.filter((r) => r.isFree) + } else { + result = result.filter((r) => r.type === activeType) + } + } + + if (searchQuery.trim()) { + const q = searchQuery.trim().toLowerCase() + result = result.filter( + (r) => + r.title.toLowerCase().includes(q) || + r.author.toLowerCase().includes(q) || + r.category.toLowerCase().includes(q) || + r.tags.some((t) => t.toLowerCase().includes(q)) + ) + } + + switch (sortBy) { + case 'oldest': + result = result.reverse() + break + case 'price-desc': + result = result.sort((a, b) => b.price - a.price) + break + case 'price-asc': + result = result.sort((a, b) => a.price - b.price) + break + } + + return result + }, [activeCategory, activeType, sortBy, searchQuery]) + + const totalPages = Math.ceil(filtered.length / PAGE_SIZE) + const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE) + + // Reset to page 1 on filter change + const handleCategoryChange = (cat: ReportCategory | 'همه') => { + setActiveCategory(cat) + setPage(1) + } + const handleTypeChange = (type: ReportType | 'همه' | 'رایگان') => { + setActiveType(type) + setPage(1) + } + + return ( +
    + {/* Page header */} +
    +

    + گزارش‌ها +

    +

    + تحلیل‌های تخصصی بازار فولاد ایران و جهان — گزارش‌های فصلی، ریسک، و فلش +

    +
    + + {/* ─── Filter bar ──────────────────────────────────── */} +
    + {/* Category pills */} +
    + {CATEGORIES.map((cat) => ( + + ))} +
    + + {/* Second row: type + sort + search */} +
    + {/* Type filter */} +
    + {TYPES.map((t) => ( + + ))} +
    + +
    + + {/* Sort */} +
    + + مرتب‌سازی + + +
    + + {/* Search */} +
    + { + setSearchQuery(e.target.value) + setPage(1) + }} + style={{ + border: '1px solid var(--ink)', + padding: '8px 36px 8px 12px', + fontSize: 12, + fontFamily: 'inherit', + background: 'transparent', + color: 'var(--ink)', + borderRadius: 0, + outline: 'none', + width: 200, + direction: 'rtl', + }} + /> + +
    +
    +
    + + {/* ─── Report grid ─────────────────────────────────── */} + {paged.length > 0 ? ( +
    + {paged.map((report) => ( + + ))} + {/* Fill empty cells in last row to keep grid consistent */} + {paged.length % 3 !== 0 && + Array.from({ length: 3 - (paged.length % 3) }).map((_, i) => ( +
    + ))} +
    + ) : ( +
    +

    نتیجه‌ای یافت نشد

    +

    + جستجو یا فیلترهای خود را تغییر دهید +

    +
    + )} + + {/* ─── Pagination ──────────────────────────────────── */} + {totalPages > 1 && ( +
    + + + + صفحه {page.toLocaleString('fa-IR')} از {totalPages.toLocaleString('fa-IR')} + + + +
    + )} + + {/* ─── Mobile responsive styles ────────────────────── */} + +
    + ) +} diff --git a/src/pages/Risks/Risks.tsx b/src/pages/Risks/Risks.tsx new file mode 100644 index 0000000..408f3be --- /dev/null +++ b/src/pages/Risks/Risks.tsx @@ -0,0 +1,340 @@ +import { Link } from 'react-router-dom'; +import { reports } from '@/data/reports'; + +const riskMatrix = [ + { id: 1, title: 'تشدید تحریم‌های صادراتی', category: 'ژئوپلیتیک', probability: 'بالا', impact: 'بسیار بالا', level: 'critical' }, + { id: 2, title: 'سقوط قیمت جهانی فولاد', category: 'بازار', probability: 'متوسط', impact: 'بالا', level: 'high' }, + { id: 3, title: 'کمبود انرژی صنعتی', category: 'زیرساخت', probability: 'بالا', impact: 'متوسط', level: 'high' }, + { id: 4, title: 'اجرای CBAM اروپا', category: 'مقررات', probability: 'قطعی', impact: 'متوسط', level: 'medium' }, + { id: 5, title: 'رقابت صادرات چین', category: 'بازار', probability: 'قطعی', impact: 'بالا', level: 'high' }, + { id: 6, title: 'نوسانات نرخ ارز', category: 'مالی', probability: 'بالا', impact: 'متوسط', level: 'medium' }, +] as const; + +type RiskLevel = 'critical' | 'high' | 'medium' | 'low'; + +const levelConfig: Record = { + critical: { color: '#7f1d1d', label: 'بحرانی' }, + high: { color: 'var(--red)', label: 'بالا' }, + medium: { color: 'var(--amber)', label: 'متوسط' }, + low: { color: 'var(--green-ink)', label: 'پایین' }, +}; + +function formatPrice(price: number): string { + if (price === 0) return 'رایگان'; + return price.toLocaleString('fa-IR') + ' تومان'; +} + +export default function Risks() { + const riskReports = reports.filter((r) => r.type === 'risk'); + + return ( +
    + {/* Page Header */} +
    +
    +
    + اندیشکده فولاد ایران — پایش ریسک +
    +
    +

    + پایش ریسک‌های صنعت فولاد +

    + + به‌روز شده: بهمن ۱۴۰۳ + +
    + + {/* Legend */} +
    + {(Object.keys(levelConfig) as RiskLevel[]).map((level) => ( +
    +
    + {levelConfig[level].label} +
    + ))} +
    +
    +
    + +
    + {/* Risk Matrix Table */} +
    +

    + + ماتریس ریسک‌های کلیدی +

    + +
    + {/* Table Header */} +
    + {['#', 'عنوان ریسک', 'دسته', 'احتمال', 'شدت تأثیر', 'سطح ریسک'].map((col, i) => ( +
    0 ? '1px solid rgba(255,255,255,0.12)' : 'none', + }} + > + {col} +
    + ))} +
    + + {/* Table Rows */} + {riskMatrix.map((risk, idx) => { + const cfg = levelConfig[risk.level as RiskLevel]; + return ( +
    + {/* # */} +
    + {risk.id} +
    + {/* Title */} +
    + {risk.title} +
    + {/* Category */} +
    + {risk.category} +
    + {/* Probability */} +
    + {risk.probability} +
    + {/* Impact */} +
    + {risk.impact} +
    + {/* Level */} +
    +
    + + {cfg.label} + +
    +
    + ); + })} +
    +
    + + {/* Risk Reports Section */} +
    +
    +

    + گزارش‌های ریسک اندیشکده +

    + + {riskReports.length} گزارش تحلیلی + +
    + +
    + {riskReports.map((report, idx) => ( + + {/* Type badge */} +
    + + ریسک + +
    + {/* Title */} +
    + {report.title} +
    + {/* Date */} +
    + {report.publishDate} +
    + {/* Pages */} +
    + {report.pages} صفحه +
    + {/* Price + link */} +
    + + {formatPrice(report.price)} + + + مشاهده ← + +
    + + ))} +
    +
    +
    +
    + ); +} diff --git a/src/pages/Scanner/Scanner.tsx b/src/pages/Scanner/Scanner.tsx new file mode 100644 index 0000000..2b1e461 --- /dev/null +++ b/src/pages/Scanner/Scanner.tsx @@ -0,0 +1,430 @@ +import { marketPrices, globalComparison } from '@/data/market'; +import type { MarketPrice } from '@/data/market'; + +const riskAlerts = [ + { + title: 'فشار صادرات چین', + desc: 'افزایش ۱۸٪ صادرات ارزان چین فشار قیمتی روی بازار آسیا', + level: 'high', + }, + { + title: 'محدودیت انرژی داخلی', + desc: 'کاهش تولید زمستانی به دلیل محدودیت گاز صنعتی', + level: 'medium', + }, + { + title: 'فرصت بازار عراق', + desc: 'کاهش واردات ترکیه، فرصت افزایش سهم بازار برای ایران', + level: 'low', + }, +] as const; + +type AlertLevel = 'high' | 'medium' | 'low'; + +const alertBorderColor: Record = { + high: 'var(--red)', + medium: 'var(--amber)', + low: 'var(--green-ink)', +}; + +const alertLabelColor: Record = { + high: 'var(--red)', + medium: 'var(--amber)', + low: 'var(--green-ink)', +}; + +const alertLabel: Record = { + high: 'بالا', + medium: 'متوسط', + low: 'فرصت', +}; + +function formatNumber(n: number): string { + return n.toLocaleString('fa-IR'); +} + +function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPrice; col: number; row: number; totalCols: number; totalRows: number }) { + const isUp = item.trend === 'up'; + const isDown = item.trend === 'down'; + const trendArrow = isUp ? '▲' : isDown ? '▼' : '—'; + const trendColor = isUp ? 'var(--green-ink)' : isDown ? 'var(--red)' : 'var(--ink-5)'; + const changePrefix = item.change > 0 ? '+' : ''; + + return ( +
    + {/* Product name */} +
    + {item.name} +
    + + {/* Price */} +
    + {formatNumber(item.value)} +
    + + {/* Unit */} +
    + {item.unit} +
    + + {/* Change + trend */} +
    + + {trendArrow} + + + {changePrefix}{formatNumber(item.change)} + + + {changePrefix}{item.changePercent.toFixed(1)}٪ + +
    +
    + ); +} + +export default function Scanner() { + // Use first 6 prices for the grid + const gridPrices = marketPrices.slice(0, 6); + const COLS = 3; + const ROWS = Math.ceil(gridPrices.length / COLS); + + // For CSS bar chart: find max price in globalComparison for proportional width + // Filter to USD-only for a fair comparison (exclude IRR) + const usdPrices = globalComparison.filter((g) => g.currency === 'USD'); + const maxUSD = Math.max(...usdPrices.map((g) => g.price)); + + return ( +
    + {/* Page Header */} +
    +
    +
    + اندیشکده فولاد ایران — داده‌های بازار +
    +
    +
    +

    + اسکنر بازار جهانی فولاد +

    +

    + داده‌های لحظه‌ای قیمت‌های بازار داخلی و جهانی +

    +
    + + به‌روز شده: بهمن ۱۴۰۳ + +
    +
    +
    + +
    + {/* Price Ticker Grid */} +
    +
    + +

    قیمت‌های لحظه‌ای بازار

    +
    + +
    + {gridPrices.map((item, idx) => { + const col = idx % COLS; + const row = Math.floor(idx / COLS); + return ( + + ); + })} +
    +
    + + {/* Global Comparison Bar Chart */} +
    +
    +

    مقایسه جهانی — قیمت میلگرد

    + دلار / تن — بازار صادراتی +
    + +
    + {usdPrices.map((entry) => { + const barPct = Math.round((entry.price / maxUSD) * 100); + return ( +
    + {/* Flag + Country */} +
    + {entry.flag} + + {entry.country} + +
    + + {/* Bar */} +
    +
    +
    + + {/* Value */} +
    + + ${entry.price} + + /تن +
    +
    + ); + })} +
    +
    + + {/* Active Risk Cards */} +
    +
    +

    ریسک‌های بازار فعال

    + هشدارهای جاری +
    + +
    + {riskAlerts.map((alert) => { + const level = alert.level as AlertLevel; + return ( +
    +
    +

    + {alert.title} +

    + + {alertLabel[level]} + +
    +

    + {alert.desc} +

    +
    + ); + })} +
    +
    +
    +
    + ); +} diff --git a/src/pages/Special/Special.tsx b/src/pages/Special/Special.tsx new file mode 100644 index 0000000..6988684 --- /dev/null +++ b/src/pages/Special/Special.tsx @@ -0,0 +1,383 @@ +import { Link } from 'react-router-dom'; +import { reports } from '@/data/reports'; +import type { Report } from '@/data/reports'; + +const typeLabel: Record = { + special: 'ویژه', + quarterly: 'فصلی', + flash: 'فوری', + risk: 'ریسک', + free: 'رایگان', +}; + +function formatPrice(price: number): string { + if (price === 0) return 'رایگان'; + return price.toLocaleString('fa-IR') + ' تومان'; +} + +function SpecialCard({ report }: { report: Report }) { + return ( + +
    + + {typeLabel[report.type] ?? report.type} + + + {report.category} + +
    + +

    + {report.title} +

    + +

    + {report.summary} +

    + +
    +
    +
    + {report.authorInitial} +
    +
    +
    {report.author}
    +
    {report.authorRole}
    +
    +
    + +
    + {report.pages} صفحه + {report.publishDate} +
    + + {formatPrice(report.price)} + + + {report.isFree ? 'دریافت رایگان' : 'خرید گزارش'} + +
    +
    +
    + + ); +} + +function CompactCard({ report }: { report: Report }) { + return ( + +
    + + {typeLabel[report.type] ?? report.type} + + {report.category} +
    + +

    + {report.title} +

    + +

    + {report.summary} +

    + +
    + {report.publishDate} + + {formatPrice(report.price)} + +
    + + ); +} + +export default function Special() { + const specialReports = reports.filter((r) => r.type === 'special'); + const otherReports = reports.filter((r) => r.type !== 'special'); + + return ( +
    + {/* Hero Banner */} +
    +
    +
    + اندیشکده فولاد ایران — پژوهش‌های تخصصی +
    +

    + گزارش‌های ویژه +

    +

    + تحلیل‌های عمیق و سناریوپردازی‌های راهبردی برای تصمیم‌گیرندگان صنعت فولاد +

    +
    + {specialReports.length} گزارش ویژه + + {reports.length} گزارش در مجموع + +
    +
    +
    + +
    + {/* Section A — Special Reports */} +
    +
    +
    +

    + گزارش‌های ویژه +

    + + تحلیل‌های سناریومحور و راهبردی + +
    + +
    + {specialReports.map((report) => ( + + ))} +
    +
    + + {/* Thick Rule Separator */} +
    + + {/* Section B — Other Reports */} +
    +
    +
    +

    + سایر گزارش‌های تحلیلی +

    + + گزارش‌های فصلی، فوری و تحلیل ریسک + +
    + +
    + {otherReports.map((report) => ( + + ))} +
    +
    +
    +
    + ); +} diff --git a/src/pages/Subscribe/Subscribe.tsx b/src/pages/Subscribe/Subscribe.tsx new file mode 100644 index 0000000..597f4c4 --- /dev/null +++ b/src/pages/Subscribe/Subscribe.tsx @@ -0,0 +1,680 @@ +import { useState } from 'react' +import { Link } from 'react-router-dom' +import { Check, X, ChevronDown, ChevronUp, Phone } from 'lucide-react' +import { plans } from '@/data/plans' +import type { Plan } from '@/data/plans' + +/* ─── feature comparison data ────────────────────────────── */ +const FEATURES: { label: string; basic: string; pro: string; gov: string }[] = [ + { label: 'دسترسی به گزارش‌های جدید', basic: 'check', pro: 'check', gov: 'check' }, + { label: 'Flash Reports فوری', basic: 'x', pro: 'check', gov: 'check' }, + { label: 'آرشیو کامل', basic: 'محدود (۱ سال)', pro: 'نامحدود', gov: 'نامحدود' }, + { label: 'داشبورد بازار', basic: 'check', pro: 'check', gov: 'check' }, + { label: 'جلسه با تیم تحریریه', basic: 'x', pro: 'ماهانه', gov: 'فصلی' }, + { label: 'گزارش‌های سفارشی', basic: 'x', pro: 'x', gov: 'check' }, + { label: 'ارائه حضوری', basic: 'x', pro: 'x', gov: 'check' }, + { label: 'تعداد کاربران', basic: '۱', pro: 'تا ۵', gov: 'نامحدود' }, + { label: 'فاکتور رسمی', basic: 'x', pro: 'check', gov: 'check' }, + { label: 'پشتیبانی اختصاصی', basic: 'x', pro: 'check', gov: 'check' }, +] + +/* ─── FAQ data ───────────────────────────────────────────── */ +const FAQS = [ + { + q: 'آیا می‌توانم گزارش را پیش از خرید ببینم؟', + a: 'بله، برای تمام گزارش‌ها یک پیش‌نمایش رایگان شامل چکیده، فهرست مطالب و چند صفحه اول در دسترس است. همچنین برخی گزارش‌ها به‌طور کامل رایگان منتشر می‌شوند.', + }, + { + q: 'فاکتور رسمی چگونه صادر می‌شود؟', + a: 'پس از تکمیل خرید، فاکتور رسمی با مشخصات سازمانی شما حداکثر ظرف ۲۴ ساعت کاری از طریق ایمیل ارسال می‌شود. برای اشتراک‌های سازمانی، قرارداد رسمی نیز منعقد می‌شود.', + }, + { + q: 'نحوه پرداخت چگونه است؟', + a: 'پرداخت از طریق درگاه بانکی اینترنتی (شبکه شتاب) و انتقال بانکی مستقیم امکان‌پذیر است. برای اشتراک‌های سازمانی پرداخت به‌صورت حواله بانکی با صدور فاکتور رسمی انجام می‌شود.', + }, + { + q: 'آیا اشتراک سازمانی قابل لغو است؟', + a: 'بله، اشتراک سازمانی با اطلاع ۳۰ روز پیشین قابل فسخ است. مبالغ پرداختی به‌صورت پرو-راتا بر اساس ماه‌های باقیمانده محاسبه و بازگشت داده می‌شود.', + }, + { + q: 'گزارش‌های سفارشی چه مدت طول می‌کشد؟', + a: 'بسته به پیچیدگی موضوع، گزارش‌های سفارشی معمولاً ۴ تا ۸ هفته زمان نیاز دارند. پس از جلسه اولیه تعریف دامنه کار، برنامه زمانی دقیق ارائه می‌شود.', + }, +] + +/* ─── plan extra features ────────────────────────────────── */ +const PLAN_EXTRA_FEATURES: Record = { + basic: [ + 'دسترسی به گزارش‌های فصلی بازار', + 'خلاصه گزارش‌های تخصصی', + 'خبرنامه هفتگی', + 'پایگاه داده قیمت‌های بازار', + 'آرشیو ۱ ساله گزارش‌ها', + 'داشبورد بازار پایه', + 'دانلود PDF گزارش‌ها', + 'پشتیبانی ایمیلی', + ], + professional: [ + 'تمام گزارش‌های تخصصی و ریسک', + 'گزارش‌های فوری (Flash)', + 'مدل‌های کمی و داده‌های خام', + 'آرشیو کامل ۵ ساله', + 'API دسترسی به داده قیمت', + 'پشتیبانی اولویت‌دار', + 'تا ۵ کاربر سازمانی', + 'فاکتور رسمی خودکار', + 'داشبورد پیشرفته بازار', + 'خبرنامه اختصاصی پیش از انتشار', + ], + government: [ + 'دسترسی نامحدود تیمی', + 'گزارش‌های سفارشی اختصاصی', + 'جلسات مشاوره ماهانه', + 'ارائه حضوری نتایج تحقیقات', + 'آموزش و کارگاه تخصصی', + 'پشتیبانی ۲۴ ساعته اختصاصی', + 'دسترسی پیش از انتشار', + 'مدیر حساب اختصاصی', + 'قرارداد و فاکتور سازمانی', + 'گزارش وضعیت اعضاء', + 'لایسنس چندکاربره نامحدود', + 'پنل مدیریت تیم', + ], +} + +/* ─── cell renderer for feature table ───────────────────── */ +function FeatureCell({ value }: { value: string }) { + if (value === 'check') { + return ( + + + + ) + } + if (value === 'x') { + return ( + + + + ) + } + return ( + + {value} + + ) +} + +/* ─── plan card ──────────────────────────────────────────── */ +function PlanCard({ plan }: { plan: Plan }) { + const [hovered, setHovered] = useState(false) + const features = PLAN_EXTRA_FEATURES[plan.id] || plan.features + + return ( +
    + {/* Badge */} + {plan.badge && ( +
    + {plan.badge} +
    + )} + + {/* Plan name */} +

    + طرح +

    +

    + {plan.name} +

    + + {/* Price */} +
    + {typeof plan.price === 'number' ? ( + <> + + {plan.price.toLocaleString('fa-IR')} + + + {plan.period} + + + ) : ( + + تماس بگیرید + + )} +

    + {plan.period} +

    +
    + + {/* Divider */} +
    + + {/* Features */} +
      + {features.map((feat, i) => ( +
    • + + {feat} +
    • + ))} +
    + + {/* CTA */} + +
    + ) +} + +/* ─── FAQ item ───────────────────────────────────────────── */ +function FaqItem({ q, a, index }: { q: string; a: string; index: number }) { + const [open, setOpen] = useState(false) + + return ( +
    + + {open && ( +
    + {a} +
    + )} +
    + ) +} + +/* ─── CTA button with hover state ───────────────────────── */ +function CtaButton({ label }: { label: string }) { + const [hovered, setHovered] = useState(false) + return ( + + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Subscribe() { + return ( +
    + {/* ─── Page Header ───────────────────────────────── */} +
    +

    + اشتراک و دسترسی +

    +

    + دسترسی به جامع‌ترین تحلیل‌های بازار فولاد ایران — از گزارش‌های فصلی تا مشاوره سازمانی +

    +
    + + {/* ─── Section 1: Plans ──────────────────────────── */} +
    +
    +
    + {plans.map((plan) => ( +
    + +
    + ))} +
    +
    +
    + + {/* ─── Section 2: Feature Comparison Table ─────────── */} +
    +
    +
    +

    + مقایسه کامل امکانات +

    +

    + مقایسه تفصیلی امکانات هر سه طرح اشتراک +

    +
    + +
    + + + + + {plans.map((plan) => ( + + ))} + + + + {FEATURES.map((feat, i) => ( + + + + + + + ))} + +
    + امکانات + + {plan.name} +
    + {feat.label} +
    +
    +
    +
    + + {/* ─── Section 3: FAQ ──────────────────────────────── */} +
    +
    +
    +

    + پرسش‌های متداول +

    +

    + پاسخ به سوال‌های رایج درباره اشتراک و گزارش‌ها +

    +
    +
    + {FAQS.map((faq, i) => ( + + ))} +
    +
    +
    + + {/* ─── Section 4: CTA Band ─────────────────────────── */} +
    +
    +

    + اندیشکده فولاد ایران +

    +

    + آماده شروع هستید؟ +

    +

    + به بیش از ۲۸۰۰ متخصص و مدیر صنعت فولاد بپیوندید که تصمیمات خود را با تحلیل‌های + اندیشکده می‌گیرند. +

    + +
    + + +
    +
    +
    + + {/* ─── Responsive styles ─────────────────────────── */} + +
    + ) +} + +/* ─── outline CTA link ───────────────────────────────────── */ +function OutlineCtaLink({ label }: { label: string }) { + const [hovered, setHovered] = useState(false) + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + style={{ + padding: '14px 32px', + fontSize: 13, + fontWeight: 700, + color: 'var(--paper)', + background: 'transparent', + border: hovered + ? '1px solid rgba(244,239,231,0.6)' + : '1px solid rgba(244,239,231,0.35)', + textDecoration: 'none', + letterSpacing: '0.3px', + transition: 'border-color 150ms', + display: 'inline-flex', + alignItems: 'center', + gap: 8, + }} + > + + {label} + + ) +} diff --git a/src/pages/Team/Team.tsx b/src/pages/Team/Team.tsx new file mode 100644 index 0000000..884518c --- /dev/null +++ b/src/pages/Team/Team.tsx @@ -0,0 +1,333 @@ +import { useState } from 'react' +import { team } from '@/data/team' + +/* ─── derived stats ──────────────────────────────────────── */ +const totalReports = team.reduce((sum, m) => sum + m.reportCount, 0) +const memberCount = team.length +const combinedYears = 85 + +/* ─── member card ────────────────────────────────────────── */ +function MemberCard({ member }: { member: (typeof team)[0] }) { + const [hovered, setHovered] = useState(false) + const [bioExpanded, setBioExpanded] = useState(false) + + return ( +
    setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {/* card header */} +
    + {/* initial avatar */} +
    + {member.initial} +
    + +
    +

    + {member.name} +

    + + {member.role} + +
    +
    + + {/* bio */} +
    +

    + {member.bio} +

    + {!bioExpanded && member.bio.length > 120 && ( + + )} +
    + + {/* expertise tags */} +
    +
    + {member.expertise.map((tag) => ( + + {tag} + + ))} +
    +
    + + {/* footer: report count + email */} +
    +
    + + {member.reportCount.toLocaleString('fa-IR')} + + + گزارش منتشرشده + +
    + + {member.email && ( + + {member.email} + + )} +
    +
    + ) +} + +/* ─── main component ─────────────────────────────────────── */ +export default function Team() { + return ( +
    + {/* ─── Section header ────────────────────────────────── */} +
    +
    +

    + تیم تحریریه +

    +
    + +
    + +
    + + متخصصان برجسته صنعت فولاد ایران + +
    +
    + + {/* ─── Stats row ─────────────────────────────────────── */} +
    + {[ + { value: totalReports.toLocaleString('fa-IR'), label: 'گزارش منتشرشده' }, + { value: memberCount.toLocaleString('fa-IR'), label: 'عضو تحریریه' }, + { value: `${combinedYears.toLocaleString('fa-IR')} سال`, label: 'تجربه ترکیبی' }, + ].map((stat, i) => ( +
    + + {stat.value} + + + {stat.label} + +
    + ))} +
    + + {/* ─── Member grid ───────────────────────────────────── */} +
    + {team.map((member) => ( + + ))} +
    + + +
    + ) +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..1578a61 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023", "DOM"], + "module": "esnext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + + /* Path aliases */ + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..d3c52ea --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..b608676 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' +import path from 'path' + +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +})