commit c6fbb9c7f20e087803eb25c999a7a00913d23ffc Author: alireza Date: Thu Aug 27 09:05:45 2026 +0330 feat: University Futures Radar — University of Isfahan foresight intelligence platform 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/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..6fa991d --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6af7e3 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the Oxlint configuration + +If you are developing a production application, we recommend enabling type-aware lint rules by installing `oxlint-tsgolint` and editing `.oxlintrc.json`: + +```json +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "oxc"], + "options": { + "typeAware": true + }, + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} +``` + +See the [Oxlint rules documentation](https://oxc.rs/docs/guide/usage/linter/rules) for the full list of rules and categories. diff --git a/design_system.md b/design_system.md new file mode 100644 index 0000000..e335643 --- /dev/null +++ b/design_system.md @@ -0,0 +1,92 @@ +# نظام طراحی سامانه هوشمندی راهبردی دانشگاه اصفهان (Design System Specification) + +این سند مرجع رسمی و استانداردهای طراحی دیداری، سلسله‌مراتب تایپوگرافی، پالت رنگ، الگوهای کامپوننت و راهنمای نگارش سامانه **«رادار آینده دانشگاه اصفهان»** است. + +--- + +## ۱. تایپوگرافی (Typography Standard) + +### ۱.۱. قلم واحد سازمانی (Universal Font Family) +- تمام متون، اعداد، نشان‌ها، دکمه‌ها و عناصر واسط کاربری باید منحصراً از قلم **Vazirmatn** استفاده کنند. +- استفاده از فونت‌های مونو یا سیستمی (`font-mono`, `Consolas`, `Courier`) برای اعداد و برچسب‌های فارسی **اکیداً ممنوع** است. اعداد فارسی باید با وزن متناسب قلم وزیرمتن نمایش داده شوند. + +```css +*, ::before, ::after { + font-family: 'Vazirmatn', -apple-system, BlinkMacSystemFont, 'Segoe UI', Tahoma, sans-serif !important; +} +``` + +### ۱.۲. مقیاس و سلسله‌مراتب تیترها (Heading Hierarchy & Contrast) +- **قاعده اساسی**: تیترها و عناوین باید به رنگ **مشکی کامل** (`#0f172a` یا `#000000`)، با وزن **بسیار ضخیم (Bold 800 - Black 900)** و با اندازه چشمگیر نسبت به متن بدنه باشند تا سلسله‌مراتب بصری در یک نگاه درک شود. + +| سطح عنصر | نام کلاس | اندازه فونت (rem / px) | وزن قلم | رنگ متن | کاربرد | +|---|---|---|---|---|---| +| **Display / Hero** | `.text-display` | `2.25rem` (36px) | 900 (Black) | `#0f172a` | ارقام کلیدی داشبورد و شاخص‌های آمادگی ۱۴۱۵ | +| **Heading 1** | `.heading-1` / `h1` | `1.75rem` (28px) | 900 (Black) | `#0f172a` | عنوان اصلی هر صفحه | +| **Heading 2** | `.heading-2` / `h2` | `1.25rem` (20px) | 800 (ExtraBold) | `#0f172a` | تیتر بخش‌های اصلی و سرفصل‌ها | +| **Heading 3** | `.heading-3` / `h3` | `1.00rem` (16px) | 800 (ExtraBold) | `#0f172a` | عنوان کارت‌های رادار و پنل‌های تحلیلی | +| **Heading 4** | `.heading-4` / `h4` | `0.875rem` (14px) | 700 (Bold) | `#1e293b` | عناوین فرعی و آیتم‌های لیست | +| **Body Main** | `.body-main` / `p` | `0.8125rem` (13px) | 500 (Medium) | `#334155` | متن توضیحات و خلاصه‌های سیاستی | +| **Caption / Meta**| `.caption` | `0.75rem` (12px) | 600 (SemiBold) | `#64748b` | اطلاعات فراداده، افق زمانی و منابع | +| **Badge / Chip** | `.badge-label` | `0.6875rem` (11px) | 700 (Bold) | متناسب با دسته | برچسب‌های وضعیت و شمارنده‌ها | + +--- + +## ۲. پالت رنگ و کنتراست (Color Palette) + +### ۲.۱. بوم و پس‌زمینه‌ها (Canvas & Surfaces) +- **بوم اصلی (Canvas Background)**: `#f4f6fb` (خاکستری سرد و ملایم با مش گرادینت سبک) +- **کارت‌ها (Card Surface)**: `#ffffff` (سفید یکدست با سایه نرم `0 8px 24px -4px rgba(15,23,42,0.04)`) +- **کارت‌های فرعی (Subtle Surface)**: `#f8fafc` +- **خطوط حاشیه (Borders)**: `#e2e8f0` (خطوط تفکیک‌کننده محکم اما چشم‌نواز) + +### ۲.۲. رنگ‌های موضوعی پاستلی (Pastel Theme Tints) +برای طبقه‌بندی هوشمندی آینده و ایجاد زیبایی در ساختار جعبه‌های بنتو: +- **روندها (Trends)**: پس‌زمینه `#eaf3fe` | حاشیه `#c8e0fd` | متن `#0284c7` +- **فناوری‌ها (Tech)**: پس‌زمینه `#e6f6f1` | حاشیه `#c4ecdf` | متن `#059669` +- **سیاست‌ها (Policies)**: پس‌زمینه `#fef7e6` | حاشیه `#fde8b8` | متن `#d97706` +- **سیگنال‌های آینده (Signals)**: پس‌زمینه `#fdecf2` | حاشیه `#f9cbd9` | متن `#e11d48` +- **برنامه‌ریزی ۱۴۱۵ / مثلث آینده**: پس‌زمینه `#f3effe` | حاشیه `#ded2fd` | متن `#7c3aed` + +--- + +## ۳. نوار کناری ناوبری (Sidebar Standard) + +- **بدنه نوار کناری**: پس‌زمینه سفید خالص (`#ffffff`)، مرز چپ با صفحه `#e2e8f0`، عرض ۲۷۰ پیکسل. +- **عناوین بخش‌ها**: خاکستری ملایم `#94a3b8`، فونت ۱۰.۵ پیکسل، وزن ۸۰۰، حروف کوچک، با فاصله مناسب از گروه قبلی. +- **دکمه‌های ناوبری**: + - *حالت عادی*: متن `#475569`، آیکون ۲۰ پیکسل با رنگ `#64748b`، گوشه‌های گرد ۱۲ پیکسل (`rounded-xl`). + - *حالت شناور (Hover)*: پس‌زمینه `#f1f5f9`، متن مشکی `#0f172a`. + - *حالت فعال (Active)*: پس‌زمینه سرمه‌ای تیره لوکس `#0f172a`، متن سفید خالص `#ffffff`، آیکون روشن، سایه ملایم. +- **شمارنده‌ها و برچسب‌ها**: + - نشان‌های عددی باید در سمت چپ آیتم قرار گیرند (`align-self: flex-start` در ساختار راست‌به‌چپ). + - متن شمارنده کاملاً با قلم وزیرمتن فارسی و بدون فونت مونو یا فاصله بی‌مورد درج شود. +- **حذف متون دوزبانه زاید**: + - تمامی پرانتزهای انگلیسی غیرضروری مانند `(Overview)`، `(Trends)`، `(Tech)`، `(Policies)`، `(Signals)`، `(Spider Gap)`، `(PULL/PUSH)` و `(CRUD)` در نوار کناری **ممنوع** بوده و با واژگان حرفه‌ای و فاخر فارسی جایگزین می‌شوند. + +--- + +## ۴. استانداردهای هندسی نمودارها و مدل‌های بصری + +### ۴.۱. مثلث هوشمندی آینده (Futures Triangle Geometry) +- **تناسب ابعادی استاندارد**: + - از ترسیم مثلث‌های عریض، پخ و تخت (نسبت عرض به ارتفاع ۲ به ۱) اکیداً پرهیز شود. + - نسبت ارتفاع به قاعده مثلث باید بین **۰.۸۵ تا ۰.۹۰** باشد (شبیه به مثلث متساوی‌الاضلاع کشیده و متوازن). + - اضلاع مثلث باید با خطوط رنگی ضخیم (حداقل ۴ پیکسل) و گرادینت‌های جهت‌دار متصل شوند. + - گره مرکزی (هوشمندی دانشگاه اصفهان) دقیقاً در گرانیگاه هندسی $(x_c, y_c)$ قرار گیرد. + +### ۴.۲. رادار دایره‌ای آینده‌پژوهی (Futures Radar) +- دایره مرکزی سفید مرواریدی، پره‌های رنگی ملایم برای ۴ ربع، نقاط سیگنال با حاشیه سفید تمیز و عدد شدت اثر درون نقطه. + +### ۴.۳. نمودار عنکبوتی ۱۰ بعدی (Spider Gap Chart) +- چندضلعی هدف دانشگاه اصفهان ۱۴۱۵ به رنگ سبز زمردی (`#00b894`) و چندضلعی وضع موجود به رنگ کهربایی (`#f59e0b`). + +--- + +## ۵. راهنمای زبان و لحن سازمانی (Content & Tone Standards) + +1. **حذف واژگان تکراری و عبارات توخالی (Zero AI-Slop)**: + - حذف برچسب‌های آکادمیک گل‌درشت مانند `نظریه سوهیل عنایت‌الله (Sohail Inayatullah)` از هدر صفحات و استفاده از برچسب‌های راهبردی خلاصه (`چارچوب آینده‌پژوهی`). + - حذف توضیحات شعاری و کش‌دار؛ محتوا باید مستقیماً پیامد دانشگاهی، عدد شاخص یا اقدام فوری را گزارش دهد. +2. **نگارش فارسی معیار و روان**: + - به کارگیری اصطلاحات دقیق آینده‌پژوهی و مدیریت دانشگاهی (مانند: *سیگنال‌های ضعیف، افق زمانی، شکاف بلوغ، شایستگی‌های نوظهور، آمایش آموزشی*). diff --git a/index.html b/index.html new file mode 100644 index 0000000..3ec44a7 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + + رادار آینده دانشگاه اصفهان | University Futures Radar + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..106a962 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1305 @@ +{ + "name": "uni", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "uni", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^1.34.0", + "react": "^19.2.8", + "react-dom": "^19.2.8" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^6.1.0", + "oxlint": "^1.79.0", + "typescript": "~6.0.2", + "vite": "^8.2.2" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.147.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", + "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.80.0.tgz", + "integrity": "sha512-RM3Plj+biQpxa5d1GOOX6ciDlcUROmm4OZ/pLTpitkQt2mJv4jhtY4cbgaetOm5UKWZe05/TGQ6o1Vl8EOHkrA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.80.0.tgz", + "integrity": "sha512-YlO5JEf0Yr2bUUlu8O8daVcUxtcGGbcSmyV7E7nSbJbfAdxTE0PFPwgnIlw7wXJaTYjb+qs5hI5q3jxUkI7cAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.80.0.tgz", + "integrity": "sha512-BULDOyO3AhsmdWfQeIUCykDt3dd7XZBGLhp1eIh56skRv01O+cNjNPwXMIbeW1x4+pxcln5if72wcRgViVo7PA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.80.0.tgz", + "integrity": "sha512-YJ4JzLw7N5TDSQFlA0hAQGHvnDZgyypm1yunObVWcWiF9KM7eGCJKYKLgTC2Fi/57OdnBhbj4OkzPGdFQJ6HyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.80.0.tgz", + "integrity": "sha512-AYUIk5QnL0s8oWAYsREZwkRYy1SupJTXALo93J1TgzHywxQtdM99FecRMQ87MXEdPQ0j1TmEpeeq3fGNkpvMqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.80.0.tgz", + "integrity": "sha512-9hBZVANupQ89W9dXyE0n8doCyaW5pDyGn3y6XlIMPZ+rIKuyqkr3SNUXmVJIhuvUq0NBU3RBiSXXE69l4XI6KA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.80.0.tgz", + "integrity": "sha512-SvS2uKqzY+pbfuvAHzH4338R6Zwo805GAwrIMVvK1KxoOWCIjZUdfzTCvilD7z6JK91v011+zYMryabhDo2AsQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.80.0.tgz", + "integrity": "sha512-tCLadyqRVL3pQTRPNg7cjXKvcvS4fbyXeQHhKk5BTJ1oftQln5/yIIWbu/Xom/DX41zv2P9QGt6+D/TtQVtY3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.80.0.tgz", + "integrity": "sha512-XfpCNRlOPcLlJl4Bn/FUhjqlR6BVavEykERBf/MV7YA9VZDa5g5znVqYhyviMafcxS9Pe/i/kPvHNO0U6svEHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.80.0.tgz", + "integrity": "sha512-3I4yMwcFG9NeO8ioY6JBBuKsIm5GL/x7MATt1S4tVWaxPu5HcJ+XnLUbcVBTxG8q2Wu56HSj+NmXQiVYb1lp6A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.80.0.tgz", + "integrity": "sha512-E1wAKymkpe1/E8helzBKdm81OBOF+ezxRyXRMEuik3ZpWDER5CPOKZwF66RsdwW98uwZv8UTFremUQtC1CzdJA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.80.0.tgz", + "integrity": "sha512-+gLRGD4sIo3+VA++iham5UxD9tKSoJ/VOrROCEXIcknrYtQg6iIQgvjN0cpiRF7N6UYC7pJbvHJlDnMge5LRpQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.80.0.tgz", + "integrity": "sha512-aR0PrzHj9leW3NmzBAAP4EzdoBNoJcs9sjnIQPIwyRnBGYrRbXUIpEB5Q39AqK3PLY5JK5uEhDQDiUa1QSAstw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.80.0.tgz", + "integrity": "sha512-vSVh5cSo3Xxs6ghBCcFJlpbkbENzDog1qXtoXLa/HC3aCrR4XO76GZbXmQoCPHnu99nQpdCeC3H9tdNICfDh7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.80.0.tgz", + "integrity": "sha512-FfzBXpNQ8u7/ZI/p8bl73MeZ508Ax3hxWp3SiJpEFiC+BB9XcXy5FAZHTLKDPSzrUpxQZSZJAVdDmuJp/+HDBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.80.0.tgz", + "integrity": "sha512-zMzbkumtmprCgRwoYNzcB3iC39fXdJIMLMU33KdCjEGLlJGOEt1+LwQ4LF8ndLzAEKVz4BR0y3V6Xrkk3Nm3yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.80.0.tgz", + "integrity": "sha512-ib6iRcrXsk4t1fm3iKcwksyWh1ZkZXC/2mEzakl0ai2+6HZunf1WWMZ/xP9EJAvw9g9K4UVTC3NF/+G2qLrbTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.80.0.tgz", + "integrity": "sha512-xhRWBMpLxZvgKAH6+DJZmpP+W8Y8UdQOSU1JfxSWNXsaBaRGW77j+1hCuNHlzj7OH4SPN8fYd1q0o2qrDtoVyw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.80.0.tgz", + "integrity": "sha512-yAnO7lwBYQnz2pcfBPIGQQZWIX5zd5R/1aAKIF3oE+TVj7IhoHcROjOkz3sRDngzqhfPKfFaXqug5j5rE5dn6Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", + "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", + "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", + "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", + "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", + "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", + "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", + "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", + "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", + "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", + "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", + "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", + "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", + "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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", + "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", + "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+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.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", + "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", + "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/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.0.tgz", + "integrity": "sha512-qd2BzUBehkov86WFhg0JkEFEYyCLG9uPCe6qWTY/kRlss9OvJrOF2UbIWT7p+8IzZHkEu0DNGHc4HSv+JdDLsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "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", + "oxc-transform-react": "^0.145.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "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==", + "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/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/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/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "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.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "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/lucide-react": { + "version": "1.34.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.34.0.tgz", + "integrity": "sha512-vnjGJNI7Htk5+oWW8gXGuaLgwgAb0T6/iZbBrp9JCfRFwdNWZ0YTm3eyxjOLgwN6r8iyAf3UA70zNmBRBNv7yg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "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/oxlint": { + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.80.0.tgz", + "integrity": "sha512-5nTiSps4qdbCWLbxzuO00alHkEO2exR9YMN/ig6QXWrLsYSG0KaObOAM+l6oU2LcKPWoSAGYbkZIGEu1ViiWKA==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.80.0", + "@oxlint/binding-android-arm64": "1.80.0", + "@oxlint/binding-darwin-arm64": "1.80.0", + "@oxlint/binding-darwin-x64": "1.80.0", + "@oxlint/binding-freebsd-x64": "1.80.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.80.0", + "@oxlint/binding-linux-arm-musleabihf": "1.80.0", + "@oxlint/binding-linux-arm64-gnu": "1.80.0", + "@oxlint/binding-linux-arm64-musl": "1.80.0", + "@oxlint/binding-linux-ppc64-gnu": "1.80.0", + "@oxlint/binding-linux-riscv64-gnu": "1.80.0", + "@oxlint/binding-linux-riscv64-musl": "1.80.0", + "@oxlint/binding-linux-s390x-gnu": "1.80.0", + "@oxlint/binding-linux-x64-gnu": "1.80.0", + "@oxlint/binding-linux-x64-musl": "1.80.0", + "@oxlint/binding-openharmony-arm64": "1.80.0", + "@oxlint/binding-win32-arm64-msvc": "1.80.0", + "@oxlint/binding-win32-ia32-msvc": "1.80.0", + "@oxlint/binding-win32-x64-msvc": "1.80.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "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.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/rolldown": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", + "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.147.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.6", + "@rolldown/binding-android-arm64": "1.2.6", + "@rolldown/binding-darwin-arm64": "1.2.6", + "@rolldown/binding-darwin-x64": "1.2.6", + "@rolldown/binding-freebsd-x64": "1.2.6", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", + "@rolldown/binding-linux-arm64-gnu": "1.2.6", + "@rolldown/binding-linux-arm64-musl": "1.2.6", + "@rolldown/binding-linux-ppc64-gnu": "1.2.6", + "@rolldown/binding-linux-s390x-gnu": "1.2.6", + "@rolldown/binding-linux-x64-gnu": "1.2.6", + "@rolldown/binding-linux-x64-musl": "1.2.6", + "@rolldown/binding-openharmony-arm64": "1.2.6", + "@rolldown/binding-win32-arm64-msvc": "1.2.6", + "@rolldown/binding-win32-x64-msvc": "1.2.6" + } + }, + "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/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/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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/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/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "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.4.0 || ^0.5.0", + "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 + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ce9d57 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "uni", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "oxlint", + "preview": "vite preview" + }, + "dependencies": { + "lucide-react": "^1.34.0", + "react": "^19.2.8", + "react-dom": "^19.2.8" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^6.1.0", + "oxlint": "^1.79.0", + "typescript": "~6.0.2", + "vite": "^8.2.2" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file 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.css b/src/App.css new file mode 100644 index 0000000..f90339d --- /dev/null +++ b/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..ec82d45 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,274 @@ +import React, { useState, useEffect } from 'react'; +import { + IntelligenceItem, + University1415Dimension, + JobEntity, + SkillEntity, + FuturesForce, + Category, + UserSession +} from './types/futures'; +import { dataService } from './services/dataService'; +import { authService } from './services/authService'; + +// Layout +import { Header } from './components/layout/Header'; +import { Sidebar } from './components/layout/Sidebar'; + +// Modals +import { GlobalSearchModal } from './components/modals/GlobalSearchModal'; +import { EntityEditorModal } from './components/modals/EntityEditorModal'; + +// Pages +import { Login } from './pages/Login'; +import { Dashboard } from './pages/Dashboard'; +import { RadarPage } from './pages/RadarPage'; +import { ItemDetailPage } from './pages/ItemDetailPage'; +import { LaborMarketPage } from './pages/LaborMarketPage'; +import { University1415Page } from './pages/University1415Page'; +import { GapAnalysisPage } from './pages/GapAnalysisPage'; +import { FuturesTrianglePage } from './pages/FuturesTrianglePage'; +import { StrategicNetworkPage } from './pages/StrategicNetworkPage'; +import { ReportsPage } from './pages/ReportsPage'; +import { AdminPage } from './pages/AdminPage'; +import { SettingsPage } from './pages/SettingsPage'; + +export function App() { + const [currentSession, setCurrentSession] = useState(authService.getSession()); + const [currentRoute, setCurrentRoute] = useState('/'); + const [selectedItem, setSelectedItem] = useState(null); + const [radarCategoryFilter, setRadarCategoryFilter] = useState('all'); + + // Modals + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + + // Live Data State + const [items, setItems] = useState([]); + const [dimensions, setDimensions] = useState([]); + const [jobs, setJobs] = useState([]); + const [skills, setSkills] = useState([]); + const [forces, setForces] = useState([]); + + // Refresh all state from dataService + const refreshData = () => { + setItems(dataService.getAllItems()); + setDimensions(dataService.getDimensions()); + setJobs(dataService.getJobs()); + setSkills(dataService.getSkills()); + setForces(dataService.getFuturesForces()); + }; + + useEffect(() => { + refreshData(); + const unsub = authService.subscribe((sess) => { + setCurrentSession(sess); + if (!sess) { + setCurrentRoute('/login'); + } + }); + return () => unsub(); + }, []); + + // Global Ctrl+K listener + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { + e.preventDefault(); + setIsSearchOpen(true); + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, []); + + const handleNavigate = (route: string) => { + setSelectedItem(null); + + // Handle query params like /radar?category=trend + if (route.startsWith('/radar?category=')) { + const cat = route.split('=')[1] as Category; + setRadarCategoryFilter(cat); + setCurrentRoute('/radar'); + } else { + setRadarCategoryFilter('all'); + setCurrentRoute(route); + } + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const handleSelectItem = (item: IntelligenceItem) => { + setSelectedItem(item); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const handleSelectDimension = (dim: University1415Dimension) => { + setSelectedItem(null); + setCurrentRoute('/university-1415'); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const handleSaveNewItem = (newItem: IntelligenceItem) => { + dataService.createItem(newItem); + refreshData(); + }; + + // If on login route or not authenticated + if (!currentSession || currentRoute === '/login') { + return ( + { + setCurrentRoute('/'); + }} + /> + ); + } + + // Calculate section counts + const counts = { + signals: items.filter((i) => i.category === 'weak_signal').length, + trends: items.filter((i) => i.category === 'trend').length, + tech: items.filter((i) => i.category === 'technology').length, + policies: items.filter((i) => i.category === 'policy').length + }; + + return ( +
+ {/* Institutional Sidebar */} + + + {/* Main Content Area */} +
+
setIsSearchOpen(true)} + onNavigate={handleNavigate} + /> + +
+ {/* Detail Page View (Highest Priority) */} + {selectedItem ? ( + setSelectedItem(null)} + onSelectItem={handleSelectItem} + onSelectDimension={handleSelectDimension} + /> + ) : ( + <> + {currentRoute === '/' && ( + + )} + + {currentRoute === '/radar' && ( + setIsCreateModalOpen(true)} + initialCategory={radarCategoryFilter} + /> + )} + + {currentRoute === '/labor-market' && ( + + )} + + {currentRoute === '/university-1415' && ( + + )} + + {currentRoute === '/gap-analysis' && ( + + )} + + {currentRoute === '/futures-triangle' && ( + + )} + + {currentRoute === '/network' && ( + + )} + + {currentRoute === '/reports' && ( + + )} + + {currentRoute === '/admin' && ( + + )} + + {currentRoute === '/settings' && ( + + )} + + )} +
+
+ + {/* Global Modals */} + setIsSearchOpen(false)} + items={items} + dimensions={dimensions} + jobs={jobs} + skills={skills} + onSelectItem={handleSelectItem} + onSelectDimension={handleSelectDimension} + onSelectJob={() => { + setSelectedItem(null); + setCurrentRoute('/labor-market'); + }} + /> + + setIsCreateModalOpen(false)} + onSave={handleSaveNewItem} + /> +
+ ); +} + +export default App; 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/cards/Dimension1415Card.tsx b/src/components/cards/Dimension1415Card.tsx new file mode 100644 index 0000000..d70dd74 --- /dev/null +++ b/src/components/cards/Dimension1415Card.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { University1415Dimension } from '../../types/futures'; +import { toPersianDigits } from '../../utils/persianNumbers'; +import { Target, ArrowLeft, Lightbulb, ArrowUpRight } from 'lucide-react'; + +interface Dimension1415CardProps { + dimension: University1415Dimension; + onClick: () => void; +} + +export const Dimension1415Card: React.FC = ({ dimension, onClick }) => { + return ( +
+
+ {/* Header Badges */} +
+ + {dimension.titleEn} + + + آمادگی: {toPersianDigits(dimension.readinessPercentage)}٪ + +
+ + {/* Title */} +

+ {dimension.title} +
+ +
+

+ + {/* Definition */} +

+ {dimension.definition} +

+ + {/* Strategic Recommendation Preview */} + {dimension.strategicRecommendations[0] && ( +
+ + {dimension.strategicRecommendations[0]} +
+ )} +
+ + {/* Scores & Gap Meter */} +
+
+ + موجود: {toPersianDigits(dimension.currentScore)} + + + هدف: {toPersianDigits(dimension.targetScore)} + + + شکاف: {toPersianDigits(dimension.gap)} + +
+ + {/* Readiness Bar */} +
+
+
+
+
+ ); +}; diff --git a/src/components/cards/IntelligenceCard.tsx b/src/components/cards/IntelligenceCard.tsx new file mode 100644 index 0000000..18ca240 --- /dev/null +++ b/src/components/cards/IntelligenceCard.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { IntelligenceItem } from '../../types/futures'; +import { + toPersianDigits, + getCategoryLabelFa, + getCategoryColor, + getHorizonShortFa +} from '../../utils/persianNumbers'; +import { Clock, Zap, ArrowUpRight } from 'lucide-react'; + +interface IntelligenceCardProps { + item: IntelligenceItem; + onClick: () => void; +} + +export const IntelligenceCard: React.FC = ({ item, onClick }) => { + const catColor = getCategoryColor(item.category); + const isCritical = item.status === 'critical' || item.impactScore === 5; + + return ( +
+
+ {/* Top Badges */} +
+ + {getCategoryLabelFa(item.category)} + + +
+ + {getHorizonShortFa(item.horizon)} +
+
+ + {/* Title */} +

+ {item.title} +

+ + {/* Executive Summary */} +

+ {item.executiveSummary} +

+
+ + {/* Footer Indicators */} +
+
+ + + اثر: {toPersianDigits(item.impactScore)}/۵ + +
+ +
+ +
+
+
+ ); +}; diff --git a/src/components/cards/JobSkillCard.tsx b/src/components/cards/JobSkillCard.tsx new file mode 100644 index 0000000..453fdf3 --- /dev/null +++ b/src/components/cards/JobSkillCard.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { JobEntity, SkillEntity } from '../../types/futures'; +import { toPersianDigits, getHorizonLabelFa } from '../../utils/persianNumbers'; +import { Briefcase, Brain, TrendingUp, AlertTriangle, ShieldCheck } from 'lucide-react'; + +interface JobCardProps { + job: JobEntity; +} + +export const JobCard: React.FC = ({ job }) => { + const isGrowing = job.type === 'growing'; + + return ( +
+
+
+ + {isGrowing ? ( + <> + + شغل رو به رشد (+{toPersianDigits(job.growthRatePercent)}٪) + + ) : ( + <> + + شغل در معرض تحول ({toPersianDigits(job.automationRiskPercent)}٪ ریسک) + + )} + + {getHorizonLabelFa(job.horizon)} +
+ +

{job.title}

+
+ دانشکده / گروه مرتبط: {job.discipline} +
+ +

+ {job.description} +

+ + {/* Required Skills Badges */} +
+ {job.keyRequiredSkills.map((sk, idx) => ( + + {sk} + + ))} +
+
+ + {/* University Action Box */} +
+ +
+ اقدام دانشگاه اصفهان: + {job.universityAction} +
+
+
+ ); +}; + +interface SkillCardProps { + skill: SkillEntity; +} + +export const SkillCard: React.FC = ({ skill }) => { + const isGrowing = skill.type === 'growing'; + + return ( +
+
+
+ + {isGrowing ? 'شایستگی صعودی' : 'مهارت در حال کاهش'} + + + اهمیت: {toPersianDigits(skill.importanceScore)}/۵ + +
+ +

{skill.title}

+

+ {skill.description} +

+ + {/* Affected Fields */} +
+ رشته‌های تحت تأثیر: +
+ {skill.affectedAcademicFields.map((f, idx) => ( + + {f} + + ))} +
+
+
+ +
+ وضعیت جایگزینی / ارتقا: + {skill.replacementOrAugmentation} +
+
+ ); +}; diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx new file mode 100644 index 0000000..8cc32d0 --- /dev/null +++ b/src/components/layout/Header.tsx @@ -0,0 +1,177 @@ +import React, { useState } from 'react'; +import { UserSession, PersonaType } from '../../types/futures'; +import { authService, PRESET_USERS } from '../../services/authService'; +import { Search, Bell, LogOut, Shield, Compass, ChevronDown, UserCheck, Sparkles } from 'lucide-react'; + +interface HeaderProps { + currentSession: UserSession | null; + onOpenSearch: () => void; + onNavigate: (route: string) => void; +} + +export const Header: React.FC = ({ currentSession, onOpenSearch, onNavigate }) => { + const [showPersonaMenu, setShowPersonaMenu] = useState(false); + const [showNotifications, setShowNotifications] = useState(false); + + const handleSwitchPersona = (role: PersonaType) => { + authService.switchPersona(role); + setShowPersonaMenu(false); + }; + + const handleLogout = () => { + authService.logout(); + onNavigate('/login'); + }; + + return ( +
+ {/* Brand & Emblem */} +
onNavigate('/')}> +
+ +
+
+
+ + رادار آینده دانشگاه اصفهان + + + افق ۱۴۱۵ + +
+ + سامانه هوشمندی راهبردی و آینده‌پژوهی آموزش عالی + +
+
+ + {/* Center Global Search Trigger (Bento Pill Input) */} +
+ +
+ + {/* Right Controls: Persona Switcher, Notifications, Profile */} +
+ {/* Mobile Search Button */} + + + {/* Persona Perspective Switcher */} +
+ + + {showPersonaMenu && ( +
+
+ تغییر دیدگاه تحلیلی سامانه: +
+ {(Object.keys(PRESET_USERS) as PersonaType[]).map((pKey) => { + const user = PRESET_USERS[pKey]; + const isActive = currentSession?.role === pKey; + return ( + + ); + })} +
+ )} +
+ + {/* Notifications Trigger */} +
+ + + {showNotifications && ( +
+
+ اعلان‌های فوری رادار + ۳ سیگنال بحرانی +
+
+
+ فراگیر شدن هوش مصنوعی زاینده +

ضرورت بازطراحی فرآیندهای سنجش آموزشی در نیمسال جاری

+
+
+ بحران آب و فرونشست اصفهان +

ابلاغ مأموریت‌های استانی و بازنگری پژوهانه‌ها

+
+
+
+ )} +
+ + {/* User Profile Pill & Logout */} +
+
+
+ {currentSession?.fullName ? currentSession.fullName.slice(0, 1) : 'د'} +
+
+ + {currentSession?.fullName || 'کاربر سازمانی'} + + + {currentSession?.roleTitleFa || 'ریاست دانشگاه'} + +
+
+ + +
+
+
+ ); +}; diff --git a/src/components/layout/PersonaBanner.tsx b/src/components/layout/PersonaBanner.tsx new file mode 100644 index 0000000..18da94b --- /dev/null +++ b/src/components/layout/PersonaBanner.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { UserSession, PersonaType } from '../../types/futures'; +import { authService } from '../../services/authService'; +import { Shield, Sparkles, Target, Compass } from 'lucide-react'; + +interface PersonaBannerProps { + currentSession: UserSession | null; +} + +export const PersonaBanner: React.FC = ({ currentSession }) => { + const currentRole = currentSession?.role || 'president'; + + const personaConfig: Record< + PersonaType, + { title: string; question: string; icon: React.ReactNode; bg: string; border: string; accent: string; badge: string } + > = { + president: { + title: 'دیدگاه ریاست و هیئت رئیسه دانشگاه', + question: '«این تغییرات، تهدیدها و فرصت‌ها برای کلیت دانشگاه اصفهان چه معنایی دارد و اولویت فوری چیست؟»', + icon: , + bg: 'bg-emerald-50/70', + border: 'border-emerald-200', + accent: 'text-emerald-700', + badge: 'bg-emerald-100 text-emerald-800' + }, + researcher: { + title: 'دیدگاه پژوهشگر آینده‌پژوهی و تحلیل‌گر علم', + question: '«چه سیگنال‌های ضعیف، فناوری‌های اخلال‌گر و روندهای جدیدی در حال ظهور در آموزش عالی هستند؟»', + icon: , + bg: 'bg-sky-50/70', + border: 'border-sky-200', + accent: 'text-sky-700', + badge: 'bg-sky-100 text-sky-800' + }, + planner: { + title: 'دیدگاه برنامه‌ریز راهبردی و تحول آموزشی', + question: '«شکاف میان وضعیت موجود و چشم‌انداز دانشگاه اصفهان ۱۴۱۵ کجاست و چه برنامه‌هایی باید اولویت یابند؟»', + icon: , + bg: 'bg-amber-50/70', + border: 'border-amber-200', + accent: 'text-amber-700', + badge: 'bg-amber-100 text-amber-800' + }, + admin: { + title: 'دیدگاه راهبری و مدیریت سیستم هوشمندی', + question: '«مدیریت یکپارچه پایگاه داده، ارزیابی شاخص‌ها، ثبت سیگنال‌های تازه و صدور گزارش‌های نهادی»', + icon: , + bg: 'bg-purple-50/70', + border: 'border-purple-200', + accent: 'text-purple-700', + badge: 'bg-purple-100 text-purple-800' + } + }; + + const currentConfig = personaConfig[currentRole]; + + return ( +
+
+ + {currentConfig.icon} + +
+ {currentConfig.title}: + {currentConfig.question} +
+
+ + {/* Quick Perspective Switching Pills */} +
+ تغییر لنز: +
+ + + +
+
+
+ ); +}; diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx new file mode 100644 index 0000000..e6cc15e --- /dev/null +++ b/src/components/layout/Sidebar.tsx @@ -0,0 +1,304 @@ +import React, { useState } from 'react'; +import { + LayoutDashboard, + Radar, + Briefcase, + Target, + GitPullRequest, + Triangle, + GitFork, + FileText, + Database, + Settings, + ChevronDown, + ChevronLeft, + Sparkles, + TrendingUp, + Cpu, + BookOpen +} from 'lucide-react'; +import { toPersianDigits } from '../../utils/persianNumbers'; + +interface SidebarProps { + currentRoute: string; + onNavigate: (route: string) => void; + counts: { + signals: number; + trends: number; + tech: number; + policies: number; + }; +} + +export const Sidebar: React.FC = ({ currentRoute, onNavigate, counts }) => { + const [radarExpanded, setRadarExpanded] = useState(true); + + const isActive = (route: string) => currentRoute === route; + + return ( + + ); +}; diff --git a/src/components/modals/EntityEditorModal.tsx b/src/components/modals/EntityEditorModal.tsx new file mode 100644 index 0000000..1218e4c --- /dev/null +++ b/src/components/modals/EntityEditorModal.tsx @@ -0,0 +1,258 @@ +import React, { useState } from 'react'; +import { IntelligenceItem, Category, Horizon, ItemStatus } from '../../types/futures'; +import { X, Save } from 'lucide-react'; + +interface EntityEditorModalProps { + isOpen: boolean; + onClose: () => void; + onSave: (item: IntelligenceItem) => void; + initialItem?: IntelligenceItem | null; +} + +export const EntityEditorModal: React.FC = ({ + isOpen, + onClose, + onSave, + initialItem +}) => { + const isEditing = !!initialItem; + + const [title, setTitle] = useState(initialItem?.title || ''); + const [category, setCategory] = useState(initialItem?.category || 'trend'); + const [horizon, setHorizon] = useState(initialItem?.horizon || 'near'); + const [status, setStatus] = useState(initialItem?.status || 'active'); + const [impactScore, setImpactScore] = useState(initialItem?.impactScore || 4); + const [uncertaintyScore, setUncertaintyScore] = useState(initialItem?.uncertaintyScore || 2); + const [geographicScope, setGeographicScope] = useState<'ایران' | 'منطقه‌ای' | 'جهانی'>( + initialItem?.geographicScope || 'ایران' + ); + const [summary, setSummary] = useState(initialItem?.executiveSummary || ''); + const [whyItMatters, setWhyItMatters] = useState(initialItem?.whyItMatters || ''); + const [relevance, setRelevance] = useState(initialItem?.relevanceToUniversity || ''); + + if (!isOpen) return null; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!title.trim() || !summary.trim()) return; + + const item: IntelligenceItem = { + id: initialItem?.id || `custom-${Date.now()}`, + title, + category, + horizon, + status, + impactScore, + uncertaintyScore, + confidence: 'high', + geographicScope, + executiveSummary: summary, + whyItMatters, + relevanceToUniversity: relevance, + impact: initialItem?.impact || { + education: impactScore, + research: Math.max(1, impactScore - 1), + operations: 3, + students: impactScore, + faculty: Math.max(1, impactScore - 1), + laborMarket: 4, + strategicImportance: impactScore + }, + evidence: initialItem?.evidence || [ + { + title: 'شواهد ثبت شده توسط کاربر در سامانه هوشمندی', + source: 'داده‌ورزی داخلی دانشگاه اصفهان', + date: '۱۴۰۳/۰۶/۰۱', + snippet: summary + } + ], + implications: initialItem?.implications || { + opportunities: ['فرصت نوآوری آموزشی و پاسخ به تقاضای بازار'], + risks: ['خطر عدم انطباق بهنگام برنامه‌های درسی'], + threats: ['عقب‌ماندگی از رقبای منطقه‌ای'], + requiredCapabilities: ['توسعه زیرساخت‌ها و بازآموزی اساتید'], + affectedDepartments: ['معاونت آموزشی', 'گروه‌های آموزشی مرتبط'] + }, + relatedItemIds: initialItem?.relatedItemIds || [], + relatedJobIds: initialItem?.relatedJobIds || [], + relatedSkillIds: initialItem?.relatedSkillIds || [], + relatedDimensionIds: initialItem?.relatedDimensionIds || ['dim-1'] + }; + + onSave(item); + onClose(); + }; + + return ( +
+
e.stopPropagation()} + > +
+

+ {isEditing ? 'ویرایش موجودیت هوشمندی' : 'افزودن سیگنال / روند / فناوری جدید'} +

+ +
+ +
+ {/* Title */} +
+ + setTitle(e.target.value)} + placeholder="مثال: دوقلوی دیجیتال آزمایشگاه‌های مهندسی دانشگاه اصفهان" + className="bg-[#091122] border border-slate-800 rounded-lg p-2.5 text-white outline-none focus:border-teal-500 text-xs" + /> +
+ + {/* Row: Category, Horizon, Status */} +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + {/* Row: Impact Score & Scope */} +
+
+ + setImpactScore(Number(e.target.value))} + className="bg-[#091122] border border-slate-800 rounded-lg p-2 text-white outline-none focus:border-teal-500 text-xs" + /> +
+ +
+ + setUncertaintyScore(Number(e.target.value))} + className="bg-[#091122] border border-slate-800 rounded-lg p-2 text-white outline-none focus:border-teal-500 text-xs" + /> +
+ +
+ + +
+
+ + {/* Summary */} +
+ +