From 9227c687fcd4f320a66890e82a933dd95ea2f233 Mon Sep 17 00:00:00 2001 From: mikemoi Date: Mon, 22 Jun 2026 22:59:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=BF=83=E7=90=86=E6=B5=8B=E8=AF=84=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 41 + .vscode/c_cpp_properties.json | 18 + .vscode/launch.json | 24 + .vscode/settings.json | 59 + README.md | 31 + app/[locale]/globals.css | 122 + app/[locale]/layout.tsx | 35 + app/[locale]/page.tsx | 38 + .../questionnaire/[id]/details/page.tsx | 35 + app/[locale]/questionnaire/[id]/page.tsx | 24 + .../questionnaire/[id]/result/page.tsx | 98 + app/[locale]/questionnaire/page.tsx | 5 + app/apple-icon.png | Bin 0 -> 16566 bytes app/favicon.ico | Bin 0 -> 15086 bytes app/icon0.svg | 1 + app/icon1.png | Bin 0 -> 6991 bytes components/CategoryFilters.tsx | 61 + components/Navbar.tsx | 43 + components/questionnaire/List.tsx | 119 + .../QuestionnaireDetailsPage.tsx | 163 + .../result/analysis/ADHDResult.tsx | 134 + .../result/analysis/AttachmentResult.tsx | 84 + .../result/analysis/BDI2Result.tsx | 281 + .../result/analysis/BigFiveResult.tsx | 197 + .../result/analysis/CRTResult.tsx | 59 + .../result/analysis/CareerAnchorsResult.tsx | 76 + .../result/analysis/DASS21Result.tsx | 271 + .../result/analysis/DarkTriadResult.tsx | 58 + .../result/analysis/EmpathyResult.tsx | 59 + .../result/analysis/FisherResult.tsx | 79 + .../result/analysis/GAD7Result.tsx | 198 + .../result/analysis/GDResult.tsx | 149 + .../result/analysis/GritResult.tsx | 96 + .../result/analysis/HEXACOResult.tsx | 67 + .../result/analysis/ISIResult.tsx | 95 + .../result/analysis/MaximizerResult.tsx | 83 + .../result/analysis/NPDResult.tsx | 181 + .../analysis/NeedForCognitionResult.tsx | 82 + .../result/analysis/OCDResult.tsx | 77 + .../result/analysis/OEPSResult.tsx | 66 + .../result/analysis/PHQ9Result.tsx | 240 + .../result/analysis/PSS10Result.tsx | 284 + .../result/analysis/RIASECResult.tsx | 79 + .../result/analysis/ResultAnalysis.tsx | 119 + .../result/analysis/SCL90Result.tsx | 156 + .../result/analysis/SDSResult.tsx | 171 + .../result/analysis/SchwartzResult.tsx | 133 + .../result/analysis/SelfControlResult.tsx | 89 + .../result/analysis/SelfEsteemResult.tsx | 77 + .../result/analysis/VIAResult.tsx | 140 + .../result/analysis/WHO5Result.tsx | 104 + .../result/public/AnswerList.tsx | 43 + .../result/public/ResultContainer.tsx | 133 + .../questionnaire/test/QuestionnaireTest.tsx | 238 + .../test/private/ADHDCalculator.tsx | 89 + .../test/private/AttachmentCalculator.tsx | 40 + .../test/private/BDI2Calculator.tsx | 82 + .../test/private/BigFiveCalculator.tsx | 85 + .../test/private/CRTCalculator.tsx | 29 + .../test/private/CareerAnchorsCalculator.tsx | 55 + .../test/private/DASS21Calculator.tsx | 95 + .../test/private/DarkTriadCalculator.tsx | 28 + .../test/private/EmpathyCalculator.tsx | 30 + .../test/private/FisherCalculator.tsx | 44 + .../test/private/GAD7Calculator.tsx | 49 + .../test/private/GDCalculator.tsx | 109 + .../test/private/GritCalculator.tsx | 40 + .../test/private/HEXACOCalculator.tsx | 41 + .../test/private/ISICalculator.tsx | 49 + .../test/private/MaximizerCalculator.tsx | 32 + .../test/private/NPDCalculator.tsx | 92 + .../private/NeedForCognitionCalculator.tsx | 39 + .../test/private/OEPSCalculator.tsx | 97 + .../test/private/PHQ9Calculator.tsx | 64 + .../test/private/PSS10Calculator.tsx | 86 + .../test/private/RIASECCalculator.tsx | 31 + .../test/private/SCL90Calculator.tsx | 70 + .../test/private/SDSCalculator.tsx | 47 + .../test/private/SchwartzCalculator.tsx | 112 + .../test/private/SelfControlCalculator.tsx | 36 + .../test/private/SelfEsteemCalculator.tsx | 24 + .../test/private/VIACalculator.tsx | 111 + .../test/private/WHO5Calculator.tsx | 32 + .../test/private/YBOCSCalculator.tsx | 77 + .../questionnaire/test/public/Navigation.tsx | 47 + .../questionnaire/test/public/ProgressBar.tsx | 16 + .../test/public/ProgressPanel.tsx | 94 + .../questionnaire/test/public/Question.tsx | 41 + components/ui/badge.tsx | 46 + components/ui/button.tsx | 59 + components/ui/card.tsx | 92 + components/ui/dropdown-menu.tsx | 257 + components/ui/input.tsx | 21 + components/ui/sonner.tsx | 22 + eslint.config.mjs | 22 + hooks/useQuestionnaire.ts | 6 + lib/result-storage.ts | 39 + lib/storage.ts | 78 + lib/utils.ts | 6 + locales/client.ts | 6 + locales/server.ts | 5 + locales/zh/app.ts | 21 + locales/zh/common.ts | 43 + locales/zh/component.ts | 65 + locales/zh/components.ts | 27 + locales/zh/index.ts | 11 + locales/zh/result/adhd.ts | 29 + locales/zh/result/bdi2.ts | 101 + locales/zh/result/dass21.ts | 86 + locales/zh/result/gad7.ts | 64 + locales/zh/result/gd.ts | 36 + locales/zh/result/isi.ts | 26 + locales/zh/result/npd.ts | 45 + locales/zh/result/ocd.ts | 13 + locales/zh/result/phq9.ts | 83 + locales/zh/result/pss10.ts | 90 + locales/zh/result/scl90.ts | 43 + locales/zh/result/sds.ts | 56 + middleware.ts | 18 + next.config.ts | 7 + package.json | 36 + pnpm-lock.yaml | 4563 +++++++++++++++++ pnpm-workspace.yaml | 5 + postcss.config.mjs | 5 + questionairies/adhd/zh.ts | 68 + questionairies/attachment/zh.ts | 75 + questionairies/bdi2/zh.ts | 123 + questionairies/bigfive/neo-data.ts | 473 ++ questionairies/bigfive/neo120-zh.ts | 56 + questionairies/bigfive/neo300-zh.ts | 52 + questionairies/bigfive/zh.ts | 102 + questionairies/career-anchors/zh.ts | 104 + questionairies/crt/zh.ts | 92 + questionairies/dark-triad/zh.ts | 86 + questionairies/dass21/zh.ts | 69 + questionairies/empathy/zh.ts | 88 + questionairies/fisher/zh.ts | 111 + questionairies/gad7/zh.ts | 56 + questionairies/gd/zh.ts | 85 + questionairies/grit/zh.ts | 72 + questionairies/hexaco/zh.ts | 122 + questionairies/isi/zh.ts | 90 + questionairies/maximizer/zh.ts | 83 + questionairies/need-for-cognition/zh.ts | 82 + questionairies/npd/zh.ts | 128 + questionairies/ocd/zh.ts | 126 + questionairies/oeps/zh.ts | 146 + questionairies/phq9/zh.ts | 61 + questionairies/pss10/zh.ts | 58 + questionairies/riasec/zh.ts | 110 + questionairies/schwartz/zh.ts | 96 + questionairies/scl90/zh.ts | 145 + questionairies/sds/zh.ts | 67 + questionairies/self-control/zh.ts | 77 + questionairies/self-esteem/zh.ts | 73 + questionairies/via/zh.ts | 113 + questionairies/who5/zh.ts | 58 + questionairies/zh.ts | 68 + tsconfig.json | 27 + types/index.ts | 72 + 160 files changed, 16974 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 app/[locale]/globals.css create mode 100644 app/[locale]/layout.tsx create mode 100644 app/[locale]/page.tsx create mode 100644 app/[locale]/questionnaire/[id]/details/page.tsx create mode 100644 app/[locale]/questionnaire/[id]/page.tsx create mode 100644 app/[locale]/questionnaire/[id]/result/page.tsx create mode 100644 app/[locale]/questionnaire/page.tsx create mode 100644 app/apple-icon.png create mode 100644 app/favicon.ico create mode 100644 app/icon0.svg create mode 100644 app/icon1.png create mode 100644 components/CategoryFilters.tsx create mode 100644 components/Navbar.tsx create mode 100644 components/questionnaire/List.tsx create mode 100644 components/questionnaire/QuestionnaireDetailsPage.tsx create mode 100644 components/questionnaire/result/analysis/ADHDResult.tsx create mode 100644 components/questionnaire/result/analysis/AttachmentResult.tsx create mode 100644 components/questionnaire/result/analysis/BDI2Result.tsx create mode 100644 components/questionnaire/result/analysis/BigFiveResult.tsx create mode 100644 components/questionnaire/result/analysis/CRTResult.tsx create mode 100644 components/questionnaire/result/analysis/CareerAnchorsResult.tsx create mode 100644 components/questionnaire/result/analysis/DASS21Result.tsx create mode 100644 components/questionnaire/result/analysis/DarkTriadResult.tsx create mode 100644 components/questionnaire/result/analysis/EmpathyResult.tsx create mode 100644 components/questionnaire/result/analysis/FisherResult.tsx create mode 100644 components/questionnaire/result/analysis/GAD7Result.tsx create mode 100644 components/questionnaire/result/analysis/GDResult.tsx create mode 100644 components/questionnaire/result/analysis/GritResult.tsx create mode 100644 components/questionnaire/result/analysis/HEXACOResult.tsx create mode 100644 components/questionnaire/result/analysis/ISIResult.tsx create mode 100644 components/questionnaire/result/analysis/MaximizerResult.tsx create mode 100644 components/questionnaire/result/analysis/NPDResult.tsx create mode 100644 components/questionnaire/result/analysis/NeedForCognitionResult.tsx create mode 100644 components/questionnaire/result/analysis/OCDResult.tsx create mode 100644 components/questionnaire/result/analysis/OEPSResult.tsx create mode 100644 components/questionnaire/result/analysis/PHQ9Result.tsx create mode 100644 components/questionnaire/result/analysis/PSS10Result.tsx create mode 100644 components/questionnaire/result/analysis/RIASECResult.tsx create mode 100644 components/questionnaire/result/analysis/ResultAnalysis.tsx create mode 100644 components/questionnaire/result/analysis/SCL90Result.tsx create mode 100644 components/questionnaire/result/analysis/SDSResult.tsx create mode 100644 components/questionnaire/result/analysis/SchwartzResult.tsx create mode 100644 components/questionnaire/result/analysis/SelfControlResult.tsx create mode 100644 components/questionnaire/result/analysis/SelfEsteemResult.tsx create mode 100644 components/questionnaire/result/analysis/VIAResult.tsx create mode 100644 components/questionnaire/result/analysis/WHO5Result.tsx create mode 100644 components/questionnaire/result/public/AnswerList.tsx create mode 100644 components/questionnaire/result/public/ResultContainer.tsx create mode 100644 components/questionnaire/test/QuestionnaireTest.tsx create mode 100644 components/questionnaire/test/private/ADHDCalculator.tsx create mode 100644 components/questionnaire/test/private/AttachmentCalculator.tsx create mode 100644 components/questionnaire/test/private/BDI2Calculator.tsx create mode 100644 components/questionnaire/test/private/BigFiveCalculator.tsx create mode 100644 components/questionnaire/test/private/CRTCalculator.tsx create mode 100644 components/questionnaire/test/private/CareerAnchorsCalculator.tsx create mode 100644 components/questionnaire/test/private/DASS21Calculator.tsx create mode 100644 components/questionnaire/test/private/DarkTriadCalculator.tsx create mode 100644 components/questionnaire/test/private/EmpathyCalculator.tsx create mode 100644 components/questionnaire/test/private/FisherCalculator.tsx create mode 100644 components/questionnaire/test/private/GAD7Calculator.tsx create mode 100644 components/questionnaire/test/private/GDCalculator.tsx create mode 100644 components/questionnaire/test/private/GritCalculator.tsx create mode 100644 components/questionnaire/test/private/HEXACOCalculator.tsx create mode 100644 components/questionnaire/test/private/ISICalculator.tsx create mode 100644 components/questionnaire/test/private/MaximizerCalculator.tsx create mode 100644 components/questionnaire/test/private/NPDCalculator.tsx create mode 100644 components/questionnaire/test/private/NeedForCognitionCalculator.tsx create mode 100644 components/questionnaire/test/private/OEPSCalculator.tsx create mode 100644 components/questionnaire/test/private/PHQ9Calculator.tsx create mode 100644 components/questionnaire/test/private/PSS10Calculator.tsx create mode 100644 components/questionnaire/test/private/RIASECCalculator.tsx create mode 100644 components/questionnaire/test/private/SCL90Calculator.tsx create mode 100644 components/questionnaire/test/private/SDSCalculator.tsx create mode 100644 components/questionnaire/test/private/SchwartzCalculator.tsx create mode 100644 components/questionnaire/test/private/SelfControlCalculator.tsx create mode 100644 components/questionnaire/test/private/SelfEsteemCalculator.tsx create mode 100644 components/questionnaire/test/private/VIACalculator.tsx create mode 100644 components/questionnaire/test/private/WHO5Calculator.tsx create mode 100644 components/questionnaire/test/private/YBOCSCalculator.tsx create mode 100644 components/questionnaire/test/public/Navigation.tsx create mode 100644 components/questionnaire/test/public/ProgressBar.tsx create mode 100644 components/questionnaire/test/public/ProgressPanel.tsx create mode 100644 components/questionnaire/test/public/Question.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/sonner.tsx create mode 100644 eslint.config.mjs create mode 100644 hooks/useQuestionnaire.ts create mode 100644 lib/result-storage.ts create mode 100644 lib/storage.ts create mode 100644 lib/utils.ts create mode 100644 locales/client.ts create mode 100644 locales/server.ts create mode 100644 locales/zh/app.ts create mode 100644 locales/zh/common.ts create mode 100644 locales/zh/component.ts create mode 100644 locales/zh/components.ts create mode 100644 locales/zh/index.ts create mode 100644 locales/zh/result/adhd.ts create mode 100644 locales/zh/result/bdi2.ts create mode 100644 locales/zh/result/dass21.ts create mode 100644 locales/zh/result/gad7.ts create mode 100644 locales/zh/result/gd.ts create mode 100644 locales/zh/result/isi.ts create mode 100644 locales/zh/result/npd.ts create mode 100644 locales/zh/result/ocd.ts create mode 100644 locales/zh/result/phq9.ts create mode 100644 locales/zh/result/pss10.ts create mode 100644 locales/zh/result/scl90.ts create mode 100644 locales/zh/result/sds.ts create mode 100644 middleware.ts create mode 100644 next.config.ts create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 postcss.config.mjs create mode 100644 questionairies/adhd/zh.ts create mode 100644 questionairies/attachment/zh.ts create mode 100644 questionairies/bdi2/zh.ts create mode 100644 questionairies/bigfive/neo-data.ts create mode 100644 questionairies/bigfive/neo120-zh.ts create mode 100644 questionairies/bigfive/neo300-zh.ts create mode 100644 questionairies/bigfive/zh.ts create mode 100644 questionairies/career-anchors/zh.ts create mode 100644 questionairies/crt/zh.ts create mode 100644 questionairies/dark-triad/zh.ts create mode 100644 questionairies/dass21/zh.ts create mode 100644 questionairies/empathy/zh.ts create mode 100644 questionairies/fisher/zh.ts create mode 100644 questionairies/gad7/zh.ts create mode 100644 questionairies/gd/zh.ts create mode 100644 questionairies/grit/zh.ts create mode 100644 questionairies/hexaco/zh.ts create mode 100644 questionairies/isi/zh.ts create mode 100644 questionairies/maximizer/zh.ts create mode 100644 questionairies/need-for-cognition/zh.ts create mode 100644 questionairies/npd/zh.ts create mode 100644 questionairies/ocd/zh.ts create mode 100644 questionairies/oeps/zh.ts create mode 100644 questionairies/phq9/zh.ts create mode 100644 questionairies/pss10/zh.ts create mode 100644 questionairies/riasec/zh.ts create mode 100644 questionairies/schwartz/zh.ts create mode 100644 questionairies/scl90/zh.ts create mode 100644 questionairies/sds/zh.ts create mode 100644 questionairies/self-control/zh.ts create mode 100644 questionairies/self-esteem/zh.ts create mode 100644 questionairies/via/zh.ts create mode 100644 questionairies/who5/zh.ts create mode 100644 questionairies/zh.ts create mode 100644 tsconfig.json create mode 100644 types/index.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4beda59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem +story-personality.zip + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# python cache +__pycache__/ +*.py[cod] + +# env files (can opt-in for committing if needed) +.env* + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..f912847 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x86", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "C:/MinGW/bin/gcc.exe", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x86", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..03547d2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "c:/Users/26931/Desktop/lx-scale/app", + "program": "c:/Users/26931/Desktop/lx-scale/app/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c9e66f1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ded6a62 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# 心理量表测试 + +中文心理量表自测项目。所有答题、计分和结果展示都在本地浏览器或当前 Next.js 应用内完成,不依赖 AI 分析、登录系统或数据库。 + +## 功能 + +- 浏览人格、认知、情绪、睡眠、职业和心理健康相关量表 +- 在线答题并查看本地计算结果 +- 本地保存未完成草稿 +- 复制或下载完整测评记录 +- 仅提供中文界面 + +## 本地运行 + +```bash +pnpm install +pnpm dev +``` + +打开 http://localhost:3000 查看项目。 + +## 生产部署 + +```bash +pnpm build +pnpm start +``` + +## 说明 + +测评结果仅供参考,不构成医疗诊断。如有持续或严重困扰,请咨询专业医生或心理健康专业人员。 diff --git a/app/[locale]/globals.css b/app/[locale]/globals.css new file mode 100644 index 0000000..dc98be7 --- /dev/null +++ b/app/[locale]/globals.css @@ -0,0 +1,122 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 0000000..64b11a8 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,35 @@ +import { Geist, Geist_Mono } from 'next/font/google'; +import './globals.css'; +import { Navbar } from '@/components/Navbar'; +import { I18nProviderClient } from '@/locales/client'; +import { Toaster } from '@/components/ui/sonner'; + +const geistSans = Geist({ + variable: '--font-geist-sans', + subsets: ['latin'], +}); + +const geistMono = Geist_Mono({ + variable: '--font-geist-mono', + subsets: ['latin'], +}); + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + +
{children}
+
+ + + + ); +} diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx new file mode 100644 index 0000000..7f59049 --- /dev/null +++ b/app/[locale]/page.tsx @@ -0,0 +1,38 @@ +import Link from 'next/link'; +import { ArrowRight, ClipboardList } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +export default function Home() { + return ( +
+
+
+
+
+ +
+

+ 心理量表测试 +

+

+ 选择一个量表,完成答题后查看结果。数据只在当前浏览器中处理,结果仅供参考,不构成医疗诊断。 +

+
+ + + +
+
人格、认知、情绪状态
+
自动计算结果
+
可复制完整记录
+
+
+
+
+ ); +} diff --git a/app/[locale]/questionnaire/[id]/details/page.tsx b/app/[locale]/questionnaire/[id]/details/page.tsx new file mode 100644 index 0000000..e85b5a7 --- /dev/null +++ b/app/[locale]/questionnaire/[id]/details/page.tsx @@ -0,0 +1,35 @@ +import { notFound } from 'next/navigation'; +import QuestionnaireDetailsPage from '@/components/questionnaire/QuestionnaireDetailsPage'; +import { questionnairesZh } from '@/questionairies/zh'; +import { Questionnaire as QuestionnaireType } from '@/types'; + +interface PageProps { + params: Promise<{ id: string }>; +} + +export default async function QuestionnaireDetailPage({ params }: PageProps) { + const { id } = await params; + const questionnaire = questionnairesZh.find((q) => q.id === id); + + if (!questionnaire) { + return notFound(); + } + + const cleanQuestionnaire = { + id: questionnaire.id, + title: questionnaire.title, + description: questionnaire.description, + category: questionnaire.category, + tags: questionnaire.tags, + time: questionnaire.time, + evaluation: questionnaire.evaluation, + details: questionnaire.details, + questions: questionnaire.questions, + }; + + return ( + + ); +} diff --git a/app/[locale]/questionnaire/[id]/page.tsx b/app/[locale]/questionnaire/[id]/page.tsx new file mode 100644 index 0000000..d97887a --- /dev/null +++ b/app/[locale]/questionnaire/[id]/page.tsx @@ -0,0 +1,24 @@ +'use client'; +import { notFound } from 'next/navigation'; +import { use } from 'react'; +import { Questionnaire } from '@/components/questionnaire/test/QuestionnaireTest'; +import { useQuestionnaire } from '@/hooks/useQuestionnaire'; +import { Questionnaire as QuestionnaireType } from '@/types'; + +export default function QuestionnairePage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = use(params); + + // Get the questionnaire with specified id from questionnaire data + const questionnaire = useQuestionnaire(id); + + // If data not found, show 404 page + if (!questionnaire) { + return notFound(); + } + + return ; +} diff --git a/app/[locale]/questionnaire/[id]/result/page.tsx b/app/[locale]/questionnaire/[id]/result/page.tsx new file mode 100644 index 0000000..5064413 --- /dev/null +++ b/app/[locale]/questionnaire/[id]/result/page.tsx @@ -0,0 +1,98 @@ +'use client'; + +import { notFound } from 'next/navigation'; +import { useEffect, useState, useMemo, use } from 'react'; +import { Button } from '@/components/ui/button'; +import { Questionnaire } from '@/types'; +import Link from 'next/link'; +import { ResultContainer } from '@/components/questionnaire/result/public/ResultContainer'; +import { AnswerList } from '@/components/questionnaire/result/public/AnswerList'; +import { ResultAnalysis } from '@/components/questionnaire/result/analysis/ResultAnalysis'; +import { useQuestionnaire } from '@/hooks/useQuestionnaire'; +import { useScopedI18n } from '@/locales/client'; +import { loadResult } from '@/lib/result-storage'; + +export default function QuestionnaireResultPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = use(params); + const [loading, setLoading] = useState(true); + const [decodedAnswers, setDecodedAnswers] = useState(null); + const t = useScopedI18n('app.questionnaire.result'); + + // Get the questionnaire with specified id from questionnaire data + const questionnaire = useQuestionnaire(id) as Questionnaire; + + // Load results from tab-local storage. Answers are intentionally not kept in the URL. + useEffect(() => { + if (!questionnaire || !questionnaire.details) { + return; + } + + setDecodedAnswers(loadResult(id)); + setLoading(false); + }, [id, questionnaire]); + + // Construct question-option text pairs for copying result data + const questionnaireResults: Record = useMemo(() => { + if (!questionnaire || !decodedAnswers) return {}; + const obj: Record = {}; + questionnaire.questions.forEach((q, idx) => { + const val = decodedAnswers[idx]; + if (val === undefined) return; + const option = questionnaire.renderOptions(q.id).find( + (o) => String(o.value) === String(val) + ); + obj[q.content] = option ? option.content : String(val); + }); + return obj; + }, [decodedAnswers, questionnaire]); + + // If data not found, show 404 page + if (!questionnaire || !questionnaire.details) { + return notFound(); + } + + if (loading) { + return ( +
+
+
+ ); + } + + if (!decodedAnswers || decodedAnswers.length !== questionnaire.questions.length) { + return ( +
+
+

+ {questionnaire.title} - {t('resultNotFoundTitle')} +

+

{t('resultNotFoundDesc')}

+ +
+
+ ); + } + + return ( + + + + + ); +} diff --git a/app/[locale]/questionnaire/page.tsx b/app/[locale]/questionnaire/page.tsx new file mode 100644 index 0000000..9b67f22 --- /dev/null +++ b/app/[locale]/questionnaire/page.tsx @@ -0,0 +1,5 @@ +import QuestionnaireList from '@/components/questionnaire/List'; + +export default function QuestionnairePage() { + return ; +} diff --git a/app/apple-icon.png b/app/apple-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe3a84b042b47a710541c1af0ee6021e01b58a98 GIT binary patch literal 16566 zcmdU1V{>Rhla6iMwr!g?wtZvUwr$(CZQIF>?PRkb-u(qzTeTm~nVPDp>2qefyL!5x zXTlZa#9^VZpa1{>U?n9)lz!W$|9&9Ae&4*)U`)Rapreww5J1fg&N%=80f3~4;2$^L zD{pWwmC@{{c6s@3;vj@DvW~NE_P~BWMtyBNoR>8@dc6rQmyAY9Hs>o6m#$6=7i%Nz z#VEje#%{>@P7zdDpr;zO9+_OvUC#mxk~^o}YkIT=@0XqQpLFI^J=2j}k8a&H{7&$I zB5)Y&IYC3oPRu57Yk^Gxhzh(qK-@nkP1r`j8zTY=Pkyjxc}%6-7}#@cU;!1sW5AO+ zuSVAVS{M=j@ah9|l}%Z&kAO8r`{(@w{GG`}u9J~1&jtoW1%AyBj>>T(x*pKRh@i}) z56np+eerJ;tT{HY;4&~{R17RQ7$M)aC9nt^cs>Ba z-lGgb{38j*j#Oiw~6ktgHa(j`aOIEM{(GV9PFUuTLLO??0c#*$QaRnn7rah!)c{ z#0haWkF@ym$T#uY!foXSJWCI-Qn%m3c+Tkzf)*WaQ&k)noZ1l9TfP9bpvA4jS;ARp z;4PqqS?U@FQ>KnL$T!Eu51bjNWyrS@kD5J;3A?@J-s$0f^6NyJqie;^jLjYRvDpfD zT#ZmXwO_PYxp-ojOQ1|`L}lzq?3r^d*m^sN7@N@Lwes6Io$&y9d-xVP_*>E>{0 zHzXpu?a~?N+WqHGRIhgnv{meJ-pW-#qy$MUy6$%bpO62@OjPM|=zW6#%TFRu3lRHT z_99|HQ;>NV!(R?EV<+*lMfc53*HT+IHNXss$iq~q+np9`>uJtFO;_A2d{g}qO`O?V zB*b&jfh;+WY`Gu~Tsw{=d(gdcR%3>?ZM>!BcEwW%NVvm2vc@LB!Tq?OXE*<%Fa{oW z7m5)RC*jJrwOLwG3G92Q$a=3`#i?Cy1e0ZzCRo)IRKL7lc9@{xhG$M;hz*So40}hY z7oflJ7WfvKnJw{7QE7Q3MpnR_Lw5S+p)GDMmkf{QnE_>;Vf8mq0?2*YbQD7sz@v4> zHwGxGs+NnD;=oM6&ek#{2}p-=2>8)OL+T_WqjS5WqT&QZJSc>OgiB>ZoK@A%g$)&8 zs6!Ss)qTwcOi4+qd39|#qNK+t5J~eS%F^*d*~5Sq)pvVwJs6v`xbdy#ZU<*JTs~OP zE?0y5k!opG2(gHl(q;q%OXmWSLmW*KVS-*b7J4`bhXn}8xZ!7Kb`urGdkOIFFBz8T zRFxHqn!4cy2M*fQRO0rMpfza81x;N6Gcr;MWi9dSVX;wtk9^y9zCXHWDH#C>d}TK* zlRh~g?Ct`q9olOi_b@xVJ?P*Mbp2QMj*$8DjM=KV=DQ`N9WEAuNvh#tv9V&>+9<3w z4k;wPl;m?Pyvn(dsHn>*OQNF39G=%uF~_=jd8LKHx6$XymRhhx4UtoAjF1A79jai~8YB`u??IsevXCpb|?+u>RfplsAf ze@*x6#fT0?Wko>hnFJYKP^ftSVz~~~Nc357(Gf^c2y+OMPwwM;czqH1aU|!p-rZY9 z;lQD&jt;Iit22k)8I8*ocX-1D6{w|LA;@9SQ)em3K7Ywh;Lg>CHu_SL4Yz#u@C_6D zub(1v21MvyJV2+Zn1 z(6%L&RlW16qA@;SaK;7~CKL)B>6`7JGT>fsW3G^*QsO1aqUf8@DK0B`+|f$u)%+C| za&QV)i}TLl&=5oYT&F0!9RJ%gyT(fLEHxji_l+i<@0<~=VIra)bO?83C}8(f=sGVQ zv|s&F?6$e}IdILpVPNZ~^@Nz&upZ(JMvq6_KU*L~Eev@U zxLBbT2yOTS&$LtKL#~(bBMZX$8KCu%fht>&t4(KoGb{SsuYIIP9Xxtrz?J!qNQ1}H zA>2?kYV4zEBW~PH5~GzSgRt<}XMP2Dv$Au&=0r9P3aAa46vQSHm@lpYm?x-rkK85I)q}Hm zX7v^W`e=yoG#+;}z^6I5^4IOH^{^QIXLG1rGGOxsgB$C~0jo;3z;-8?hNg$C{5D%_ z{>PhLP+hzPCRaRvYDQu+(s;=^Cst6-Z~}9*M5TcY_r2H>Cb6!FFaQXf!CX_fZU{%W zdZ*#P#<-lYv7t+f5U0z2qgH!&{o9JV7k%U(@DCjIU<8Byg58UZR{s!|pkk*&wVcZd z8-fa&tH{+GmjIG#ZJwY-GXEe9ky}-~RPR6S4b4Bi8tb4{R9YS9j_H$cb9Wd~>3aS{ z4B~9YEJlpqCIOI5bD!QnbaUUQ`gg^gVw4gV1u@*V@6Ax>Ylh*IO*6B|l;5X3@DJ_M zXKN+E$vI1jVoc9&S8}!B)65DtGz!xVP>t3eBi2Cc*?wsyA_(3SGT!BMYc}7EfQAO8 z5X@zCY#-AuBs7DFxrDJqq$>*$#FT_i^rH!e(xNGWm}tMikDi>hS2%!?w-~AxT|72t zFhll3K}ppjg)szpHu@~~^ae9<>~^FU)B?$G$MqWM`ysKJLq(x4dKN)sKva3;dDIV3 z2jKwb?&;Cl`?4^4o%d)hu8J^%;B%;r#T3yaeLGN8d?>2_dnBI(JQCeFVyF9*WW|PC zBAfCV$od)K-11K6e%Dj{Wq3|-8xc#;FE5sW4?$!q6CO$ypqzaftRZX;V!iPGCo9P0 zNbK~!k4uZ(9vtwg{&!`c=(zMr0Wkz3%OpPcdhFA-TG6X-t)}D)f!{UVL3Op7A*tc< zfXn9*I?~K%C=0<2$M)!G68ZOWwk~BmtS;)z^yv6>Qb7~c6ZaDG*!CRUZ^xGQ&o*0;g$5qWH6_UN3D<=<#hW#}&)5kmucm;-?;hk=@=9VvSTL9o8Q?G1F_}wXL&TR96iUTGv{(nB>ZDds z@Dk8mP*qVKtID+IaA?n&HT!0HJdb@c4!S*9M3AfD1|zdOV~oZ#wyS)C0@JmX)K2>o znZ*m`d!6F^(k%W=U=Y zuDHx$xZ0_`*7HEoVwx+U0Xj~{g@BJ89Zu|#ub1nTp!r9|&OrzfRFsCOkX6I+g+sxK zm%x5_ch*cWz|aG6J}xlHW4uCL@)dhx{1Mbt#hp~OYCVvc(_PaS;0{sl^DA9^zWVBk zx!4uzIvVP%*Jdk*ebpxMI2(jUQ4ukX%>cpU(R$RWBB_=tWYlJ_RlcfaiM8q4{9KOE zgg4sy2x5+hPKAL)pwML3q)cX2plJ%WbXHqzrrk@EWr>y-)n>CLi8)@WR*7A~l=a+) z+U`kJI%V0Zj!v*?6N-GZV(D0a`GLKQ{xcvWlkmoi53P19d?10TrZm0!T2g-SCf5xj zKN{u9oJHVNhkEGH_X~g&RfT9xtg}gP7A}p;f!xH{wM3mqzR#{y^D!_3E?JLYOjyiqh?ob@ddCGK_lkokvfNyj)o&F)J;L7GW0w-C z=A|bZ$lNe&w^lv$#pUvlezZhHN#G~@^jiFnP?7`Cu(liv=`6~bfvI3sQ?9JN5np^n zTxz)gGK(~5kOa-`YyEh4LK=f{bhEyrY>4H}Bwo>2;_j}te6cCWn+&y`#(@NkKu#;V zg-mhr*+;nIVP2>IV`a7 zCdoeitQR0+bex?9ia>6!Y8d*iMi{gxsY47|9~L_Y@ZsnSXt=Y2!Z}_V02w@q6<-<{ z+{0e6X8M4%>|s`3J1plVOYa)FoNRSz0U23IYOJybr+d5Ppt^);Ts@l0QepyG!Re}$ z4K*t;eG}5Vn&Zr42MS`Ssa)aBKUCh$iFfT-HLDxiRNL}h&OOnY`Uf}HbJdB30tsqL zCZyghXZn%HfAf1Z+k1Xw&#!jkY~M|$&^kZj7rv6R_D&={Z58lVM_m(|dTP93J!g}y4XTPJSS{I$7okkw(5a^FgD>FKigz$NCvF}PPxRg4si zYX^rib>%o6OqrBq0;*}GyRtBB$~OKP`)3$PifE|Y#?!4^+*<(OFWan5C7N^Kl998x z3+TJq9`HJhOL4hi;7%4QtUhn&SXDf3SyuE3c+Uqr^Ir@O#vw=8l6F{YPHK9Qed!<4 zk*Z}#ppF^p3LTHFBHF6Z!HnXHv_^)E`nwQNnLrX{GLRn6#EA$=%`9T3KTE16R)yj- zcmXHwRXW2v)tK_6F~(B62Wqb{Lf<_snuU`b-Dn~K>OpWSHenPB3CyTn96pykeA*q! zDkt;(Hu?gce?AZT%h|j4_8$Uz`vJXQ2TVTQ_MtkYwA8t{sF37&Sl}cnA-jH5mi&u# zHJFR9oZHJ8%%qkh&Q(S$%J>;h+nrlX*%Mg_6QbMf=8byEu+TN|UFh?5NYf}Jt@ie4 z5dl(SDl9@yXDZ!zCm+<|%Okkbbx#AuQB)Pw77}WL+5WD}5Pw3`c3RA_c2se8 z(Q@5NMNZ!BC(yRJ9cSQl+3y>?E@xS^m8!kWM$Tux`vd}IJUX9z7ru@c$aG|+i}TM{ zYI(|ZyAlu;5Un>A7bjyXQdvzYq9NUJRFQ!IyV+kVc$plefhFU@LYXVEy`6N>zMiIR8#`lP`GgXKD38GR?fL7p z^E5&UcN+n#FN*AbQ_%Jiq+Sla#bBw`ZZ2M%EC6pQHd!i68WmYLn|?j^7mY)Q&~{tS zv7}rKvixLX=yc$jz93tUe?}-drGxeK+yEN51uHevi2ZY!;TMm|9$ZlAR8-L+CEg%j z+}wwcofhjdi64|zyq4A7%JZWuuNrXW*OppcfP^6%8rB~<%0F8wZ=Q){5-G{T1mvo& zN#7MoQ#2PsWJ$Ew)CtwP`AI#<+%53kOda=>*AJ2BZ@S5jcf7O2~;83CI6cd+%VSAqI?;Z;QJ9{`3jt}bp~fsT+)E{S^5MRYD)i~fLS zGf(eL*=zTO`!t;#ANa z(3rxOo{_FSlCA<007`-a(w-0pr3{c$IF8aaXK%bb%SE>AXE@8~Ew8|iA9nz?!ZRtP z1Bas%G5K;xKZC={N32f7bmGH^Zk(Q`XPS)?hk7rgL$z(Yuh0;|PfJB~OS>tVU!tgXx`E*Yz}sjWQ?wf-?kn9ikF2?#qUhhHf5T{lym)f`L+#Nk9HtPJ%GT0OOQ=Xwuru866oXA@@VDeX;j zMcjNVfI3;R)^?8jYF#)S@M;w)Q&yrC^t^iL4@i^{H6?nccq6}GRf*H@4{TOv{3O?= zO_xqhdUuvPljCs#(LHXaEjE~gN3nM$ozde z9{Lw4rPaqh5a@k*n3|E_suzT%T)@+K#N6pRfy$eJmDb8%o|l>M7^F3M3=&$9P(yYJ zjbV{uRE(ta&%ZU1HPw>FmAJD?z95{10^Et@1`N^4Mzly5ZbrNn)WR7R@~`}F z+=bizNWCuSpq(T3-#y&Tet}fQ{5)X2|UlcjV7skIcc_d3c5`4Fk6|s5GSE?x|+qd)rq5K zj%jESou#|;{J_}iF!%G*NW|5Yr`{FXtyb~$gBNt)&l|qy@0a zGqZ$3^FBQPkEQy2<3H4YEor2`lWuByf2CW5$r2#KO?Hu|r`d-mL1icp(bZR7?tvx3 zWu+lKp){4jAcHMfo9Ad^ZeLv1JT_AaKRrR9u05K*CZiYd4E%{cg%+qkl7W$&*HeP4 zB&_<)%J(8G$)<8Wf@!awff7q0eXO*qi&vD?j=#b)!+f5#;tyPrVP)`ESxHgTe62LL z#>{1Yn*MIK83-VZ7&0`BIEuE6#=H{{r0JZg?=yqsI@6cGnW>I%o&d~ zWSKs7ySC&|SUTe)rMemy+J|(_^2d(ta!y-cYmt`sG7wfJ48nn=gmt@nxSJMb`Veh8 zu-wNm$?7W=ny$uh&LHA!wt&^8SP#~ZpadM^F(k9lZQ||>FejJrR;5*?vDf?6>HjF) zo3BsMRlC_4iUg^681k;03zvy`IDrMFD8WP-(GlszL{rKG5MlY-Yc5M@LWS6jjF892 znY#CRZ?aNwceye((o2sb0xw10Ujwm<4;{C=lHkuyU%js{ai`tC^BdRwQ*$!H_=np1 z1T0J8^t>+gn$0c72~|vS&pl~FO;<@?BCQSODuZJk`gqDFNE*~s2QN$}0AK2=*HT3g zbF~L?BIWe61)wjMLsVhXibg=i5*pHGGJNBa!k&)xZaQo{w*&!?hg;ziQj$yVs$>sS zfvegQCeitmUXeKA8=Y_QH~}OQo!ZOJuTPoM=1Ic0-ZyOJ>aS0wHQZ!>S3zMX=J>US z3UPRd=HkinYUsPtdIZycjdMa+uFQQ2F3Fborj(g<)@L?UY+tP^{SYY0;01Gp58La>h z?y=%%%8e_rStT=juwQVz1kydIJy|53Msx{`H#2qJxsXLW#KGIog1cHry3_O6ciwtz zf3Q&G?P_;o{$`%w8m4l+aApu>av3my!JJa)D{jb3<1s}u<$Zg9$a0k-S&a>0eQ;lN(Oc?u>u(Kc_ zBl@fLFo)XoR{DO{HAt$y85uQG2d0P^phNxmq2JnvroFLfXpq-`HuBoFFKO|xS_A0j zupYbYKXtrVC_ z$0~J0@Dim>sVS{&TD*Tiz~(h}p5G9C=cCS8gi*!u!s3wQo-Pvsu4mk(v=Dj)xJv<~E6F;Wg7NlA&%Gs2t_I z9zRFoFWsU{$wASEZ8$0S`n zS!s*(a^XLpUY^ACDD~%0{9s~Q;}#hu;I5Ys*jWSQbh#=?zRNW0afvb3<`DTKeiZyr zSNU@Bq-4x&$q`p8`pwL)U*aj<9d6V`>#ES?ziB=C{D~ah&)PRH6~bD&3VRVQe7_( zDEj+`8RYNXSXuL)3tDFlQlojFrUWn2T7T-)8iW0Yt2S+QnqnhMYZkuG31kV$uwFU@ z+h0S6I{U|k{@rHEyt9)5+VTW-7LiF_SY?G^o55v5vWdT`J`h49gqYSnJX%#qTbV2 z@1%SyzQDj34oCe53m^*VheKb&ucg=6RtIxK{|ciPv8KU%BzNTXs5hG)qPYD>qGjj& zla>MJiEN6e@LNJ_~kk3qAK5 zQiS6~Lj@Xl;`DuUQMj|)i}Q;XHV6X@?vgv$ow`LLgluz)&kcVQUGqo1SU_Oqs8P*C z67~C0j_kLXEi5fS%|8RBybYaFwz|r{(h6xx&=tSK>2*fK_v*yV-=Bi7GG7er!)S)3 zgss4@EWtAJJ@?>p+3#Ds6E%V5LW1Qlq@)c~eIYVHKQp9L4=4Bb%fo*2>ydyW54=}xngoPBKp&U}6w>$F=#aQWE&RFFh2a!=lS2=?J z(>?%m_80f=EU>UFXfbxCZT{<;NyNff4^A~Cbur*OUFURfaKjsr2K&HJMnMI*gwUP}d- zmMe~^+zu+qh{%G<+5Y@xopdE{?I{J+KjH+VAD@}IPu6jbb&$EQ4@7~=%U~MQgnTAu zm|xe{=4zwDs{3)_kfieGmYroTwQAbSt_h?`&j$bO`0I0L>2EaP*$!@(@A!_(x~F5E z`7#}x3DXgdM0n(g9$s$v&J{MDb}3?F^Uj5fo&MJgLH5^11?=lZiH_0);t@ii@6Ti- z#c!TqvMG1s`h1ud&>8e6a02UI&yG41b0c!g2>tFkA)2LKI4Ws{3Q^Dd+6PJVDcbOs zK9jAEu^YlY3|MZk@7ueS(RP>&bA$+55;6}Q=FHL|=kZ)(WUV+j2&52q;Gf_>p4Tfa zX^gz{GRHi6Nrh=lVpf(z$X!jidJEzw8J5uX8w-&q&RJc09#-eq<;=))_k;$VK0AGv z-I~{7VrJ|mOzS@aiJ^Lwbp2JmU4aJoL6I4y3rFqaugNwmd;t^qlf@Q>PyO$jz>Pdm!;RBolZ05!DVK zpboXab~oJjO(J4}8Kaw5mj}_=C|^+57JwJI)$Pfh5`bK3%#kPD)mQu7g<*nf%?-ii z_wHg7+U_`#ktK8o5tK17Xe(0eq+7kzgxW{qg(`pZH&HT+06co$eZ6T;r7NNB44D$ZgL^>WP}9II+=0`AZ-AHJ>} zVz}o=w;^Tu7qu1q$IBPj;ht{s#cs9z?lBN~u2Idi`7%-u%ar3blAm_(gNa$-<$4P) zmX~LxiL*?_CC(Qd!xymslp9RFUiI%aXh)#0WV?gRxc1M`9N+8wrA4%hu|pwX#^YXd zgM*jR4$&pI=Qv4XXOnHT#$w2VvWoX=l{%5cMgpM)s*M5lXgvN^-`MItD4%*AWGF;l zdpl%a?@x02P$RUy`69)9jp8b@fZ5IT@F(h~xicZ}25eqJptYwdFBtLA#jx}bD?G!Q zqR`_8Ty?_jL8EeUAQc`8;=xd5?W{QEws{s{hnfdg&uy43PKVF*>_EPh6Ie*6Pi{m4 z6wS<5c%qp^hE`m>f6X}U?MkBKT#boRVI6aE)JT}aSb1Dlfb$uJPnR2M#TjKWOm@-3 z2<&+{E*_o#F>5 zBSB(fo=`NxQaf+baQY)dEfV3RQ~2N`qij{(2g2S|1`|jixXa;89TvcmUF*+l?Tdya zfUUk%B#75T@W(i?>;n0}T~E57%jQ@`C0#ne?M7uSue

YiL%}U^rM6V8PM@8X zW)4g@lJEwDRRfJaIWQ~rFkt>3U!oHj+_}AY*zLS%DD!(FQPkwu+T)8lFMiI-Kv$aj zMsT--{`#}6V%S8yjFe(Eol=7Bp((((fY+nGQRo&!J-Bt-SYCcQt_WaFTp~$u=(onn z;9_bQgUxT8p<$7|eGB5bYH=+3Bf?!W;h?S6z8r7zdIHB#?Jj`<|DgO(1c62D3c)@A z-q_UP#^eo3$?(^chefv!4dO9s5pLnTe^w8Q+Nv|nZNN}-8@Df|p-(6g81g<@{Kyh+ zo07WhFGHYR-EPd@^5@#va5C5FG5E7t0Y`0ZVn(N8X6EzvRm`V%hr{U{!UJv#kR(=c z!ndze^Jh!#u)K$v{sQ&kdfe*fo2i~ty@a<~ZRXSs@@Fu(!8hLSWH98B;gyo891eRl zJ9<6A=xTuZ47ZV~c;LJ-x(|;MB7qE#0aI6&rgv_4eM$KkTw%;jX)ypn9{xYC(>xj0 zNPWeA-$;N>>G_4(2CwT@_yuiQILcESi-ks5Icbv^n(z>@^+KwEwX5yr6=a`zznm4T*c%ew*UV zm~@sdc)I)V{@h9~#ZijB*IM5)jti=u)md%R!Q-VdfeeDZ>@7%P>;iC}YcaBP_9Uau z;n<H$dcI#Qd+hjQz^kW| z)BQriSCSA|960WS%1jR5Uq{IM)@%y*rX3N8#?NRe?dZd_d@cMS~ zpeO=3jWbhorF1uSh`e8q7ib{3_JpBz4@4hC_~m$Q&4*^(zzn$@8hq2kW`Loa#Q=tw z|Bg5t4Sw-kMDJgsr&SL@1&^uNz)X?K!+oA2ejzB9g~s@7Gv4MPd$UL0h%?_d$-N$E z86?-A+du-Y-=btGQ!aC&3)lX<+GZV_?i_H7U#j!;=kNMtsUXR7-|7cwXplGDm&(uo z4Y0w<;QQ^M%paC6Z{6;g;&GJr0ICy_ZlmcD1s<&21Br!&?jHEPqmUXTQclmfnk|A) zb2F?rwAb*fMEJ)`Kb#6W0!Z9zbSZ~^eCqI2SXna)44LPqe75f@LrvM<{hvIzGg0!! zBaczh4p*sHj1KR#-32<%BufYVITyw9tX5m>5`eGXaBBOkS8#m_2{z~t@436tA2h+S~cM{dp+5<`M z&#J?pNg^>r;LizT1(5ys5n9pdf1ZKe#w!G_pAz0JCgw=m3%rT4L)ZuDYq!d|9e+zo zNE>RAvn0zW0<)aAAg4auW&Zjx-WM9saJe=`#Qiju3i|iQa|p9E^*4K?n6TO@Q}Wyz z-A?q2#<4eoDf4r?Zk(QDCEBVyFFAW^A>_w5CII~hq~;MA`-Z-Nq&0$QKSd%Vp8&bt65nS zGagG^Q&N4he2~v3JjD%Iyj1K^xLmQ5k*_iY>dEGS>V4eB+rba=eDTaAG3h*z-?Ka1 z914jpy+bX^-Z5!9hut;Oa^8TQh|iEnkkS2`&)H%rGE4xIvZea|#OWiAGJPF4(s=!M zv(VWzuWIp1{qjfb5c@I%Pg9w9-t>8TLePuuC(%Ix4Rl7W*%;WTXCfHO zdFa+B(C1O&@|y285f2G-DJ=j5BaWNXJA~8u3R+AY!muHGg42Gs42H+%GqHLPqgDeQ zr$(NQ1CgPT{G1>P zVsLO#oM`Ag2ytLW)|F#T5ix07G9#BM&19FGwozT_Ps|J7HWWOkQb1sCQ|uqXXRB2# z{#;(vZOv9o0k@sc!VI1~NtY}A9nw#qC$URTZ#gp6p(;#Nw2%)n7=hZTX9!4Am9ZuU zCFq;g-h&|RUaM0^nr#8JYo@i=6EKcR=7>Y0r3UuWEoH%1r;;yw(nUz*^Nkd)cs_A6 zN&ID{t^$@iNvK4QR`E>%rLIyp*t_c^3IrgB(7>P6N7P8uU)&omNoXu_b2U(t8 zC`+$zulw3|yDf{mKw=590)Dp}DF3%HYs`_(5)p{C4o*5yc**s0C2)2QDFf>(BgOk{ zfyoVL%y6ZvRlNZid$(ci)}LiVt9U>QY@Lm>_cas>#Gz3$WK<#}13hQGiyh39C4O{L zZGJ7rF}5B=&u9>bv}Uu1{Ss(4dj*9Xw(-}us`4=j>0+}3mZw=AKy$v2eJM%Cp2u2I z4tLJnn)O7IqbDMorSMVHsFVy%G?eu6xJ}{D1dPo6{w~0qZU>dWPe>J z+w`zrUS@R4@H<($48*dZ8!UlHu(-2Pcr=rPwc*Kz7|qXUYuwwV!Nd3!K2SKUPE=_pualbFkTG#?$VchZg#@1Zjfx`4;Y8^f8t`}6|C=%Z7ZVq?jvvzEhZWo$7 z8S0cj$ZFH83$JQ`S(+n91G`V}Z~rwcbJ(9_MUyJGIZTlai5dP2@;_T(jh2Ggr}!8B z0(mRBeI9FG{Z9)9PPQLHv()#Xi_U~<_1a5X|4L<^Ro)1O+f=nS+IY{w1LhTzwc&!f?=~s3x){ADy=SmEm@6QSI(WnmrX6C zBSfg)vY3pOA%BAs;A9fkR++{=bj+M#*(iQwR z?B=;ohLReOdKhsxEh!JRWwT?L)vig> z(vT>FdkxdCmYWgMHe`<1tD2E0H>`~%p|m{Q7>kFt&(BLz=4n15nOuSlQ*o6h^)pgW z0HOUG9w;8E0Ax71-o5-efr~?qkQ2Uh3BErPC=e zlyP30_7~MPDW8cIUc!cYafL~Q5akC}Q>6lC_;vz3*|u-9WJhuvmUP$s)p%#p2<-1u0D;=f`H3e zCRmN8e?_#lscTmtxQRY97!|{$O3fJgDVw3{PynaA-#M1g?=OfzhQkSSq|1Bj2{n}4 z;^^(75&enI@Itev9b>P;P>DC<2D!j1Roar!gFRj83lkWmJAVaD825; z>aJc?0F!F)NLW(6jgk-zUOFQZWwlnCCI6>ON(f$wFK84d9z`8#*b<_xgUhM;vOIFi zP&H~NW%cyV02UF7hh7d>g>gfaD75B^2{gRd9Cr#z%Ko7V_nE5B`#qh5taTc)Ee{`d z-~x9*(h1lgxj6uBls(lCRgSwSAC!beu?SwI_-Ih*AvgV$AMsXsat z`K7S3Y{INl4RHE*Rkhga?Kyb>Ets6&Qg=>1d+5xYMwam|C;iUrDU~)IjS_HHuYtBY z#rNGM@!7Hj_P|zoKeCe_Sq^V`;rDTbGl#O>bW>SMUWj$i*H9sHTo#V6cL|!cWj_D- z8c!Y3nhL;5EPpvhvetsgJ-fb6?`oz`xr-cWFC8t_VArsgU435KRX3p?+!COd$-79@ z#rjoEv?<8HhCE;v3knUV&hsP6Pt#+MtkZe;W{*sinbE`VapAcZ8);K>Uk^=04{$_mKhjHfI;Uw2F%XyKY9eI@s7u>lD%xUj^=Qj*^HP}Hb za7DOfc4hKL<_7GXV=QK~_@*+*;JqzPWhl~WX8TUMe3Nn$+U5RsrT)9WIaT{#Okz0d zhN~eC2yI?D%&mtY%hOh`w?HZqq`0C)P>Ma{>xAPXK*LW6OnQ7J$%`2o zq7v_1$UYHQ!OgT;w8N70eTMEYwC_?UZLxBIJY+Mtm4C~RIkje-HJ0c^)`ivcqkVL) zS9n85+1!cR58hHB5^mk=0L7=n36?H3Nr{$vfjX#LlYD4DGFDDNM!QI?2GFoRWpP)8 zX_`b3h;UIp>$-#BnYKKIO|@Sb&9#QAb|66Bb?@j+x(Z1Vh6C6f6W2erRDR%yk2g?` zSxa!220&I-tCrURs8_EMG&LeZ_7n#x;VNzcX^|XWtTG8C@!@C?!G|rRN*?tUV1g5Hw>2TRu6|x`}_MVyZ(D?Y>n!t7g-EKeU0&6H$B%!k$loU)zr}Pz6!-l2 zP{eXKzV#mHxcFGT*?>Rh=xDxxwjRO1i#{H+mX85*@%=@}SY5r&DBU&RdGVOwFPmf! z4u>nAe0L*24Oc47l|iXB;FT8nJ~Vu+usVkUQ+MWX$m3eAD$CG`M>jI(Cr}Wl zh~F>{XMJ7sdFMadoDD9ogm!Fj`K-j^!uva2_-~dMK$){;UJ52ZcBo0y!8wmmYc3Zg zdir0wNdy&J1t?gzu9J*S@F*TuV4cK(c|z>+Drg%`6m??&UqOt8_6WABhP^lQTO21o z&2@%mm3nq`Rbi<`R=Un;40ALbbboA#{Lp3pSM84 zf~!?-M0p}OalUAjSQ`*imLKQB5tG2TEo#Q$WOhv|9Eu;5pYt{^YHCvIAN{YAz%eeN zzN(dD1octzp#4hZki+xmDCEy;U=QI=XBH-BP2vgn)G*CC9W@~=8pd`MLnv?JPz?BkFwLxfuhkS|XGOS5Wr#-`QC`vocMH3tOG z*qr1|*tt{pw|r^k5yP1{pRjQAw!^TSqU^uVJJSjIqU4C;_Fwgfug~Jt`flSnqY=U5 zP-YF`ua6&K#5@UE6US!WL?+_ZD?*Wo(!#Ik;~RWytK&K5FxM63!xgaWV|)&(S0AII zjhlxf@+G^0p&{^=Ox*rEG#Iv{Pt#a)1;FFyfb{el;Ts)X%~_FWinZXt9j-%B=fVb` zAKJ*dS3p&gsQdBf`<#&wY@$4$P{ukNc6Ese;iG}g*w2QJEA}=Tn8<|2^5Zvhn4#q2 zu(1D%#-jmtz*u%O-wU@tZcog(6;#J#(vhd6v)cW6x%YGKz7UYH&vzAB!tHjhxYWOf zp1u645u2YXmy=o78V)JguF$xt>#>pIQP2@1O2uIY7L7s(sdzrctnrps*ZfXSXlrXm zK7Xe!ecDveIxQhrQ*@0)ABP`13$ictAZFu}(kd??a`G3Nd@3AWMPAAlxZkdbu(V#c zhtX<`9h5df^dtjxY+Lj<6Nei~EDRxFB;zXc_b7~R1gw5SS#SKiUt|4gcuc0W)6c?c zB1fM^DEByf7UntfEa>*9&7zU9yU`n^o>gYwEWT!3dUeQEM;4aNmOJ9=LoTmI?s9(0 z<%NKGRBZ%ixbaYofG%PR=Q&g^X_|EcaqJU>;^yy8j9&GQ&`V58r6MeN7aqLW<4p|Z z3mgNk;ALmTtb;4+GlPjmm6S+qOkCk%i!Fb|apv>Gdcbxf3`Xm!aQ|mDCGvH|RXdhL z*}C8J;wCE{`p$X-wC`>M_VEz~*nXzHDGqZTxmfH}-Z*;mi~``y#M%j|*1y`6ttc6W z4lA9pnVJ5E=#M>yS3)TsM@-C%Lo-{T+R(!C~fy+*+1#R&2s_+I5pl$a9Cr2Yo}?k|(p8~0Bf%iDz& z+rF)q3$yGrKMUVJC>h*Mw4#y6IIknEC!vL;GwQs8l5l0sgNOZ|03BM@&a~?~dMJ3@ zP&7F6$IFoKOioOlFMg!8FVM>`85dgsoKyIYJ^iAN;e$b&&cl?38sy+(R2H8fAP~&C z7`vu2_8L+X-*AFHAzC!fkqG{VW*w$J6Ck<{L!fNY|AVRHz^t)kBjXLs+64F=j4NE* zp}h^nwq`fL1UzO@SV)B@?Fxg-M1~&*ccD8cv7Ls|tR6jgGSC}b-pJrYt^>4gi)S8S zdFsW)X|VMg7u+Ev;;Y@~m+af)gFjA9hlYAMYsGGK*F${oM_8Xn&9f99PW-Nan^$uw zgM#VHRx7)NZ|2su*2<4Q$-f!;Z$q;hDAPuav)YyYvzPL4o{qA9y|l@8Nro-YwyE^h znG`kI%~_I66=&Qa^wtie0hI{MR30xPiVt^L5+G&!Zuz;iuRIZ*;byVADI|y*hSFrj zNpA24FFP2mz6IEgnd~>JX6_!YB?a(u-)E}gN*IjcUu@}d?%ZwJQVWQ1_Qg&j?!)QdNw zp+`kpUX3STtk`q8gWqPgXa}(uY=jSc_RZ|gYfsEW0v}7pPSUo4sltroSBAjvXxwsMSC$%_J{`uOD|p_~J>dyGX<;3G}?kmA-sTl^gV70A1Z+|R6=6`bC`!0&A8+1B{C69+Oh z+pZ{htzpU!O!B(brFezagFxo?^GSHwztwb8;vol+dmFcAQ&(v0xz_O`QWcfg(-R%_ zG;ahoX=<-l)V--{wl9V`CNd~Tu&0qmqfo+p$lP4-%#Jd_zec}OTpUegVOvhJ`tO0o z>xo}zw#fv^^k3nF>Iw%37`lwGOyRGEU1wsUZ1FPtwnl2;+^B!$w5LMqLr+qlk;QX6 z&JbN_<3UYOpJN8o78cC2SF?&aod?xaC&uHT8*%%#w*S}&?4H0a+m0SJa?z@`McpIh zbH84mcvxZ2O)uXIZv6eFr(3R_Y|#Aw>PHJv5B^>fp#IM8qGr7GG>`1W;dCf9Ah?T#uJl_$wcD{vdQ*X z9uHJ@&}a|@6%de3_C-`SS>5*-6i|ek@9X;-p7?-=B9L=(&uRL;dvAArRb5?OUDcRO z51Ae|O`dE*J;@aRsL3?lWHL>fWc&Sy$<%~rkD%uJFaBaOx#EMz@eP`oK0)F0;Vuzb zj#7?t8)c$i#72zE?fJp)h=~e#2wV~5fxHay%|w5tb!MXD91Pz?*Z0A2>*o`Bu4;^b z)+3Za_pL5D)mkCh2ewGoes4M1QYPKEI!D;c_)Gwa;6~=R~@0c3qc(tYD$s6JkD)KJ32% zKMVLE4?8g?KI@;Bob&)8zZ2n7*t0`{eY(U)y=R9#9@tsP18N%&JHQ`#$U7J)U0vhL z!>yY)q&Rmg=IS(wi+l&zw<(-$z{_Ibx4XX@ulS3PdKYtgD#pwr;09 zd(8I5Qa=E@=AjMpH;Qd6Fb>o&^1B6gsW2y8D2K7UZo!t97se``iQf8#)StOi?)}(} zG0?yDc(mHaA$I#)$}Sb>khGNoOj{2(<#mejzjd;H`O?l z6hsT<@*uD7?k=e;*`elNgyj`r-)iVPveox*O+O^x+lYU)xw#$mfRuJXSw}G2SJlIU3h(^iy4)sAOXem=EmX&>3U=TQ0{)dge-T z`{8kM`w<1T>kofL&GH!T9}~A#cE#?uE1vVcs(HWaQKJp+Jyt&{Ri(cQY2VLv`}QrV zuGk~qKTi>>|6KSo26jV#^(=45|?3Jj*yls=<7;6`ioa%8|wd(c!Gx`Wr;!z_j2{z z?v}cv>EZ?K)R}svGyVF``PU5m`}hAxVX0}eNM((Wm^aS=)^G5AxNHl48Gg@)(sVIO zatnTv0Jj-Zdc+-momE1%JSQo8zfkYApQP)$)7vD>JRkb>H>m{%Vj0QR+uJLbFE)$c z#+i`eTxo!e?B}MTKXKB0DF*ucFNMFhEmW!-%wq9zPy4u#;1u+ux`@4ShH4H|b~|cwbnyQ4)V$Doq!n)N^gu>rPoQ zXIzxuO#I}PI@oeK*1!*5_N9KU#;~tH#mAjGO1tIt^JHmlDs@Wyy}dn>yyJT{*SJ3E zz6Yy`w+-@UG2~~+pZGa{cklREnlHxOF*Y2lUp7>ml&+;)C2oIhr92?51u$@Q&X2q=Al)}zGyuBrWSKO$m?0m-!H*i^qn;9XzH=1 zzZ&G>;*>q*Ive0ROR_+BIK0lhS3C_E8zq0tnY~G$tNA-vKjfM3@?e+gUpNy3;5Xby zl~ipW75t8{aoxzy{ZT4lZ?%jEe#hr}{JEBFS@)DvzLCPeak`XLx;-%b#L96Qh|7~Ro&PW_=yIHNyY>%#s1AyL-X9gJekI0WmK>=! zI~?bs<{z;$_7)qlSdG2C-|8$6CdR4sEJcTk@wuii`p0!?yb!seCAK*X4$3+6&UEoW$jL3iEIP~<~mNO@- zB?#?9yk3Og=&k%nLl+Y6!afezo7h{k^0YCk9vB#<=pAx66W&?@XV38hFZYEc|y#G*vmW*`}MjsH54eno&HE;U5;3{EQ0?$TN0u_F#13nwpV?iW_*#l8W;H< z^!8b?c)u*C+Nxl4M&|o#?7F7keZBpX#A6<0IR|@>xw0+j73j!ovK6%jzqw{m z-?9(*$?w0N)y@0rYgbQ8W{QUdt3L3p5Bdl;`e4mI*msb}OOn0cSK6;#7_rXu5p!@u z+tqR7s>(~05z`aHqdOKo&0s)zW1G<$tD92n@nq2Kg2(OUJp_NYuP#EoXr|?=Q*uOC@-K4cTNjBwjSji%2-{b zwI0xTPKmU~>l$mm2l6?VG9O6$zJAk}VoZx=Z2hWUm6Oa#_m@!Lg%aws0Ou2Y<)+HD zIvmx29t+Th zj^$?tDLGfw`}M`x4En^um|&b7Y4BIyP&e3SG{%DY?~3AhwNGW`+-|$tj0xfga#G!txu`tGiI#E^Uhke{jQjl$NDmFJz{v12% za%IU*#QNt;byJYiGvuZu*xJD9peTp3NN|o!` z8uYSM)%xLF+6?41gOJyWMTwJY%uNg8EtMs|411P>af#zqDd=OiR3M*4U1MB@W6Ic% z8GIdWG~?XZN64dkNhYvozG!LGFA^-@%^$)SC*$D!~0WDFX7)_6VwImb^`-PR{VVm0)rq{>b9 zC$1ihJmM}Jq^NwOQ}o$qKMtEM(qnE%TYtmapuc8N`m}YKIGfSmrk_UtGt#m>xACpV zS%1?f`vGIf+*?kD|Dg0wBi57<^}bz9b};$|zXSEeUM3ogu8E0f5L-?Z4oaUkH(}dS z*cf-{U_b4F{IO5Y>pbMX3yRm-;kDr!z&CN0A;uaVy2R!uIflU4?upt%671;$J#bZX zNaNQv`DjF3ljk<9zKc{&HX~!D(m6+R=R7Qe%?$E<(Kzz}8#mO&vnjL#p<5P6GV&A+ zIKyU~1A(uDIbMZWYV_8{5*hrST)xzb7M1{U3QDG>d@5s)GFQp_e67fWyIfkEw^{V5>6)DQu zrp`7*hAzgpZ^9nGrP`(^yUO`9HTUEr>$On%+3*F3pKF9;zou8c<2Uph*oNzp>H8 z$n+g~*6d)`w>bB)O7lF)gbS&K8(Em}sBc8Kww$WF6`#H4_(Cu}t z`(fRGZVo;(XxX>F-hNSv%RNBT$k%FrlIM0vOZgAzgKI7ODz3N_KW%?;emKq?t}$}( z7_*Wi9+H;2M$%G#f`8?XH4@_p{&Nd{mcl%%ZN0E@0G(hTS1z?6e_9|dP5C$zP^Z=k kt{3Oe)~IKE$8XeH)2~O46-o \ No newline at end of file diff --git a/app/icon1.png b/app/icon1.png new file mode 100644 index 0000000000000000000000000000000000000000..b9b4acedb9484ae5529cb01973b7cedea2bbe353 GIT binary patch literal 6991 zcmbtZWmgmou-+w>rBk|5x?4IHkWP7#?(PNw>0UaPE(N8O4i{J&B$e)68mR?I0lnV) z7w(67=EIzsb7s!WnM%^rQ6t2o#RC8UgzB##2LHJA{}LGV54T}KlK&Xc&p=HH&^S$h z1OU+CszVfwf-O%iZVihMO8)Zp@SQYYwMS+gYGVU2=2<85+|~G#N|w{IB?F61#_xr* zyO=!p&xkt?eyGU_Zy%=({(s&&HGt%XPnj6murhO^C6+VZ*M3iH>>y>N`Z zpR~qewDrgSv&V!0V)SkZVswHWD7T4xcwiBoWeSs*4@L*C;SoZWn=0eo9k6R(iy4cV zPDlcRgVD&-aVXpJ7V!Y!-OcBkgwgi2!Cs*bl5z=Z!JdzhnO%a=ntkRs88#8>*!P#* zc}t5Gi!L5u^sgI?tr@Clz`NLAF(d+bilP3`FSjdM@bwd}pT;p;F?rJWD<%>@W0sY|c+;exNQ&1klQu=YZj9mV zW}{TC@H8fAI{KWtzzWf-l(Y~)sI^9PB|wTb%fuKgv%~Zo8UZ5yBWIaN8mqudwjC)+ zsb-i>$_2LBlJ?VoAp1n0i`g;DUoZqSil>PqJ2qvY!&&+Y1j$0eQFl=7n#{RUN0L^O zB$Bg|JY#`)zhcICdO{u_5HfiQ1XFv|>C_~sR>g&SF!1oWnHV%!JF3Pvt@*+7xnMa4 zW*4k1$p%ArH>j{7JnSH6OrVUEz+hawxabD$Xlz(!l+M$vWY$n-Vlp0f8B-+SofSKU zoSY^43fYG@WBjV?qx=}h@@1I$q#iXn2nD@;f+lQTlfh!ZGZ}>*TCj z;V(jRHFN}4z-%pI$96GT700Q)HUao_$S}afLpLigJMoCG9`32iDm+I{O3GrrgKV4{ z?T1E%W#xW6Bs8(G7@Th<=y|fErY;y*?FL2l%1Oeg6TGMx5&iG6f862yqihCgB613O6@E_wX=QctS-g5TkljwpT`QahZvhXVAzKB*nGkUoT@2(y;%rZj$B=NSB1V_iRL0Kb z*wYBk&K(xGLw}Ec{~?D}V2=IH%BxsQvp?lU!WRLlA6-drHui*pwQXYNE~ouwViH1i zM#7_^%o5o5U0mSphk~N^Jc;a(rYkqQYX+jiIV?dgdZx1e(K0cmCdhZ__&aK5Bo1nw zEg_RL4P(Zy#e1QxS35=|8-Z#3-~8bTql)1%GsU-OV2nq!yCP%h%VJ_u`7u;jy_a_q zKvZmRivyjj?_dW|D$soM35o*&pjY0*iR(}HJ21_;HU`|VEA1n@GLkr(;{G@_) z<^yGJMHi?kqI#FsSL&&%F8Y*}wn{5Q76q+Piggt0IT7HcI;)M6aKcTge(>86Hff90 zWs}So+^c9GC!0<4NbTpd=2Y+TjFxW8ozUVkAu`2NI)lV%l&nHBlP-MTg@9y!&gs7a zA!4fgl*|M~N6 z5L@fGp4ArB&Pk@rwUknEiy$mRBV&uDm?DNj!9)MMP)l}8Ipf&aFM2hF{)LfwO}LLg zC%9YFbeWA1YA!aE&BDXT5WU3PzrTcC1`tRr&-p-`P|Gw~RK?xOk)34ebb~&@ufa9$>r*@B*_chC<(Nz;% z7@UtXf2UQhbY^UIZAEo*_L-$tUvjiCD0pEHHOTCM>!*6Ha_Z0y4$|E?Bcu%UvGRSF zGTTbnL`D9|01a9A-9GjtXMRR@X6k2#2^GDJzZP^s0hf6XB|=(62dy9I%o$q3IgZ_E z!``e!6`d-BuP#Q^4*Djcl3e-@Xuo}U%P}j( zbFAU7c66NllYd^~HYTI%YVMrvdy?Kf7l!lT-jT%SK%q|h;DezLT(pR<_w06t3STtl*x$S`S1qq z%{tV*h4pf-Os8rv%eTNm6Epg8(tkA@Ne`qPIr>8=(47 zss1}sZ&R+AcOt4|CR|uihTk}7nEuOG8u9iw`pt6|8&#dgThOsDBr+k@V8#B%v(q1c zngiylwHl&S6a%K7&;YNuVhXHgLw zPP0thHaH9qRly<|^jmOf`&E(0H|hP>s5dPe-*&+!R&$l`W(IAKnEKkC?fY|&GGdrM zFDkb~ZV0_DV~}n4hMJc2XnAl=cjRSTV5xCYIDfn}G3W>NVyu|&`ixP)MF&p+by;y- zA~^UXKs^Z(*--87P@Iy&9Trl>VDYq5pS82`UxyGuF5hRQmE0p%n{riR8p-x@ru&Iz zQjZ;STKQnIehp{B+kXP?7QP${tB95O%6s^s@X@BodK$~8!%H?GO8u%jm z!JGkRxXAfcTI?#Wro>hUVGt&FV_Q{sTY7FT{Lc?NWCF~%#C)91ZHj-zt^tT*1(r$~ zCU!0h;`~~cY;D%Dn?)3wCFs3b!j5x&mcIXfXKSX!@dNaK%dos#qc-AhW`?J!aY0aC z!7U1*O=*`VwR!c{Oh9d%@+>g>lOVv$f^#&(zA!WJPn<|%vREOc1?gun1srxs5A&g6 zviLN(yvtknEcCbgzhyYl4WOSP7ixqTcIrFkcKHo0bmR6<|XSxp!UdwGU zAm!21p21APV=ZT_%AQLElY3+7X$*HWj?tss#qsMxSuq8}-0+s%q*w~_6nV*z2kG>q zpC*lJ>}RTIh2@o@y5!3TcM@yKIt`!o!wcCERiUVT*!feRO!Go(< zUu(-2<4(5$zs|=aP_IVB;8?yz*+7QlSD(FEIh{sx;+c*N_bu5$grTog5u`&9eCS}G zS=nOm?)2MMTE1G>ulj52I8ftsj!Th?D)M2HgrxR-K<%r})s@lK$jZu(-oUi{e~w!f zH?bY35=rRYB)$H&)Cfx3U^CO(j+qdH<$cqKWk)w>(bwULr%?C~_oxc=V{S&=(t<`g z3PA9i^})nsHbP|Y(vd%`FsFGw#6E}1bO(HQ=ZcB1JcTtx0xSx#e)u8lrXphP9Za@V zD=jF-O&IIVnn-0}-rP|C{w2z)FB2`&t z-2$B5X%NugjPME>+&N9b&m@`C!}gS#cwbH&`<`iKYRGb#Sbw~eetkPK}TQkR674c zMZ-B|wmUx18tbvD5*vCNQ?GR~J{R{&Gu>&IGiRdn(FGHf2)G0~rD9RLxlZCa`rq*` z3Vmg)n=jdnJnQu_uRDr0>c#TXB@VkCW1+n7G;Kc<24bNRlk}S(KdOYxFpFdeWmZV) zXF+Ahh5Q$t?1<{w9T?7Mboh{ibpiGDcDhQUy%9Hpu_G``-?x-h{I6|XRG^RZ_A)ol zz_6Ykn!|klrHZbgSP!n=%Z3D!e8j@GvuLQ1xtFrG8xuxYxX)R|;MkZJQqVZGS)-kP zG{Q%!P@QNz+}HTg=GhO3l0SqqjbU6#LxVsjxU}dqZ^I8Pzd7G9?>0(5BmE2#yXVXq zQM@!o!*LS}dJP{l+a!RxGR9a|R%ekTdpv}lD?Q`lVu1JARM5I#=ug-Si z;p@CnjYo}gq#-MKm%foUEZ9$A>9LuX5S0bqponv?sdK-T&tbAEl8C6ee7dL)%pqra z@E)m;tcRcXOf0}kve{nBC8tE27-4Jayz)W$J~NtdeSe-bnhlAR$OrXZs*@MKO`rw} z$ot`4*roxblxLSEBWG&YOft}8O_^CrazEi0MLNEn;LnVF0|jt`8e@1+i}o!fGAXvMq~}igHJj=I<#I)b zpzq{|b=s&*>z*H?p<;6yitMk2F&y{zPPzu3T)+gozMKX}tgdFiD=g=jxp}cTG<;E~ zr^A4+#%s`vPcIj*3Ywo1D~i+fIXMru*8Nf#;wsyQ`yYGU2_U=ItNa9M+D%AQX4O4O z3Y$qt70yh|vx0)~$V>|14mP_v&d+D|9QLm+vj<{>@102*CuZOr=&s&&R6~+K^?7!h z++$NWu*y&PkRa5!Y4X)F9geQR4=gu6Ym1igZ&W$Kw+3R42YZe|PMMURzXro-iUe!o zQ>|wCZt94O+PbvqD8Dv#r1^G!%!ej3kUTe_W?4rOgeFIP@EFOT!0W(%^k9pmWhHBD zy+yvOEfN&hEwmQQ$`~|QDs9yftWrYwszv2|bAaFaJgauHZ#Q+dH1VTcX(bcq|17cS z{7@qD%eat`A&A7IkEC5E^h+7xy5iZcAd4lpZ0hK((^kuF5fmNh9QjGel*-)ofXFLV z)4}~UEhFt@yP#o~9gIE0&%v8o#kgjn>F2i3xXdpu|38W0D&Ol#^;omP<=Lr=RBgH+ zexA(tjN8vTA*cWTyp{^=oT=QU`$#Tbb)bP~ZMMll<@qw{e6 zm23Mj)*A^e{juRioqtyYg7Ij2sTiPsB}p;ogo>}hJSc7D~^eXghm#_rJvDj)VtLmv;RoO@`{2@6?SIy8$#+PJ^bIeKY zb;qyOKn&Q+A3xFhnjD*-+#C<$)uXEh22ZF)-#jYvMKvCi{jwolF}7cNgo_)1ukrQm=>RHl#0L8=~b#39Gb^i zGos0z6lx)7L$(;+Z*fZjrW1;!QK&0!1UzoE~|Ok1%1?3zS1+V7WBo<#Dtu1#gl-Wn|i*g1!R zDApkH=nJUw%R8M5mb5u<9 z$_>b!9h|)=k{4l9dmT;;^3)a@PbVN6n8WcyZ)aj)-E}XWn9LmY!kCYV79dLD_!&^G$(v`sKq#wt_e@x~Q(+j1AKJQxI zvtv|HP37ZW3?%IPYU8bAody9GGRIUkMhl zzRk90x~97gcr$2@XU6@yyzvvUM~@wE4CM3sFc|?%c{%-Uql3Y#2&I$Co36%cQS=V zX}>r$mS>0ut`(dk<6^}gZ&VrhJFcyLLgM;MvI`^G;%LD5Y-F7b+z%2}-j|;DMVEJ1 zKgWNMM6~*E+1?s{y0hz|k$iUvR>Cls&D)i$W#q(gF66$ZjR1*x|70_73!%8Sve&Sm|IanfD7tjajI6sJ50L&@u)jB*|?v%Q|y1@_h zj9c>A`HCeuGS(1uLWVRBW{~LBjI>K#4;}q~3;*I{$7JWWbcLb*eaHanDmsuxCF|(_ E0rZPXSO5S3 literal 0 HcmV?d00001 diff --git a/components/CategoryFilters.tsx b/components/CategoryFilters.tsx new file mode 100644 index 0000000..2ecd158 --- /dev/null +++ b/components/CategoryFilters.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { Questionnaire, QuestionnaireCategory } from '@/types'; + +interface CategoryFiltersProps { + questionnaires: Questionnaire[]; + value: QuestionnaireCategory | null; + onChange: (category: QuestionnaireCategory | null) => void; +} + +const categories: Array<{ + value: QuestionnaireCategory; + label: string; +}> = [ + { value: '人格', label: '人格特点' }, + { value: '情绪', label: '情绪与压力' }, + { value: '认知', label: '注意与思考' }, + { value: '睡眠', label: '睡眠' }, + { value: '职业', label: '职业兴趣' }, + { value: '心理健康', label: '综合心理健康' }, +]; + +export function CategoryFilters({ + questionnaires, + value, + onChange, +}: CategoryFiltersProps) { + const availableCategories = categories.filter((category) => + questionnaires.some( + (questionnaire) => questionnaire.category === category.value, + ), + ); + + return ( +

+ ); +} diff --git a/components/Navbar.tsx b/components/Navbar.tsx new file mode 100644 index 0000000..e072ad4 --- /dev/null +++ b/components/Navbar.tsx @@ -0,0 +1,43 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { ClipboardList } from 'lucide-react'; +import { useScopedI18n } from '@/locales/client'; + +export function Navbar() { + const pathname = usePathname(); + const t = useScopedI18n('component.navBar'); + + return ( +
+
+ + + + + {t('title')} + + + +
+
+ ); +} diff --git a/components/questionnaire/List.tsx b/components/questionnaire/List.tsx new file mode 100644 index 0000000..6de3868 --- /dev/null +++ b/components/questionnaire/List.tsx @@ -0,0 +1,119 @@ +'use client'; +import { Search } from 'lucide-react'; +import { useState, useCallback } from 'react'; +import Link from 'next/link'; + +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { CategoryFilters } from '@/components/CategoryFilters'; +import { useScopedI18n } from '@/locales/client'; +import { useQuestionnaire } from '@/hooks/useQuestionnaire'; +import { Questionnaire, QuestionnaireCategory } from '@/types'; + +export default function QuestionnaireList() { + const questionnaires = useQuestionnaire(); + const t = useScopedI18n('component.questionnaire.list'); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedCategory, setSelectedCategory] = + useState(null); + + const handleCategoryChange = useCallback( + (category: QuestionnaireCategory | null) => { + setSelectedCategory(category); + }, + [], + ); + + // Filter questionnaires based on search terms and the selected purpose. + const filteredQuestionnaires = (questionnaires as Questionnaire[]).filter((q) => { + // Text search filtering + const matchesSearch = + q.title.toLowerCase().includes(searchQuery.toLowerCase()) || + q.description.toLowerCase().includes(searchQuery.toLowerCase()) || + q.tags.some((tag) => + tag.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const matchesCategory = + selectedCategory === null || q.category === selectedCategory; + + return matchesSearch && matchesCategory; + }); + + return ( +
+
+
+

{t('title')}

+ + {/* Search bar */} +
+ + setSearchQuery(e.target.value)} + /> +
+ + + + {/* Questionnaire list */} +
+ {filteredQuestionnaires.length > 0 ? ( + filteredQuestionnaires.map((questionnaire) => ( + + + {questionnaire.title} + + +
+

+ {questionnaire.description} +

+
+
+ + + {questionnaire.time} + + + + + +
+ )) + ) : ( +
+ {t('noMatch')} +
+ )} +
+
+
+
+ ); +} diff --git a/components/questionnaire/QuestionnaireDetailsPage.tsx b/components/questionnaire/QuestionnaireDetailsPage.tsx new file mode 100644 index 0000000..093168b --- /dev/null +++ b/components/questionnaire/QuestionnaireDetailsPage.tsx @@ -0,0 +1,163 @@ +'use client'; + +import Link from 'next/link'; +import { Button } from '@/components/ui/button'; +import { ArrowLeft, ArrowRight, Clock, FileText } from 'lucide-react'; +import { useScopedI18n } from '@/locales/client'; +import { Questionnaire } from '@/types'; + +interface QuestionnaireDetailsPageProps { + questionnaire: Questionnaire; +} + +export default function QuestionnaireDetailsPage({ + questionnaire, +}: QuestionnaireDetailsPageProps) { + const t = useScopedI18n('app.questionnaire.page'); + const { title, details, evaluation, id } = questionnaire; + + return ( +
+ + +
+

{title}

+ +
+
+ + {details.questionCount} +
+
+ + {details.evaluationTime} +
+
+ + {evaluation && ( +
+
+
学术认可度
+
{evaluation.academicRecognition}
+
+
+
适合重测
+
+ {evaluation.retestSuitable ? '是' : '否'} +
+
+
+
推荐周期
+
{evaluation.recommendedInterval}
+
+
+ )} + + + + +
+ +
+ +
+
+

准备好开始测评了吗?

+ + + +
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/ADHDResult.tsx b/components/questionnaire/result/analysis/ADHDResult.tsx new file mode 100644 index 0000000..d9fda13 --- /dev/null +++ b/components/questionnaire/result/analysis/ADHDResult.tsx @@ -0,0 +1,134 @@ +'use client'; + +import React from 'react'; +import { useScopedI18n } from '@/locales/client'; +import { calculateADHDResults } from '../../test/private/ADHDCalculator'; + +function useLabels() { + const t = useScopedI18n('components.adhdResult'); + return { + totalScore: t('totalScore'), + inattention: t('inattention'), + hyperactivity: t('hyperactivity'), + partAScore: t('partAScore'), + screeningResult: t('screeningResult'), + severityLevel: t('severityLevel'), + positiveScreen: t('positiveScreen'), + negativeScreen: t('negativeScreen'), + partAPositiveResponses: t('partAPositiveResponses'), + basedOnTotalScore: t('basedOnTotalScore'), + recommendations: t('recommendations'), + importantNotes: t('importantNotes'), + severityLevels: { + low: t('severityLevels.low'), + mild: t('severityLevels.mild'), + moderate: t('severityLevels.moderate'), + high: t('severityLevels.high'), + }, + notes: { + screening: t('notes.screening'), + symptoms: t('notes.symptoms'), + evaluation: t('notes.evaluation'), + }, + recommendationTexts: { + positive: t('recommendationTexts.positive'), + negative: t('recommendationTexts.negative'), + }, + }; +} + +export function ADHDResult({ + answers, +}: { + answers: string[]; +}) { + const labels = useLabels(); + + // Convert answers array to object format expected by calculator + const answersObj: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersObj[index + 1] = answer; + }); + + const results = calculateADHDResults({ answers: answersObj, questions: [] }); + + const getSeverityColor = (severity: string) => { + switch (severity) { + case 'low': return 'text-green-600'; + case 'mild': return 'text-yellow-600'; + case 'moderate': return 'text-orange-600'; + case 'high': return 'text-red-600'; + default: return 'text-gray-600'; + } + }; + + const getSeverityLabel = (severity: string) => { + return labels.severityLevels[severity as keyof typeof labels.severityLevels] || 'Unknown'; + }; + + return ( +
+
+ + + + +
+ +
+
+

{labels.screeningResult}

+
+ {results.screeningPositive ? labels.positiveScreen : labels.negativeScreen} +
+

+ {labels.partAPositiveResponses}: {results.partAPositive}/6 +

+
+ +
+

{labels.severityLevel}

+
+ {getSeverityLabel(results.severity)} +
+

+ {labels.basedOnTotalScore}: {results.totalScore}/72 +

+
+
+ +
+

{labels.recommendations}

+

+ {results.screeningPositive ? labels.recommendationTexts.positive : labels.recommendationTexts.negative} +

+
+ +
+

{labels.importantNotes}

+
    +
  • • {labels.notes.screening}
  • +
  • • {labels.notes.symptoms}
  • +
  • • {labels.notes.evaluation}
  • +
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + {value} +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/AttachmentResult.tsx b/components/questionnaire/result/analysis/AttachmentResult.tsx new file mode 100644 index 0000000..13a0583 --- /dev/null +++ b/components/questionnaire/result/analysis/AttachmentResult.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { calculateAttachmentResults } from '../../test/private/AttachmentCalculator'; + +interface AttachmentResultProps { + answers: string[]; +} + +const patternText = { + secure: '相对安全型', + preoccupied: '焦虑偏高型', + dismissive: '回避偏高型', + fearful: '焦虑与回避都偏高', +}; + +const patternDescription = { + secure: '你在当前重要关系中通常较能亲近、信任和表达需求,不安全感相对较低。', + preoccupied: '你可能较容易担心关系不够稳定,或需要更多确认来获得安全感。', + dismissive: '你可能更习惯保持独立和距离,不太愿意依靠他人或表达脆弱。', + fearful: '你可能既渴望亲近,又担心受伤或被拒绝,因此在关系中容易拉扯。', +}; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 6) * 100))}%`; +} + +export function AttachmentResult({ answers }: AttachmentResultProps) { + const results = calculateAttachmentResults(answers); + + return ( +
+
+

亲密关系依恋结果

+
+ + + +
+
+ +
+ + +
+ +
+

结果解释

+

{patternDescription[results.pattern]}

+
+ +
+ 注:依恋结果会随关系对象和关系阶段变化。它适合帮助你观察互动模式,不是给关系下定论。 +
+
+ ); +} + +function MetricCard({ title, value }: { title: string; value: string }) { + return ( +
+
{title}
+
{value}
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 7 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/BDI2Result.tsx b/components/questionnaire/result/analysis/BDI2Result.tsx new file mode 100644 index 0000000..906911d --- /dev/null +++ b/components/questionnaire/result/analysis/BDI2Result.tsx @@ -0,0 +1,281 @@ +'use client'; + +import React from 'react'; +import { calculateBDI2Results } from '../../test/private/BDI2Calculator'; +import { useScopedI18n } from '@/locales/client'; + +interface BDI2ResultProps { + answers: string[]; +} + +export function BDI2Result({ answers }: BDI2ResultProps) { + const t = useScopedI18n('components.bdi2Result'); + const tCommon = useScopedI18n('common'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateBDI2Results({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + minimal: t('severity.minimal'), + mild: t('severity.mild'), + moderate: t('severity.moderate'), + severe: t('severity.severe') + }; + + const severityDescriptions = { + minimal: t('severityDescriptions.minimal'), + mild: t('severityDescriptions.mild'), + moderate: t('severityDescriptions.moderate'), + severe: t('severityDescriptions.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "minimal": return "text-green-600 bg-green-50 border-green-200"; + case "mild": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "severe": return "text-red-600 bg-red-50 border-red-200"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + const subscaleInfo = { + emotional: { name: t('subscales.emotional'), maxScore: 18, description: t('subscaleDescriptions.emotional') }, + cognitive: { name: t('subscales.cognitive'), maxScore: 15, description: t('subscaleDescriptions.cognitive') }, + somatic: { name: t('subscales.somatic'), maxScore: 15, description: t('subscaleDescriptions.somatic') }, + behavioral: { name: t('subscales.behavioral'), maxScore: 15, description: t('subscaleDescriptions.behavioral') } + }; + + const questionTexts = [ + t('symptoms.0'), t('symptoms.1'), t('symptoms.2'), t('symptoms.3'), t('symptoms.4'), + t('symptoms.5'), t('symptoms.6'), t('symptoms.7'), t('symptoms.8'), t('symptoms.9'), + t('symptoms.10'), t('symptoms.11'), t('symptoms.12'), t('symptoms.13'), t('symptoms.14'), + t('symptoms.15'), t('symptoms.16'), t('symptoms.17'), t('symptoms.18'), t('symptoms.19'), + t('symptoms.20') + ]; + + return ( +
+ {/* Emergency warning */} + {results.suicidalIdeation && ( +
+
+
+ + + +
+
+

{t('labels.emergency_reminder')}

+
+ {t('crisis.suicide_warning')} +
    +
  • {t('crisis.hotline')}
  • +
  • {t('crisis.hospital')}
  • +
  • {t('crisis.doctor')}
  • +
  • {t('crisis.support')}
  • +
+
+
+
+
+ )} + + {/* Overall score */} +
+

{t('title')}

+
+ + + +
+
+ + {/* Severity level description */} +
+

{t('labels.result_interpretation')}

+

+ {severityDescriptions[results.severity as keyof typeof severityDescriptions] || "评估结果异常,请重新测试。"} +

+ +
+
{t('labels.scoring_criteria')}:
+
    +
  • {t('scoring.range_0_13')}
  • +
  • {t('scoring.range_14_19')}
  • +
  • {t('scoring.range_20_28')}
  • +
  • {t('scoring.range_29_63')}
  • +
+
+
+ + {/* Symptom dimension analysis */} +
+

{t('labels.symptom_dimension_analysis')}

+
+ {Object.entries(subscaleInfo).map(([key, info]) => { + const score = results.factorScores[key] as number; + const percentage = (score / info.maxScore) * 100; + + return ( +
+
+ {info.name} + {score}/{info.maxScore} +
+ +
+
+
= 75 ? "bg-red-400" : + percentage >= 50 ? "bg-orange-400" : + percentage >= 25 ? "bg-yellow-400" : "bg-green-400" + }`} + style={{ width: `${percentage}%` }} + >
+
+
+ +
+ {info.description} +
+
+ ); + })} +
+
+ + {/* Item analysis */} +
+

{t('labels.item_detailed_analysis')}

+
+ {results.itemAnalysis.map((item: any, index: number) => ( +
= 1 ? 'bg-red-50 border border-red-200' : 'bg-gray-50' + }`}> +
+ + {index + 1}. {questionTexts[index]} + + {item.questionId === 9 && item.score >= 1 && ( +
{t('labels.suicide_risk_attention')}
+ )} +
+
+ = 1 ? 'text-red-700' : + item.score >= 2 ? 'text-red-600' : + item.score >= 1 ? 'text-yellow-600' : 'text-green-600' + }`}> + {item.score} + + {item.isHigh && item.questionId !== 9 && ( + + {tCommon('labels.needs_attention')} + + )} +
+
+ ))} +
+
+ + {/* Professional advice */} +
+

{t('labels.professional_advice')}

+
+ + {results.severity === "minimal" ? ( +
+
+ {t('advice.maintain_good_state')}: +
    +
  • {t('advice.maintain_good_state_item_1')}
  • +
  • {t('advice.maintain_good_state_item_2')}
  • +
  • {t('advice.maintain_good_state_item_3')}
  • +
  • {t('advice.maintain_good_state_item_4')}
  • +
+
+
+ ) : ( +
+ {t('advice.depression_management')}: +
+ +
+

{t('advice.daily_management')}

+
    +
  • {t('advice.daily_management_item_1')}
  • +
  • {t('advice.daily_management_item_2')}
  • +
  • {t('advice.daily_management_item_3')}
  • +
  • {t('advice.daily_management_item_4')}
  • +
  • {t('advice.daily_management_item_5')}
  • +
+
+ +
+

{t('advice.social_support')}

+
    +
  • {t('advice.social_support_item_1')}
  • +
  • {t('advice.social_support_item_2')}
  • +
  • {t('advice.social_support_item_3')}
  • +
  • {t('advice.social_support_item_4')}
  • +
  • {t('advice.social_support_item_5')}
  • +
+
+
+
+ )} + + {(results.severity === "moderate" || results.severity === "severe") && ( +
+
+ {t('advice.professional_treatment')}: +
    +
  • {t('advice.professional_treatment_item_1')}
  • +
  • {t('advice.professional_treatment_item_2')}
  • +
  • {t('advice.professional_treatment_item_3')}
  • +
  • {t('advice.professional_treatment_item_4')}
  • +
  • {t('advice.professional_treatment_item_5')}
  • +
+
+
+ )} + +
+

+ {t('labels.important_reminder')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/BigFiveResult.tsx b/components/questionnaire/result/analysis/BigFiveResult.tsx new file mode 100644 index 0000000..8197b0b --- /dev/null +++ b/components/questionnaire/result/analysis/BigFiveResult.tsx @@ -0,0 +1,197 @@ +'use client'; + +import { + calculateBigFiveResults, + calculateIpipNeoResults, + ipipNeoItemsByVersion, +} from '../../test/private/BigFiveCalculator'; +import { ipipNeoFacets } from '@/questionairies/bigfive/neo-data'; + +interface BigFiveResultProps { + answers: string[]; + version?: 50 | 120 | 300; +} + +interface ScoreResult { + score: number; + average: number; + itemCount?: number; +} + +const domainLabels = { + neuroticism: { + name: '神经质', + desc: '情绪敏感、压力反应以及体验焦虑、烦躁等负面情绪的倾向。', + }, + extraversion: { + name: '外向性', + desc: '社交投入、自信表达、活跃度和积极情绪。', + }, + openness: { + name: '开放性', + desc: '想象力、审美、求知、尝新和价值观开放程度。', + }, + agreeableness: { + name: '宜人性', + desc: '信任、真诚、合作、利他、谦逊和同情倾向。', + }, + conscientiousness: { + name: '尽责性', + desc: '自我效能、条理、责任、自律、成就追求和谨慎。', + }, +}; + +const shortLabels = { + extraversion: domainLabels.extraversion, + agreeableness: domainLabels.agreeableness, + conscientiousness: domainLabels.conscientiousness, + emotionalStability: { + name: '情绪稳定性', + desc: '情绪平稳、压力耐受和较少焦虑烦躁。', + }, + openness: domainLabels.openness, +}; + +function level(average: number) { + if (average >= 3.8) return '较高'; + if (average <= 2.4) return '较低'; + return '中等'; +} + +function barWidth(average: number) { + return `${Math.max(0, Math.min(100, ((average - 1) / 4) * 100))}%`; +} + +function ScoreCard({ + name, + desc, + result, + maxScore, +}: { + name: string; + desc: string; + result: ScoreResult; + maxScore: number; +}) { + return ( +
+
+
+

{name}

+

{desc}

+
+
+
+ {result.score} +
+
/ {maxScore}
+
+
+
+
+
+
+ 倾向:{level(result.average)} + + 平均 {result.average.toFixed(2)} / 5 + +
+
+ ); +} + +export function BigFiveResult({ answers, version = 50 }: BigFiveResultProps) { + if (version === 50) { + const results = calculateBigFiveResults(answers); + + return ( +
+
+

五大人格结果

+

+ 每个维度满分50分。分数表示相对倾向,没有绝对好坏。 +

+
+
+ {Object.entries(shortLabels).map(([key, info]) => ( + + ))} +
+
+ ); + } + + const items = ipipNeoItemsByVersion[version]; + const results = calculateIpipNeoResults(answers, [...items]); + const domainItemCount = version === 120 ? 24 : 60; + const facetItemCount = version === 120 ? 4 : 10; + + return ( +
+
+

+ IPIP-NEO {version}题结果 +

+

+ 先看五大维度了解整体轮廓,再查看30个面向理解具体差异。分数代表相对倾向,没有绝对好坏。 +

+
+ +
+

五大维度

+
+ {Object.entries(domainLabels).map(([key, info]) => ( + + ))} +
+
+ +
+

30个细分面向

+
+ {Object.entries(ipipNeoFacets).map(([key, facet]) => { + const result = results.facets[key] as ScoreResult; + return ( +
+
+

{facet.name}

+ + {result.score} / {facetItemCount * 5} + +
+
+
+
+
+ {level(result.average)} · 平均 {result.average.toFixed(2)} / 5 +
+
+ ); + })} +
+
+ +
+ 建议结合维度和面向一起阅读:同一大维度下的六个面向可能高低不同,这通常比单一总分更能描述个人风格。 +
+
+ ); +} diff --git a/components/questionnaire/result/analysis/CRTResult.tsx b/components/questionnaire/result/analysis/CRTResult.tsx new file mode 100644 index 0000000..b41e4c5 --- /dev/null +++ b/components/questionnaire/result/analysis/CRTResult.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { calculateCRTResults } from '../../test/private/CRTCalculator'; + +interface CRTResultProps { + answers: string[]; +} + +function summary(score: number) { + if (score >= 6) return '反思推理表现较强,能较好地抑制直觉错误。'; + if (score >= 3) return '反思推理表现中等,有些题目能停下来重新检查。'; + return '本次更容易受直觉答案影响,建议在关键判断中刻意放慢。'; +} + +export function CRTResult({ answers }: CRTResultProps) { + const result = calculateCRTResults(answers); + + return ( +
+
+

CRT 结果

+
+ + {result.score} + + / {result.total} 分 +
+

{summary(result.score)}

+
+ +
+

逐题判断

+
+ {result.items.map((item) => ( +
+ 第 {item.questionId} 题 + + {item.isCorrect ? '正确' : `错误,正确选项:${item.correct}`} + +
+ ))} +
+
+ +
+ CRT 对题目熟悉度很敏感。如果以前见过类似题,分数会偏高;如果当前疲劳或分心,分数也可能偏低。 +
+
+ ); +} diff --git a/components/questionnaire/result/analysis/CareerAnchorsResult.tsx b/components/questionnaire/result/analysis/CareerAnchorsResult.tsx new file mode 100644 index 0000000..82f9bd5 --- /dev/null +++ b/components/questionnaire/result/analysis/CareerAnchorsResult.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { calculateCareerAnchorsResults, CareerAnchor } from '../../test/private/CareerAnchorsCalculator'; + +interface CareerAnchorsResultProps { + answers: string[]; +} + +const descriptions: Record = { + technical: "适合持续积累专业深度,通过专业能力获得成就和认可。", + managerial: "适合整合资源、带团队、处理复杂组织目标和责任。", + autonomy: "适合自由度较高、能自主安排方法和节奏的工作模式。", + security: "适合稳定、规则明确、风险可控且保障清晰的环境。", + entrepreneurial: "适合从零创造、开拓机会、主导项目或业务方向。", + service: "适合使命驱动、对他人或社会有明确贡献感的工作。", + challenge: "适合高难度、高竞争、需要突破限制的问题场景。", + lifestyle: "适合能与健康、家庭、自由时间和整体生活协调的职业路径。", +}; + +function width(score: number) { + return `${Math.max(0, Math.min(100, ((score - 1) / 4) * 100))}%`; +} + +export function CareerAnchorsResult({ answers }: CareerAnchorsResultProps) { + const results = calculateCareerAnchorsResults(answers); + + return ( +
+
+

Career Anchors 职业锚结果

+
+ + +
+
+ +
+

+ 核心职业取向:{results.primary.name} + {results.secondary.name} +

+

+ 职业锚代表你在职业选择中最不愿长期牺牲的价值和动机。高分职业锚之间如有冲突,应结合现实岗位、行业阶段和个人生活目标做取舍。 +

+
+ +
+ {results.ranked.map((item) => ( +
+
+

{item.name}

+ {item.score.toFixed(2)} / 5 +
+
+
+
+

{descriptions[item.id]}

+
+ ))} +
+ +
+ 注:职业锚不是职业能力测验。它更适合帮助你判断什么工作条件和发展路径最可能让你长期投入。 +
+
+ ); +} + +function MetricCard({ title, value, score }: { title: string; value: string; score: string }) { + return ( +
+
{title}
+
{value}
+
{score} / 5
+
+ ); +} diff --git a/components/questionnaire/result/analysis/DASS21Result.tsx b/components/questionnaire/result/analysis/DASS21Result.tsx new file mode 100644 index 0000000..840c4cb --- /dev/null +++ b/components/questionnaire/result/analysis/DASS21Result.tsx @@ -0,0 +1,271 @@ +'use client'; + +import React from 'react'; +import { calculateDASS21Results } from '../../test/private/DASS21Calculator'; +import { useScopedI18n } from '@/locales/client'; + +interface DASS21ResultProps { + answers: string[]; +} + +export function DASS21Result({ answers }: DASS21ResultProps) { + const t = useScopedI18n('components.dass21Result'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateDASS21Results({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + normal: t('severity.normal'), + mild: t('severity.mild'), + moderate: t('severity.moderate'), + severe: t('severity.severe'), + extremely_severe: t('severity.extremely_severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "normal": return "text-green-600 bg-green-50 border-green-200"; + case "mild": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "severe": return "text-red-600 bg-red-50 border-red-200"; + case "extremely_severe": return "text-red-700 bg-red-100 border-red-300"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + const dimensionInfo = { + depression: { + name: t('dimensions.depression'), + score: results.depressionScore, + severity: results.depressionSeverity, + description: t('descriptions.depression'), + maxScore: 42 + }, + anxiety: { + name: t('dimensions.anxiety'), + score: results.anxietyScore, + severity: results.anxietySeverity, + description: t('descriptions.anxiety'), + maxScore: 42 + }, + stress: { + name: t('dimensions.stress'), + score: results.stressScore, + severity: results.stressSeverity, + description: t('descriptions.stress'), + maxScore: 42 + } + }; + + return ( +
+ {/* Overall score */} +
+

{t('title')}

+
+ + + + +
+
+ + {/* Three-dimension analysis */} +
+

{t('labels.three_dimension_analysis')}

+
+ {Object.entries(dimensionInfo).map(([key, info]) => ( +
+
+

{info.name}

+
{info.score}
+
/{info.maxScore}
+
+ +
+
+
+
+
+ +
+ + {severityNames[info.severity as keyof typeof severityNames]} + +
+ +
+ {info.description} +
+
+ ))} +
+
+ + {/* Severity level standards */} +
+

{t('labels.scoring_criteria')}

+
+ +
+

{t('labels.depression_dimension')}

+
+
{t('scoring.depression.normal')}
+
{t('scoring.depression.mild')}
+
{t('scoring.depression.moderate')}
+
{t('scoring.depression.severe')}
+
{t('scoring.depression.extremely_severe')}
+
+
+ +
+

{t('labels.anxiety_dimension')}

+
+
{t('scoring.anxiety.normal')}
+
{t('scoring.anxiety.mild')}
+
{t('scoring.anxiety.moderate')}
+
{t('scoring.anxiety.severe')}
+
{t('scoring.anxiety.extremely_severe')}
+
+
+ +
+

{t('labels.stress_dimension')}

+
+
{t('scoring.stress.normal')}
+
{t('scoring.stress.mild')}
+
{t('scoring.stress.moderate')}
+
{t('scoring.stress.severe')}
+
{t('scoring.stress.extremely_severe')}
+
+
+
+
+ + {/* Result interpretation and recommendations */} +
+

{t('labels.result_interpretation_advice')}

+
+ + {/* Overall assessment */} +
+

{t('labels.overall_assessment')}

+
+ {results.isSevere ? ( +

{t('assessment.severe_message')}

+ ) : ( +

{t('assessment.normal_message')}

+ )} +
+
+ + {/* Dimension-specific recommendations */} + {results.depressionSeverity !== "normal" && ( +
+

{t('advice.depression_dimension')}

+
+

{t('advice.depression_item_1')}

+

{t('advice.depression_item_2')}

+

{t('advice.depression_item_3')}

+

{t('advice.depression_item_4')}

+ {(results.depressionSeverity === "severe" || results.depressionSeverity === "extremely_severe") && ( +

{t('advice.depression_severe')}

+ )} +
+
+ )} + + {results.anxietySeverity !== "normal" && ( +
+

{t('advice.anxiety_dimension')}

+
+

{t('advice.anxiety_item_1')}

+

{t('advice.anxiety_item_2')}

+

{t('advice.anxiety_item_3')}

+

{t('advice.anxiety_item_4')}

+ {(results.anxietySeverity === "severe" || results.anxietySeverity === "extremely_severe") && ( +

{t('advice.anxiety_severe')}

+ )} +
+
+ )} + + {results.stressSeverity !== "normal" && ( +
+

{t('advice.stress_dimension')}

+
+

{t('advice.stress_item_1')}

+

{t('advice.stress_item_2')}

+

{t('advice.stress_item_3')}

+

{t('advice.stress_item_4')}

+ {(results.stressSeverity === "severe" || results.stressSeverity === "extremely_severe") && ( +

{t('advice.stress_severe')}

+ )} +
+
+ )} + + {/* Severe situation warning */} + {results.isSevere && ( +
+
+
+ + + +
+
+
+ {t('labels.important_reminder')}:{t('warning.severe_distress')} +
+
+
+
+ )} + +
+

+ {t('labels.note')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/DarkTriadResult.tsx b/components/questionnaire/result/analysis/DarkTriadResult.tsx new file mode 100644 index 0000000..bb1a1a3 --- /dev/null +++ b/components/questionnaire/result/analysis/DarkTriadResult.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { calculateDarkTriadResults } from '../../test/private/DarkTriadCalculator'; + +interface DarkTriadResultProps { + answers: string[]; +} + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function DarkTriadResult({ answers }: DarkTriadResultProps) { + const results = calculateDarkTriadResults(answers); + + return ( +
+
+

黑暗三联征结果

+
+ + + +
+
+ +
+

结果解释

+

+ 这些维度描述普通人群中的人格倾向。高分可以提示策略性、优越感或低共情冲动带来的关系风险,但不能单独用于临床判断。 +

+
+ +
+ 注:黑暗三联征结果容易被误读,请结合具体行为、关系反馈和情境理解。 +
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/EmpathyResult.tsx b/components/questionnaire/result/analysis/EmpathyResult.tsx new file mode 100644 index 0000000..dd2f339 --- /dev/null +++ b/components/questionnaire/result/analysis/EmpathyResult.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { calculateEmpathyResults } from '../../test/private/EmpathyCalculator'; + +interface EmpathyResultProps { + answers: string[]; +} + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function EmpathyResult({ answers }: EmpathyResultProps) { + const results = calculateEmpathyResults(answers); + + return ( +
+
+

人际反应指数结果

+
+ + + + +
+
+ +
+

结果解释

+

+ 观点采择和共情关怀更接近成熟的理解与关心;个人痛苦代表在他人痛苦面前自己的不安程度,过高时可能需要边界和情绪调节;想象代入代表对故事和角色的情绪进入能力。 +

+
+ +
+ 注:IRI更适合看四个分项组合,不建议只用一个总分判断“共情高低”。 +
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/FisherResult.tsx b/components/questionnaire/result/analysis/FisherResult.tsx new file mode 100644 index 0000000..a4f2365 --- /dev/null +++ b/components/questionnaire/result/analysis/FisherResult.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { calculateFisherResults, FisherDimension } from '../../test/private/FisherCalculator'; + +interface FisherResultProps { + answers: string[]; +} + +const descriptions: Record = { + explorer: '偏好新鲜体验、自由探索和灵活变化,适合开放、变化快、允许试错的环境。', + builder: '重视秩序、责任和稳定关系,适合目标清楚、节奏可靠、需要长期维护的环境。', + director: '偏好逻辑、效率和直接决策,适合需要分析、判断、系统设计和明确目标的环境。', + negotiator: '重视共情、意义和关系协调,适合需要理解人、连接观点和处理复杂情境的环境。', +}; + +const colorClass: Record = { + explorer: 'bg-emerald-500', + builder: 'bg-blue-500', + director: 'bg-indigo-500', + negotiator: 'bg-rose-500', +}; + +export function FisherResult({ answers }: FisherResultProps) { + const results = calculateFisherResults(answers); + + return ( +
+
+

Fisher 气质结果

+
+ + +
+
+ +
+

+ {results.primary.name} + {results.secondary.name} +

+

+ 你的结果以{results.primary.name}为主,{results.secondary.name}为辅助。Fisher模型更适合看“人格签名”: + 最高分说明你最自然调用的风格,第二高分说明你常用的补充策略。 +

+
+ +
+ {results.ranked.map((item) => ( +
+
+

{item.name}

+ {item.score}/40 +
+
+
+
+

{descriptions[item.id]}

+
+ ))} +
+ +
+ 注:Fisher气质量表用于人格风格参考,不是临床诊断。分数差距很小时,说明多个风格都比较常被你使用。 +
+
+ ); +} + +function MetricCard({ title, value, score }: { title: string; value: string; score: string }) { + return ( +
+
{title}
+
{value}
+
{score}
+
+ ); +} diff --git a/components/questionnaire/result/analysis/GAD7Result.tsx b/components/questionnaire/result/analysis/GAD7Result.tsx new file mode 100644 index 0000000..3d6df4a --- /dev/null +++ b/components/questionnaire/result/analysis/GAD7Result.tsx @@ -0,0 +1,198 @@ +'use client'; + +import React from 'react'; +import { calculateGAD7Results } from '../../test/private/GAD7Calculator'; +import { useScopedI18n } from '@/locales/client'; + +interface GAD7ResultProps { + answers: string[]; +} + +export function GAD7Result({ answers }: GAD7ResultProps) { + const t = useScopedI18n('components.gad7Result'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateGAD7Results({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + minimal: t('severity.minimal'), + mild: t('severity.mild'), + moderate: t('severity.moderate'), + severe: t('severity.severe') + }; + + const severityDescriptions = { + minimal: t('severityDescriptions.minimal'), + mild: t('severityDescriptions.mild'), + moderate: t('severityDescriptions.moderate'), + severe: t('severityDescriptions.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "minimal": return "text-green-600 bg-green-50 border-green-200"; + case "mild": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "severe": return "text-red-600 bg-red-50 border-red-200"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + const questionTexts = [ + t('questions.0'), t('questions.1'), t('questions.2'), t('questions.3'), + t('questions.4'), t('questions.5'), t('questions.6') + ]; + + return ( +
+ {/* Overall score */} +
+

{t('title')}

+
+ + + +
+
+ + {/* Severity level description */} +
+

{t('labels.result_interpretation')}

+

+ {severityDescriptions[results.severity as keyof typeof severityDescriptions] || "评估结果异常,请重新测试。"} +

+ +
+
{t('labels.scoring_criteria')}:
+
    +
  • {t('scoring.range_0_4')}
  • +
  • {t('scoring.range_5_9')}
  • +
  • {t('scoring.range_10_14')}
  • +
  • {t('scoring.range_15_21')}
  • +
+
+
+ + {/* Item analysis */} +
+

{t('labels.item_analysis')}

+
+ {results.itemAnalysis.map((item: any, index: number) => ( +
+
+ + {index + 1}. {questionTexts[index]} + +
+
+ = 2 ? 'text-red-600' : + item.score >= 1 ? 'text-yellow-600' : 'text-green-600' + }`}> + {item.score} + + {item.isHigh && ( + + {t('labels.needs_attention')} + + )} +
+
+ ))} +
+ + {results.highScoreItemCount > 0 && ( +
+

{t('labels.high_score_item_alert')}

+
+ {t('highScoreAlert.message', { count: results.highScoreItemCount })} +
+
+ )} +
+ + {/* Professional advice */} +
+

{t('labels.professional_advice')}

+
+ + {results.severity === "minimal" ? ( +
+
+ {t('advice.maintain_good_state')}: +
    +
  • {t('advice.maintain_good_state_item_1')}
  • +
  • {t('advice.maintain_good_state_item_2')}
  • +
  • {t('advice.maintain_good_state_item_3')}
  • +
  • {t('advice.maintain_good_state_item_4')}
  • +
+
+
+ ) : ( +
+ {t('advice.self_management_advice')}: +
    +
  • {t('advice.self_management_item_1')}
  • +
  • {t('advice.self_management_item_2')}
  • +
  • {t('advice.self_management_item_3')}
  • +
  • {t('advice.self_management_item_4')}
  • +
  • {t('advice.self_management_item_5')}
  • +
+
+ )} + + {(results.severity === "moderate" || results.severity === "severe") && ( +
+
+
+ + + +
+
+
+ {t('advice.professional_help_message', { severity: severityNames[results.severity as keyof typeof severityNames] })} +
+
+
+
+ )} + +
+

+ {t('labels.note')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/GDResult.tsx b/components/questionnaire/result/analysis/GDResult.tsx new file mode 100644 index 0000000..62b8753 --- /dev/null +++ b/components/questionnaire/result/analysis/GDResult.tsx @@ -0,0 +1,149 @@ +'use client'; + +import React from 'react'; +import { useScopedI18n } from '@/locales/client'; +import { calculateGDResults } from '../../test/private/GDCalculator'; + +function useLabels() { + const t = useScopedI18n('components.gdResult'); + return { + totalScore: t('totalScore'), + scorePercentage: t('scorePercentage'), + elevatedItems: t('elevatedItems'), + genderIdentity: t('genderIdentity'), + socialRole: t('socialRole'), + physicalDysphoria: t('physicalDysphoria'), + genderExpression: t('genderExpression'), + overallAssessment: t('overallAssessment'), + recommendations: t('recommendations'), + importantNotes: t('importantNotes'), + understandingResults: t('understandingResults'), + factorScores: t('factorScores'), + interpretationLevels: { + low: t('interpretationLevels.low'), + mild: t('interpretationLevels.mild'), + moderate: t('interpretationLevels.moderate'), + high: t('interpretationLevels.high'), + }, + factorDescriptions: { + genderIdentity: t('factorDescriptions.genderIdentity'), + socialRole: t('factorDescriptions.socialRole'), + physicalDysphoria: t('factorDescriptions.physicalDysphoria'), + genderExpression: t('factorDescriptions.genderExpression'), + }, + notes: { + purpose: t('notes.purpose'), + substitute: t('notes.substitute'), + complexity: t('notes.complexity'), + professional: t('notes.professional'), + }, + recommendationTexts: { + high: t('recommendationTexts.high'), + low: t('recommendationTexts.low'), + }, + }; +} + +export function GDResult({ + answers, +}: { + answers: string[]; +}) { + const labels = useLabels(); + + // Convert answers array to object format expected by calculator + const answersObj: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersObj[index + 1] = answer; + }); + + const results = calculateGDResults({ answers: answersObj, questions: [] }); + + const getInterpretationColor = (interpretation: string) => { + switch (interpretation) { + case 'low': return 'text-green-600'; + case 'mild': return 'text-yellow-600'; + case 'moderate': return 'text-orange-600'; + case 'high': return 'text-red-600'; + default: return 'text-gray-600'; + } + }; + + const getInterpretationLabel = (interpretation: string) => { + return labels.interpretationLevels[interpretation as keyof typeof labels.interpretationLevels] || 'Unknown'; + }; + + return ( +
+
+ + + +
+ +
+ + + + +
+ +
+

{labels.overallAssessment}

+
+ {getInterpretationLabel(results.interpretation)} Level +
+

+ Score: {results.totalScore}/189 ({results.scorePercentage}%) +

+
+ +
+

{labels.recommendations}

+

+ {results.scorePercentage >= 50 ? labels.recommendationTexts.high : labels.recommendationTexts.low} +

+
+ +
+

{labels.importantNotes}

+
    +
  • • {labels.notes.purpose}
  • +
  • • {labels.notes.substitute}
  • +
  • • {labels.notes.complexity}
  • +
  • • {labels.notes.professional}
  • +
+
+ +
+

{labels.understandingResults}

+
+

{labels.factorScores}:

+
    +
  • • {labels.genderIdentity}: {labels.factorDescriptions.genderIdentity}
  • +
  • • {labels.socialRole}: {labels.factorDescriptions.socialRole}
  • +
  • • {labels.physicalDysphoria}: {labels.factorDescriptions.physicalDysphoria}
  • +
  • • {labels.genderExpression}: {labels.factorDescriptions.genderExpression}
  • +
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + {value} +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/GritResult.tsx b/components/questionnaire/result/analysis/GritResult.tsx new file mode 100644 index 0000000..9f122cd --- /dev/null +++ b/components/questionnaire/result/analysis/GritResult.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { calculateGritResults } from '../../test/private/GritCalculator'; + +interface GritResultProps { + answers: string[]; +} + +const levelText = { + low: '坚毅程度偏低', + moderate: '坚毅程度中等', + high: '坚毅程度较高', +}; + +const levelDescription = { + low: '你当前在长期目标上的持续投入或兴趣稳定性可能偏弱。更适合从目标拆小、减少干扰、明确反馈开始,而不是单靠意志力硬撑。', + moderate: '你具备一定的坚持能力,但在目标很长期、反馈很慢或兴趣变化较快时,可能会出现波动。', + high: '你通常能围绕长期目标持续努力,也较不容易被短期挫折带偏。继续保留调整目标的弹性会更稳。', +}; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function GritResult({ answers }: GritResultProps) { + const results = calculateGritResults(answers); + + return ( +
+
+

坚毅量表结果

+
+ + + +
+
+ +
+ + +
+ +
+

{levelText[results.level]}

+

{levelDescription[results.level]}

+
+ +
+ 注:Grit 结果描述的是长期目标中的坚持和稳定倾向,不代表能力高低,也不意味着所有目标都应该坚持到底。 +
+
+ ); +} + +interface MetricCardProps { + title: string; + value: string; +} + +function MetricCard({ title, value }: MetricCardProps) { + return ( +
+
{title}
+
+ {value} +
+
/ 5
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/HEXACOResult.tsx b/components/questionnaire/result/analysis/HEXACOResult.tsx new file mode 100644 index 0000000..d61def8 --- /dev/null +++ b/components/questionnaire/result/analysis/HEXACOResult.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { calculateHEXACOResults } from '../../test/private/HEXACOCalculator'; + +interface HEXACOResultProps { + answers: string[]; +} + +const labels = [ + ['诚实谦逊', 'honestyHumility'], + ['情绪性', 'emotionality'], + ['外向性', 'extraversion'], + ['宜人性', 'agreeableness'], + ['尽责性', 'conscientiousness'], + ['开放性', 'openness'], +] as const; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function HEXACOResult({ answers }: HEXACOResultProps) { + const results = calculateHEXACOResults(answers); + + return ( +
+
+

HEXACO 六因素结果

+
+ {labels.map(([label, key]) => ( + + ))} +
+
+ +
+

结果解释

+

+ HEXACO 结果应按六个维度分别理解。它和 Big Five 接近,但额外强调诚实谦逊,有助于观察公平、真诚、地位追求和自我中心相关倾向。 +

+
+ +
+ 注:人格分数是长期倾向的近似画像,不是固定标签。建议结合 Big Five、RIASEC 和实际生活反馈综合理解。 +
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/ISIResult.tsx b/components/questionnaire/result/analysis/ISIResult.tsx new file mode 100644 index 0000000..bcce878 --- /dev/null +++ b/components/questionnaire/result/analysis/ISIResult.tsx @@ -0,0 +1,95 @@ +'use client'; + +import React from 'react'; +import { calculateISIResults } from '../../test/private/ISICalculator'; +import { useScopedI18n } from '@/locales/client'; + +interface ISIResultProps { + answers: string[]; +} + +export function ISIResult({ answers }: ISIResultProps) { + const t = useScopedI18n('components.isiResult'); + + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateISIResults({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + no_insomnia: t('severity.no_insomnia'), + subthreshold: t('severity.subthreshold'), + moderate: t('severity.moderate'), + severe: t('severity.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "no_insomnia": return "text-green-600 bg-green-50 border-green-200"; + case "subthreshold": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "severe": return "text-red-600 bg-red-50 border-red-200"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + return ( +
+
+

{t('title')}

+
+ + + +
+
+ +
+

{t('labels.result_interpretation')}

+
+
{t('labels.scoring_criteria')}:
+
    +
  • {t('scoring.range_0_7')}
  • +
  • {t('scoring.range_8_14')}
  • +
  • {t('scoring.range_15_21')}
  • +
  • {t('scoring.range_22_28')}
  • +
+
+
+ + {results.isSevere && ( +
+
+ {t('advice.sleep_specialist_message')} +
+
+ )} +
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/MaximizerResult.tsx b/components/questionnaire/result/analysis/MaximizerResult.tsx new file mode 100644 index 0000000..036f8e7 --- /dev/null +++ b/components/questionnaire/result/analysis/MaximizerResult.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { calculateMaximizerResults } from '../../test/private/MaximizerCalculator'; + +interface MaximizerResultProps { + answers: string[]; +} + +const levelText = { + satisficer: '满意型选择风格', + balanced: '平衡型选择风格', + maximizer: '最大化选择风格', +}; + +const levelDescription = { + satisficer: '你更倾向于在达到标准后做决定,选择成本较低,也更容易保持行动效率。', + balanced: '你会在重要选择中认真比较,但通常不会让比较无限扩大。这个区间相对灵活。', + maximizer: '你更倾向于追求最优选择,会投入较多比较和信息搜集。优势是标准高,风险是决策成本和后悔感增加。', +}; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 6) * 100))}%`; +} + +export function MaximizerResult({ answers }: MaximizerResultProps) { + const results = calculateMaximizerResults(answers); + + return ( +
+
+

决策最大化倾向结果

+
+ + + +
+
+ +
+ + + +
+ +
+

结果解释

+

{levelDescription[results.level]}

+
+ +
+ 注:最大化倾向不是好坏判断。重要决策可以认真比较,日常低风险选择则适合设置停止规则。 +
+
+ ); +} + +function MetricCard({ title, value }: { title: string; value: string }) { + return ( +
+
{title}
+
{value}
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 7 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/NPDResult.tsx b/components/questionnaire/result/analysis/NPDResult.tsx new file mode 100644 index 0000000..1375834 --- /dev/null +++ b/components/questionnaire/result/analysis/NPDResult.tsx @@ -0,0 +1,181 @@ +'use client'; + +import React from 'react'; +import { useScopedI18n } from '@/locales/client'; +import { calculateNPDResults } from '../../test/private/NPDCalculator'; + +function useLabels() { + const t = useScopedI18n('components.npdResult'); + return { + totalScore: t('totalScore'), + percentile: t('percentile'), + leadership: t('leadership'), + exhibitionism: t('exhibitionism'), + narcissisticTraitsLevel: t('narcissisticTraitsLevel'), + dominantTrait: t('dominantTrait'), + entitlement: t('entitlement'), + interpretation: t('interpretation'), + understandingTraits: t('understandingTraits'), + factorBreakdown: t('factorBreakdown'), + importantNotes: t('importantNotes'), + healthyVsProblematic: t('healthyVsProblematic'), + interpretationLevels: { + low: t('interpretationLevels.low'), + average: t('interpretationLevels.average'), + above_average: t('interpretationLevels.above_average'), + high: t('interpretationLevels.high'), + }, + traitLabels: { + leadership: t('traitLabels.leadership'), + exhibitionism: t('traitLabels.exhibitionism'), + entitlement: t('traitLabels.entitlement'), + }, + factorDescriptions: { + leadership: t('factorDescriptions.leadership'), + exhibitionism: t('factorDescriptions.exhibitionism'), + entitlement: t('factorDescriptions.entitlement'), + }, + notes: { + continuum: t('notes.continuum'), + adaptive: t('notes.adaptive'), + disorder: t('notes.disorder'), + purpose: t('notes.purpose'), + population: t('notes.population'), + }, + healthyAspects: t('healthyAspects'), + potentialConcerns: t('potentialConcerns'), + balanceKey: t('balanceKey'), + recommendationTexts: { + high: t('recommendationTexts.high'), + above_average: t('recommendationTexts.above_average'), + low: t('recommendationTexts.low'), + }, + }; +} + +export function NPDResult({ + answers, +}: { + answers: string[]; +}) { + const labels = useLabels(); + + // Convert answers array to object format expected by calculator + const answersObj: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersObj[index + 1] = answer; + }); + + const results = calculateNPDResults({ answers: answersObj, questions: [] }); + + const getInterpretationColor = (interpretation: string) => { + switch (interpretation) { + case 'low': return 'text-green-600'; + case 'average': return 'text-blue-600'; + case 'above_average': return 'text-yellow-600'; + case 'high': return 'text-red-600'; + default: return 'text-gray-600'; + } + }; + + const getInterpretationLabel = (interpretation: string) => { + return labels.interpretationLevels[interpretation as keyof typeof labels.interpretationLevels] || 'Unknown'; + }; + + const getDominantTraitLabel = (trait: string) => { + return labels.traitLabels[trait as keyof typeof labels.traitLabels] || 'Unknown'; + }; + + return ( +
+
+ + + + +
+ +
+
+

{labels.narcissisticTraitsLevel}

+
+ {getInterpretationLabel(results.interpretation)} +
+

+ Score: {results.totalScore}/16 (≈{results.percentile}th percentile) +

+
+ +
+

{labels.dominantTrait}

+
+ {getDominantTraitLabel(results.dominantTrait)} +
+

+ {labels.entitlement}: {results.factorScores.entitlement} +

+
+
+ +
+

{labels.interpretation}

+

+ {results.interpretation === "high" + ? labels.recommendationTexts.high + : results.interpretation === "above_average" + ? labels.recommendationTexts.above_average + : labels.recommendationTexts.low} +

+
+ +
+

{labels.understandingTraits}

+
+

{labels.factorBreakdown}:

+
    +
  • {labels.traitLabels.leadership} ({results.factorScores.leadership}): {labels.factorDescriptions.leadership}
  • +
  • {labels.traitLabels.exhibitionism} ({results.factorScores.exhibitionism}): {labels.factorDescriptions.exhibitionism}
  • +
  • {labels.traitLabels.entitlement} ({results.factorScores.entitlement}): {labels.factorDescriptions.entitlement}
  • +
+
+
+ +
+

{labels.importantNotes}

+
    +
  • • {labels.notes.continuum}
  • +
  • • {labels.notes.adaptive}
  • +
  • • {labels.notes.disorder}
  • +
  • • {labels.notes.purpose}
  • +
  • • {labels.notes.population}
  • +
+
+ +
+

{labels.healthyVsProblematic}

+
+

{labels.healthyAspects}

+

{labels.potentialConcerns}

+

{labels.balanceKey}

+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + {value} +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/NeedForCognitionResult.tsx b/components/questionnaire/result/analysis/NeedForCognitionResult.tsx new file mode 100644 index 0000000..82921cc --- /dev/null +++ b/components/questionnaire/result/analysis/NeedForCognitionResult.tsx @@ -0,0 +1,82 @@ +'use client'; + +import { calculateNeedForCognitionResults } from '../../test/private/NeedForCognitionCalculator'; + +interface NeedForCognitionResultProps { + answers: string[]; +} + +const levelText = { + low: '认知需求偏低', + moderate: '认知需求中等', + high: '认知需求较高', +}; + +const levelDescription = { + low: '你可能更偏好清晰、直接、低认知负荷的处理方式。面对复杂问题时,降低进入门槛和明确收益会更有帮助。', + moderate: '你会根据情境投入思考:重要、有兴趣或有价值的问题更容易激发你深入加工。', + high: '你通常喜欢深入思考、分析复杂问题和寻找新解法。注意在低风险问题上保留效率感,会更平衡。', +}; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function NeedForCognitionResult({ answers }: NeedForCognitionResultProps) { + const results = calculateNeedForCognitionResults(answers); + + return ( +
+
+

认知需求量表结果

+
+ + + +
+
+ +
+ + +
+ +
+

结果解释

+

{levelDescription[results.level]}

+
+ +
+ 注:认知需求表示思考动机和偏好,不是智力测验。可与CRT结果结合理解。 +
+
+ ); +} + +function MetricCard({ title, value }: { title: string; value: string }) { + return ( +
+
{title}
+
{value}
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/OCDResult.tsx b/components/questionnaire/result/analysis/OCDResult.tsx new file mode 100644 index 0000000..17317b6 --- /dev/null +++ b/components/questionnaire/result/analysis/OCDResult.tsx @@ -0,0 +1,77 @@ +'use client'; + +import React from 'react'; +import { useScopedI18n } from '@/locales/client'; + +function useLabels() { + const t = useScopedI18n('components.ocdResult'); + return { + total: t('totalScore'), + obs: t('obsessionsScore'), + comp: t('compulsionsScore'), + severity: t('severity'), + severityMap: { + 1: t('severityLevel.1'), + 2: t('severityLevel.2'), + 3: t('severityLevel.3'), + 4: t('severityLevel.4'), + 5: t('severityLevel.5'), + } as Record, + }; +} + +export function OCDResult({ + answers, +}: { + answers: string[]; +}) { + const labels = useLabels(); + const toNumber = (v: string | undefined) => Number(v) || 0; + + let obsessionsScore = 0; + let compulsionsScore = 0; + for (let i = 1; i <= 10; i++) { + const score = toNumber(answers[i - 1]); // array index start 0 + if (i <= 5) obsessionsScore += score; + else compulsionsScore += score; + } + + const totalScore = obsessionsScore + compulsionsScore; + + let severityLevel = 0; + if (totalScore >= 32) severityLevel = 5; + else if (totalScore >= 24) severityLevel = 4; + else if (totalScore >= 16) severityLevel = 3; + else if (totalScore >= 8) severityLevel = 2; + else severityLevel = 1; + + return ( +
+ + + + +
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + {value} +
+ ); +} diff --git a/components/questionnaire/result/analysis/OEPSResult.tsx b/components/questionnaire/result/analysis/OEPSResult.tsx new file mode 100644 index 0000000..d644a45 --- /dev/null +++ b/components/questionnaire/result/analysis/OEPSResult.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { calculateOEPSResults } from '../../test/private/OEPSCalculator'; + +interface OEPSResultProps { + answers: string[]; +} + +function barWidth(value: number) { + return `${Math.max(0, Math.min(100, (value / 5) * 100))}%`; +} + +export function OEPSResult({ answers }: OEPSResultProps) { + const result = calculateOEPSResults(answers); + + return ( +
+
+

九型人格结果

+

+ 你的最高候选类型是: +

+
+
+ {result.top.name} +
+

{result.top.description}

+

+ 平均分:{result.top.average.toFixed(2)} / 5 +

+
+
+ +
+

九型分数排序

+
+ {result.ranked.map((item) => ( +
+
+
+
{item.name}
+
+ {item.description} +
+
+
+ {item.average.toFixed(2)} +
+
+
+
+
+
+ ))} +
+
+ +
+ OEPS 和九型人格适合作为自我探索材料。若前几名分数接近,建议把它们都作为候选类型继续阅读和核对,不要只看最高分。 +
+
+ ); +} diff --git a/components/questionnaire/result/analysis/PHQ9Result.tsx b/components/questionnaire/result/analysis/PHQ9Result.tsx new file mode 100644 index 0000000..c3667df --- /dev/null +++ b/components/questionnaire/result/analysis/PHQ9Result.tsx @@ -0,0 +1,240 @@ +'use client'; + +import React from 'react'; +import { calculatePHQ9Results } from '../../test/private/PHQ9Calculator'; +import { useScopedI18n } from '@/locales/client'; + +interface PHQ9ResultProps { + answers: string[]; +} + +export function PHQ9Result({ answers }: PHQ9ResultProps) { + const t = useScopedI18n('components.phq9Result'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculatePHQ9Results({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + minimal: t('severity.minimal'), + mild: t('severity.mild'), + moderate: t('severity.moderate'), + moderately_severe: t('severity.moderately_severe'), + severe: t('severity.severe') + }; + + const severityDescriptions = { + minimal: t('severityDescriptions.minimal'), + mild: t('severityDescriptions.mild'), + moderate: t('severityDescriptions.moderate'), + moderately_severe: t('severityDescriptions.moderately_severe'), + severe: t('severityDescriptions.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "minimal": return "text-green-600 bg-green-50 border-green-200"; + case "mild": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "moderately_severe": return "text-red-600 bg-red-50 border-red-200"; + case "severe": return "text-red-700 bg-red-100 border-red-300"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + const questionTexts = [ + t('questions.0'), t('questions.1'), t('questions.2'), t('questions.3'), t('questions.4'), + t('questions.5'), t('questions.6'), t('questions.7'), t('questions.8') + ]; + + return ( +
+ {/* Overall score */} +
+

{t('title')}

+
+ + + +
+
+ + {/* Important warning */} + {results.suicidalIdeation && ( +
+
+
+ + + +
+
+

{t('labels.emergency_reminder')}

+
+ {t('crisis.suicide_warning')} +
    +
  • {t('crisis.hotline')}
  • +
  • {t('crisis.hospital')}
  • +
  • {t('crisis.doctor')}
  • +
  • {t('crisis.support')}
  • +
+
+
+
+
+ )} + + {/* Severity level description */} +
+

{t('labels.result_interpretation')}

+

+ {severityDescriptions[results.severity as keyof typeof severityDescriptions] || "评估结果异常,请重新测试。"} +

+ +
+
{t('labels.scoring_criteria')}:
+
    +
  • {t('scoring.range_0_4')}
  • +
  • {t('scoring.range_5_9')}
  • +
  • {t('scoring.range_10_14')}
  • +
  • {t('scoring.range_15_19')}
  • +
  • {t('scoring.range_20_27')}
  • +
+
+ + {results.majorDepressionCriteria && ( +
+
+ {t('clinical.major_depression_warning')} +
+
+ )} +
+ + {/* Item analysis */} +
+

{t('labels.item_analysis')}

+
+ {results.itemAnalysis.map((item: any, index: number) => ( +
= 1 ? 'bg-red-50 border border-red-200' : 'bg-gray-50' + }`}> +
+ + {index + 1}. {questionTexts[index]} + + {item.questionId === 9 && item.score >= 1 && ( +
{t('labels.needs_immediate_attention')}
+ )} +
+
+ = 1 ? 'text-red-700' : + item.score >= 2 ? 'text-red-600' : + item.score >= 1 ? 'text-yellow-600' : 'text-green-600' + }`}> + {item.score} + + {item.isHigh && item.questionId !== 9 && ( + + {t('labels.needs_attention')} + + )} +
+
+ ))} +
+ + {results.highScoreItemCount > 0 && ( +
+

{t('labels.high_score_item_analysis')}

+
+ {t('highScoreAnalysis.message', { count: results.highScoreItemCount })} +
+
+ )} +
+ + {/* Professional advice */} +
+

{t('labels.professional_advice')}

+
+ + {results.severity === "minimal" ? ( +
+
+ {t('advice.maintain_good_state')}: +
    +
  • {t('advice.maintain_good_state_item_1')}
  • +
  • {t('advice.maintain_good_state_item_2')}
  • +
  • {t('advice.maintain_good_state_item_3')}
  • +
  • {t('advice.maintain_good_state_item_4')}
  • +
+
+
+ ) : ( +
+ {t('advice.self_management_advice')}: +
    +
  • {t('advice.self_management_item_1')}
  • +
  • {t('advice.self_management_item_2')}
  • +
  • {t('advice.self_management_item_3')}
  • +
  • {t('advice.self_management_item_4')}
  • +
  • {t('advice.self_management_item_5')}
  • +
  • {t('advice.self_management_item_6')}
  • +
+
+ )} + + {(results.severity === "moderate" || results.severity === "moderately_severe" || results.severity === "severe") && ( +
+
+ {t('advice.professional_treatment')}: +
    +
  • {t('advice.professional_treatment_item_1')}
  • +
  • {t('advice.professional_treatment_item_2')}
  • +
  • {t('advice.professional_treatment_item_3')}
  • +
  • {t('advice.professional_treatment_item_4')}
  • +
+
+
+ )} + +
+

+ {t('labels.note')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/PSS10Result.tsx b/components/questionnaire/result/analysis/PSS10Result.tsx new file mode 100644 index 0000000..9b70d8f --- /dev/null +++ b/components/questionnaire/result/analysis/PSS10Result.tsx @@ -0,0 +1,284 @@ +'use client'; + +import React from 'react'; +import { calculatePSS10Results } from '../../test/private/PSS10Calculator'; +import { useScopedI18n } from '@/locales/client'; + +interface PSS10ResultProps { + answers: string[]; +} + +export function PSS10Result({ answers }: PSS10ResultProps) { + const t = useScopedI18n('components.pss10Result'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculatePSS10Results({ + answers: answersMap, + questions: [] + }); + + + const severityDescriptions = { + low: t('severityDescriptions.low'), + moderate: t('severityDescriptions.moderate'), + high: t('severityDescriptions.high') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "low": return "text-green-600 bg-green-50 border-green-200"; + case "moderate": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "high": return "text-red-600 bg-red-50 border-red-200"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + const questionTexts = [ + t('questions.0'), t('questions.1'), t('questions.2'), t('questions.3'), t('questions.4'), + t('questions.5'), t('questions.6'), t('questions.7'), t('questions.8'), t('questions.9') + ]; + + const getScoreInterpretation = (score: number) => { + if (score <= 13) return { level: t('scoreInterpretation.low_level'), color: "text-green-600", desc: t('scoreInterpretation.low_desc') }; + if (score <= 26) return { level: t('scoreInterpretation.moderate_level'), color: "text-yellow-600", desc: t('scoreInterpretation.moderate_desc') }; + return { level: t('scoreInterpretation.high_level'), color: "text-red-600", desc: t('scoreInterpretation.high_desc') }; + }; + + const scoreInterp = getScoreInterpretation(results.totalScore); + + return ( +
+ {/* Overall score */} +
+

{t('title')}

+
+ + + + +
+
+ + {/* Severity level description */} +
+

{t('labels.result_interpretation')}

+

+ {severityDescriptions[results.severity as keyof typeof severityDescriptions]} +

+ +
+
+ {t('labels.score_interpretation')}: +
    +
  • • {t('scoring.total_range')}
  • +
  • • {t('scoring.stress_perception_desc')}
  • +
  • • {t('scoring.coping_ability_desc')}
  • +
+
+ +
+ {t('labels.reference_standards')}: +
    +
  • • {t('scoring.range_0_13')}
  • +
  • • {t('scoring.range_14_26')}
  • +
  • • {t('scoring.range_27_40')}
  • +
+
+
+
+ + {/* Subscale analysis */} +
+

{t('labels.subscale_analysis')}

+
+ + {/* Stress perception */} +
+

+ + {t('subscales.stress_perception_title')} ({results.stressPerceptionScore}/24分) +

+
+
+
+
+

+ {t('subscales.stress_perception_desc')} +

+
+
+ + {/* Coping ability */} +
+

+ + {t('subscales.coping_ability_title')} ({results.copingAbilityScore}/16分) +

+
+
+
+
+

+ {t('subscales.coping_ability_desc')} +

+
+
+
+
+ + {/* Item analysis */} +
+

{t('labels.item_analysis')}

+
+ {results.itemAnalysis.map((item: any, index: number) => ( +
+
+
+ + {index + 1}. {questionTexts[index]} + + {item.isReverse && ( + + {t('labels.reverse_scoring')} + + )} +
+
+ {t('labels.original_score')}: {item.originalScore} {item.isReverse ? `→ ${t('labels.actual_score')}: ${item.actualScore}` : ''} +
+
+
+ = 3 ? 'text-red-600' : + item.actualScore >= 2 ? 'text-yellow-600' : 'text-green-600' + }`}> + {item.actualScore} + + {item.isHigh && ( + + {t('labels.high_stress')} + + )} +
+
+ ))} +
+ + {results.highScoreItemCount > 0 && ( +
+

{t('labels.high_score_reminder')}

+
+ {t('highScoreAnalysis.message', { count: results.highScoreItemCount })} +
+
+ )} +
+ + {/* Stress management recommendations */} +
+

{t('labels.stress_management_advice')}

+
+ + {results.severity === "low" ? ( +
+
+ {t('advice.maintain_good_state')}: +
    +
  • {t('advice.maintain_good_state_item_1')}
  • +
  • {t('advice.maintain_good_state_item_2')}
  • +
  • {t('advice.maintain_good_state_item_3')}
  • +
  • {t('advice.maintain_good_state_item_4')}
  • +
+
+
+ ) : ( +
+ {t('advice.stress_management_strategies')}: +
+ + {/* Short-term strategies */} +
+

{t('advice.short_term_strategies')}

+
    +
  • {t('advice.short_term_item_1')}
  • +
  • {t('advice.short_term_item_2')}
  • +
  • {t('advice.short_term_item_3')}
  • +
  • {t('advice.short_term_item_4')}
  • +
  • {t('advice.short_term_item_5')}
  • +
+
+ + {/* Long-term strategies */} +
+

{t('advice.long_term_strategies')}

+
    +
  • {t('advice.long_term_item_1')}
  • +
  • {t('advice.long_term_item_2')}
  • +
  • {t('advice.long_term_item_3')}
  • +
  • {t('advice.long_term_item_4')}
  • +
  • {t('advice.long_term_item_5')}
  • +
+
+
+
+ )} + + {results.severity === "high" && ( +
+
+
+ + + +
+
+
+ {t('advice.high_stress_warning')} +
+
+
+
+ )} + +
+

+ {t('labels.note')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/RIASECResult.tsx b/components/questionnaire/result/analysis/RIASECResult.tsx new file mode 100644 index 0000000..ed983a2 --- /dev/null +++ b/components/questionnaire/result/analysis/RIASECResult.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { + calculateRIASECResults, + riasecTypes, +} from '../../test/private/RIASECCalculator'; + +interface RIASECResultProps { + answers: string[]; +} + +const descriptions = { + R: '动手、设备、机械、户外与具体操作', + I: '研究、分析、科学与复杂问题', + A: '创作、设计、表达与开放式任务', + S: '帮助、教学、照护与合作', + E: '领导、说服、销售与组织资源', + C: '数据、记录、流程与明确规范', +}; + +function barWidth(average: number) { + return `${Math.max(0, Math.min(100, ((average - 1) / 4) * 100))}%`; +} + +export function RIASECResult({ answers }: RIASECResultProps) { + const { scores, ranking, hollandCode } = calculateRIASECResults(answers); + + return ( +
+
+
你的 Holland Code
+
+ {hollandCode} +
+

+ 前三位依次代表当前最突出的职业兴趣方向。分数接近时,不必过度强调字母顺序。 +

+
+ +
+ {ranking.map(([code]) => { + const result = scores[code] as { score: number; average: number }; + const type = riasecTypes[code as keyof typeof riasecTypes]; + return ( +
+
+
+

+ {code} · {type.name} +

+

+ {descriptions[code as keyof typeof descriptions]} +

+
+
+
{result.score}
+
/ 40
+
+
+
+
+
+
+ 平均 {result.average.toFixed(2)} / 5 +
+
+ ); + })} +
+ +
+ 兴趣高分表示更可能享受这类活动,不代表已经具备相应能力。选择职业时还应结合技能、价值观、现实机会与工作环境。 +
+
+ ); +} diff --git a/components/questionnaire/result/analysis/ResultAnalysis.tsx b/components/questionnaire/result/analysis/ResultAnalysis.tsx new file mode 100644 index 0000000..729f1da --- /dev/null +++ b/components/questionnaire/result/analysis/ResultAnalysis.tsx @@ -0,0 +1,119 @@ +"use client"; + +import React from 'react'; +import { OCDResult } from './OCDResult'; +import { SCL90Result } from './SCL90Result'; +import { SDSResult } from './SDSResult'; +import { GAD7Result } from './GAD7Result'; +import { PHQ9Result } from './PHQ9Result'; +import { PSS10Result } from './PSS10Result'; +import { DASS21Result } from './DASS21Result'; +import { BDI2Result } from './BDI2Result'; +import { ISIResult } from './ISIResult'; +import { ADHDResult } from './ADHDResult'; +import { GDResult } from './GDResult'; +import { NPDResult } from './NPDResult'; +import { BigFiveResult } from './BigFiveResult'; +import { CRTResult } from './CRTResult'; +import { OEPSResult } from './OEPSResult'; +import { RIASECResult } from './RIASECResult'; +import { WHO5Result } from './WHO5Result'; +import { SelfEsteemResult } from './SelfEsteemResult'; +import { GritResult } from './GritResult'; +import { SelfControlResult } from './SelfControlResult'; +import { NeedForCognitionResult } from './NeedForCognitionResult'; +import { MaximizerResult } from './MaximizerResult'; +import { AttachmentResult } from './AttachmentResult'; +import { EmpathyResult } from './EmpathyResult'; +import { DarkTriadResult } from './DarkTriadResult'; +import { HEXACOResult } from './HEXACOResult'; +import { FisherResult } from './FisherResult'; +import { SchwartzResult } from './SchwartzResult'; +import { VIAResult } from './VIAResult'; +import { CareerAnchorsResult } from './CareerAnchorsResult'; + +interface Props { + questionnaireId: string; + answers: string[]; +} + +export function ResultAnalysis({ questionnaireId, answers }: Props) { + switch (questionnaireId) { + case 'bigfive': + return ; + case 'bigfive-120': + return ; + case 'bigfive-300': + return ; + case 'oeps': + return ; + case 'crt': + return ; + case 'riasec': + return ; + case 'self-esteem': + return ; + case 'grit': + return ; + case 'self-control': + return ; + case 'need-for-cognition': + return ; + case 'maximizer': + return ; + case 'attachment': + return ; + case 'empathy': + return ; + case 'dark-triad': + return ; + case 'hexaco': + return ; + case 'fisher': + return ; + case 'schwartz': + return ; + case 'via': + return ; + case 'career-anchors': + return ; + case 'ocd': + return ; + case 'scl90': + return ; + case 'sds': + return ; + case 'gad7': + return ; + case 'phq9': + return ; + case 'pss10': + return ; + case 'dass21': + return ; + case 'who5': + return ; + case 'bdi2': + return ; + case 'isi': + return ; + case 'adhd': + return ; + case 'gd': + return ; + case 'npd': + return ; + default: + return ( +
+
+

暂不支持的量表

+

+ 抱歉,暂时不支持量表ID为 "{questionnaireId}" 的结果分析。 + 请检查量表配置或联系开发人员。 +

+
+
+ ); + } +} diff --git a/components/questionnaire/result/analysis/SCL90Result.tsx b/components/questionnaire/result/analysis/SCL90Result.tsx new file mode 100644 index 0000000..9e0ce8f --- /dev/null +++ b/components/questionnaire/result/analysis/SCL90Result.tsx @@ -0,0 +1,156 @@ +'use client'; + +import React from 'react'; +import { useScopedI18n } from '@/locales/client'; +import { calculateSCL90Results } from '../../test/private/SCL90Calculator'; + +interface SCL90ResultProps { + answers: string[]; +} + +export function SCL90Result({ answers }: SCL90ResultProps) { + const t = useScopedI18n('components.scl90Result'); + const tCommon = useScopedI18n('common'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateSCL90Results({ + answers: answersMap, + questions: [] + }); + + const factorNames = { + somatization: t('factors.somatization'), + obsessive: t('factors.obsessive'), + interpersonal: t('factors.interpersonal'), + depression: t('factors.depression'), + anxiety: t('factors.anxiety'), + hostility: t('factors.hostility'), + phobic: t('factors.phobic'), + paranoid: t('factors.paranoid'), + psychotic: t('factors.psychotic'), + other: t('factors.other') + }; + + const severityNames = { + normal: tCommon('severity.normal'), + mild: tCommon('severity.mild'), + moderate: tCommon('severity.moderate'), + severe: tCommon('severity.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "normal": return "text-green-600"; + case "mild": return "text-yellow-600"; + case "moderate": return "text-orange-600"; + case "severe": return "text-red-600"; + default: return "text-gray-600"; + } + }; + + const getFactorSeverity = (score: number) => { + if (score >= 3) return "severe"; + if (score >= 2) return "moderate"; + if (score >= 1.5) return "mild"; + return "normal"; + }; + + return ( +
+ {/* Overall score */} +
+

{t('labels.overall_assessment')}

+
+ + + + +
+
+ + {/* Factor scores */} +
+

{t('labels.factor_analysis')}

+
+ {Object.entries(results.factorScores).map(([factor, score]) => { + const factorSeverity = getFactorSeverity(Number(score)); + return ( +
+
+ {factorNames[factor as keyof typeof factorNames]} + + {severityNames[factorSeverity as keyof typeof severityNames]} + +
+
{Number(score).toFixed(2)}
+
+ ); + })} +
+
+ + {/* Result interpretation */} +
+

{tCommon('labels.result_interpretation')}

+
+
+ {t('clinical.rating_criteria')}: +
    +
  • • {t('clinical.rating_scale')}
  • +
+
+
+ {t('clinical.judgment_criteria')}: +
    +
  • • {t('clinical.total_score_criteria')}
  • +
  • • {t('clinical.factor_score_2')}
  • +
  • • {t('clinical.factor_score_3')}
  • +
+
+ {results.isSevere && ( +
+
+
+ + + +
+
+
+ {t('warnings.severe_condition')} +
+
+
+
+ )} +
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/SDSResult.tsx b/components/questionnaire/result/analysis/SDSResult.tsx new file mode 100644 index 0000000..ce7b9d4 --- /dev/null +++ b/components/questionnaire/result/analysis/SDSResult.tsx @@ -0,0 +1,171 @@ +'use client'; + +import React from 'react'; +import { calculateSDSResults } from '../../test/private/SDSCalculator'; +import { useScopedI18n } from '@/locales/client'; + +interface SDSResultProps { + answers: string[]; +} + +export function SDSResult({ answers }: SDSResultProps) { + const t = useScopedI18n('components.sdsResult'); + + // Convert answer format to the format required by calculator + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateSDSResults({ + answers: answersMap, + questions: [] + }); + + const severityNames = { + normal: t('severity.normal'), + mild: t('severity.mild'), + moderate: t('severity.moderate'), + severe: t('severity.severe') + }; + + const severityDescriptions = { + normal: t('severityDescriptions.normal'), + mild: t('severityDescriptions.mild'), + moderate: t('severityDescriptions.moderate'), + severe: t('severityDescriptions.severe') + }; + + const getSeverityColor = (severity: string) => { + switch (severity) { + case "normal": return "text-green-600 bg-green-50 border-green-200"; + case "mild": return "text-yellow-600 bg-yellow-50 border-yellow-200"; + case "moderate": return "text-orange-600 bg-orange-50 border-orange-200"; + case "severe": return "text-red-600 bg-red-50 border-red-200"; + default: return "text-gray-600 bg-gray-50 border-gray-200"; + } + }; + + // Get raw score (not multiplied by 1.25) + const rawScore = Math.round(results.totalScore / 1.25); + + return ( +
+ {/* Overall score */} +
+

{t('title')}

+
+ + + +
+
+ + {/* Severity level description */} +
+

{t('labels.result_interpretation')}

+

+ {severityDescriptions[results.severity as keyof typeof severityDescriptions]} +

+ +
+
{t('labels.scoring_criteria')}:
+
    +
  • • {t('scoring.range_0_52')}
  • +
  • • {t('scoring.range_53_62')}
  • +
  • • {t('scoring.range_63_72')}
  • +
  • • {t('scoring.range_73_plus')}
  • +
+
+
+ + {/* Item analysis */} +
+

{t('labels.detailed_analysis')}

+
+ + {/* High score items reminder */} +
+

{t('labels.scale_description')}

+
+

• {t('scaleInfo.description_1')}

+

• {t('scaleInfo.description_2')}

+

• {t('scaleInfo.description_3')}

+
+
+ + {/* Scoring method description */} +
+

{t('labels.scoring_method')}

+
+

正向计分项目:{t('scaleInfo.positive_items')}

+

反向计分项目:{t('scaleInfo.reverse_items')}

+

选项计分:{t('scaleInfo.option_scoring')}

+

反向计分:{t('scaleInfo.reverse_scoring')}

+
+
+ + {(results.severity === "severe" || results.severity === "moderate") && ( +
+
+
+ + + +
+
+
+ {t('labels.important_reminder')}:{t('warnings.depression_reminder')} +
+
+
+
+ )} +
+
+ + {/* Professional advice */} +
+

{t('labels.professional_advice')}

+
+
+ {t('advice.high_score_title')} +
    +
  • • {t('advice.seek_professional')}
  • +
  • • {t('advice.share_feelings')}
  • +
  • • {t('advice.maintain_routine')}
  • +
  • • {t('advice.avoid_substances')}
  • +
  • • {t('advice.suicide_help')}
  • +
+
+
+

+ {t('labels.note')}:{t('disclaimer')} +

+
+
+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; + className?: string; +} + +function MetricCard({ title, value, className = '' }: MetricCardProps) { + return ( +
+ {title} + + {value} + +
+ ); +} \ No newline at end of file diff --git a/components/questionnaire/result/analysis/SchwartzResult.tsx b/components/questionnaire/result/analysis/SchwartzResult.tsx new file mode 100644 index 0000000..3c45155 --- /dev/null +++ b/components/questionnaire/result/analysis/SchwartzResult.tsx @@ -0,0 +1,133 @@ +'use client'; + +import { + calculateSchwartzResults, + SchwartzHigherOrder, + SchwartzValue, +} from '../../test/private/SchwartzCalculator'; + +interface SchwartzResultProps { + answers: string[]; +} + +const valueDescriptions: Record = { + selfDirection: '重视独立思考、自由选择和创造。', + stimulation: '重视新奇、挑战、变化和兴奋体验。', + hedonism: '重视愉悦、享受和积极体验。', + achievement: '重视能力表现、努力成果和外部认可。', + power: '重视影响力、资源、地位和掌控感。', + security: '重视稳定、安全、健康和生活保障。', + conformity: '重视规则、克制和对他人影响的边界。', + tradition: '重视文化、家庭、仪式和既有传承。', + benevolence: '重视照顾亲近的人、忠诚和可靠支持。', + universalism: '重视公平、包容、自然和更广泛的人群福祉。', +}; + +const higherOrderDescriptions: Record = { + opennessToChange: '偏向自由、探索、变化和个人选择。', + conservation: '偏向秩序、稳定、规范和连续性。', + selfEnhancement: '偏向成就、影响力和个人资源增长。', + selfTranscendence: '偏向关怀、公平、包容和超越个人利益。', +}; + +function width(score: number) { + return `${Math.max(0, Math.min(100, ((score - 1) / 4) * 100))}%`; +} + +export function SchwartzResult({ answers }: SchwartzResultProps) { + const results = calculateSchwartzResults(answers); + const topValues = results.rankedValues.slice(0, 3); + + return ( +
+
+

Schwartz 价值观结果

+
+ {topValues.map((item, index) => ( + + ))} +
+
+ +
+

+ 主要价值组合:{topValues.map((item) => item.name).join(' / ')} +

+

+ 价值观结果更适合看相对优先级,而不是单个分数高低。你的高分价值通常会影响职业选择、关系边界、生活节奏和长期目标。 +

+
+ +
+ {results.rankedHigherOrders.map((item) => ( + + ))} +
+ +
+

十种基础价值观

+
+ {results.rankedValues.map((item) => ( + + ))} +
+
+ +
+ 注:价值观不是好坏评价,而是优先级。相对冲突的价值同时高分时,实际选择中可能更需要明确场景和取舍。 +
+
+ ); +} + +function MetricCard({ title, value, score }: { title: string; value: string; score: string }) { + return ( +
+
{title}
+
{value}
+
{score} / 5
+
+ ); +} + +function ScoreCard({ + title, + score, + description, + color, +}: { + title: string; + score: number; + description: string; + color: string; +}) { + return ( +
+
+

{title}

+ {score.toFixed(2)} / 5 +
+
+
+
+

{description}

+
+ ); +} diff --git a/components/questionnaire/result/analysis/SelfControlResult.tsx b/components/questionnaire/result/analysis/SelfControlResult.tsx new file mode 100644 index 0000000..32e4476 --- /dev/null +++ b/components/questionnaire/result/analysis/SelfControlResult.tsx @@ -0,0 +1,89 @@ +'use client'; + +import { calculateSelfControlResults } from '../../test/private/SelfControlCalculator'; + +interface SelfControlResultProps { + answers: string[]; +} + +const levelText = { + low: '自控力偏低', + moderate: '自控力中等', + high: '自控力较高', +}; + +const levelDescription = { + low: '你可能比较容易受到诱惑、情绪或即时满足影响。比起责备自己,更有效的方向通常是减少诱因、降低开始成本,并把计划拆得更具体。', + moderate: '你具备一定的自控能力,但在压力、疲劳或诱惑较强的场景中可能会波动。环境设计和习惯系统会很有帮助。', + high: '你通常能较好地管理冲动、抵抗诱惑并推进计划。继续注意休息和目标弹性,能避免把自律变成过度紧绷。', +}; + +function width(value: number) { + return `${Math.max(0, Math.min(100, ((value - 1) / 4) * 100))}%`; +} + +export function SelfControlResult({ answers }: SelfControlResultProps) { + const results = calculateSelfControlResults(answers); + + return ( +
+
+

自控力量表结果

+
+ + + +
+
+ +
+ + +
+ +
+

结果解释

+

{levelDescription[results.level]}

+
+ +
+ 注:自控力会受睡眠、压力、环境和任务设计影响。结果适合用于自我观察,不应作为能力或人格优劣判断。 +
+
+ ); +} + +interface MetricCardProps { + title: string; + value: string; +} + +function MetricCard({ title, value }: MetricCardProps) { + return ( +
+
{title}
+
+ {value} +
+
+ ); +} + +function BarCard({ title, value }: { title: string; value: number }) { + return ( +
+
+

{title}

+ + {value.toFixed(2)} / 5 + +
+
+
+
+
+ ); +} diff --git a/components/questionnaire/result/analysis/SelfEsteemResult.tsx b/components/questionnaire/result/analysis/SelfEsteemResult.tsx new file mode 100644 index 0000000..63c911f --- /dev/null +++ b/components/questionnaire/result/analysis/SelfEsteemResult.tsx @@ -0,0 +1,77 @@ +'use client'; + +import { calculateSelfEsteemResults } from '../../test/private/SelfEsteemCalculator'; + +interface SelfEsteemResultProps { + answers: string[]; +} + +const levelText = { + low: '自尊偏低', + moderate: '自尊中等', + high: '自尊较高', +}; + +const levelDescription = { + low: '你当前的整体自我评价可能偏严苛,容易在挫折、人际评价或压力情境中否定自己。这个结果适合提醒你关注自我接纳、情绪状态和支持系统。', + moderate: '你的整体自尊处在较常见区间,通常能够看到自身价值,但在压力或失败体验下可能仍会有明显波动。', + high: '你对自身价值和能力通常有较稳定、积极的评价。继续保持现实、温和且有弹性的自我认识会更有帮助。', +}; + +function barWidth(score: number) { + return `${Math.max(0, Math.min(100, (score / 30) * 100))}%`; +} + +export function SelfEsteemResult({ answers }: SelfEsteemResultProps) { + const results = calculateSelfEsteemResults(answers); + const levelClass = + results.level === 'high' + ? 'border-green-200 bg-green-50 text-green-800' + : results.level === 'moderate' + ? 'border-blue-200 bg-blue-50 text-blue-800' + : 'border-yellow-200 bg-yellow-50 text-yellow-800'; + + return ( +
+
+

自尊量表结果

+
+ + + +
+
+
+
+
+ +
+

结果解释

+

{levelDescription[results.level]}

+
+ +
+ 注:Rosenberg 自尊量表用于自评和追踪,不是临床诊断工具。建议结合近期生活事件、情绪状态和其他量表一起理解。 +
+
+ ); +} + +interface MetricCardProps { + title: string; + value: string; +} + +function MetricCard({ title, value }: MetricCardProps) { + return ( +
+
{title}
+
+ {value} +
+
+ ); +} diff --git a/components/questionnaire/result/analysis/VIAResult.tsx b/components/questionnaire/result/analysis/VIAResult.tsx new file mode 100644 index 0000000..2be840a --- /dev/null +++ b/components/questionnaire/result/analysis/VIAResult.tsx @@ -0,0 +1,140 @@ +'use client'; + +import { calculateVIAResults, VIAStrength, VIAVirtue } from '../../test/private/VIACalculator'; + +interface VIAResultProps { + answers: string[]; +} + +const strengthDescriptions: Record = { + creativity: "用新颖有效的方式思考和行动。", + curiosity: "主动探索未知,持续提问和观察。", + judgment: "开放评估证据,避免草率判断。", + loveOfLearning: "从学习和掌握新知识中获得满足。", + perspective: "能整合经验,为自己或他人提供有用视角。", + bravery: "在压力、风险或恐惧中坚持重要行动。", + perseverance: "面对困难仍持续完成目标。", + honesty: "真实、可靠,不依赖伪装获得认可。", + zest: "带着能量、投入和生命力行动。", + love: "重视亲密、信任和双向关心。", + kindness: "愿意照顾他人并提供实际帮助。", + socialIntelligence: "理解他人情绪、动机和场景需求。", + teamwork: "愿意合作并承担团队责任。", + fairness: "重视公正、平等和不偏袒。", + leadership: "组织、协调并带动群体前进。", + forgiveness: "能在合适时机放下怨气,重新看待关系。", + humility: "不过度夸大自己,承认仍需学习。", + prudence: "行动前考虑风险、后果和边界。", + selfRegulation: "管理冲动、情绪和行为节奏。", + appreciation: "感受自然、艺术和卓越表现中的美。", + gratitude: "看见并珍惜已经拥有和被给予的东西。", + hope: "相信未来可以改善,并愿意为此行动。", + humor: "用轻松方式缓和压力、连接他人。", + spirituality: "感到自己与更大的意义、信念或整体有关联。", +}; + +const virtueDescriptions: Record = { + wisdom: "认知探索、学习、判断和形成视角的优势。", + courage: "面对困难仍行动、坚持和保持真实的优势。", + humanity: "建立亲密、善意和人际理解的优势。", + justice: "合作、公平和组织群体行动的优势。", + temperance: "克制、审慎、谦逊和修复关系的优势。", + transcendence: "连接意义、美、希望、感恩和幽默的优势。", +}; + +function width(score: number) { + return `${Math.max(0, Math.min(100, ((score - 1) / 4) * 100))}%`; +} + +export function VIAResult({ answers }: VIAResultProps) { + const results = calculateVIAResults(answers); + const signature = results.rankedStrengths.slice(0, 5); + + return ( +
+
+

VIA 性格优势结果

+
+ {signature.map((item, index) => ( + + ))} +
+
+ +
+

+ 标志性优势:{signature.map((item) => item.name).join(" / ")} +

+

+ VIA结果重点看你最自然、最常调用的优势。低排序项目不等于弱点,只表示它们不是当前最突出的表达方式。 +

+
+ +
+ {results.rankedVirtues.map((item) => ( + + ))} +
+ +
+

24项性格优势

+
+ {results.rankedStrengths.map((item) => ( + + ))} +
+
+ +
+ 注:本测评用于性格优势自评,不是官方VIA-IS题库,也不用于诊断。建议把前5项优势用于学习、职业和关系策略设计。 +
+
+ ); +} + +function MetricCard({ title, value, score }: { title: string; value: string; score: string }) { + return ( +
+
{title}
+
{value}
+
{score} / 5
+
+ ); +} + +function ScoreCard({ + title, + score, + description, + color, +}: { + title: string; + score: number; + description: string; + color: string; +}) { + return ( +
+
+

{title}

+ {score.toFixed(2)} / 5 +
+
+
+
+

{description}

+
+ ); +} diff --git a/components/questionnaire/result/analysis/WHO5Result.tsx b/components/questionnaire/result/analysis/WHO5Result.tsx new file mode 100644 index 0000000..577382e --- /dev/null +++ b/components/questionnaire/result/analysis/WHO5Result.tsx @@ -0,0 +1,104 @@ +'use client'; + +import React from 'react'; +import { calculateWHO5Results } from '../../test/private/WHO5Calculator'; + +interface WHO5ResultProps { + answers: string[]; +} + +const levelText = { + high: '幸福感较好', + moderate: '幸福感中等', + low: '幸福感偏低', + very_low: '幸福感明显偏低', +}; + +const levelDescription = { + high: '最近两周的积极情绪、活力和生活兴趣整体较好,可以继续保持目前有帮助的生活节奏。', + moderate: '整体幸福感处在中等区间,可以留意睡眠、压力、社交和日常恢复感的变化。', + low: '幸福感偏低,建议结合PHQ-9、GAD-7或DASS-21进一步了解情绪状态。', + very_low: '幸福感明显偏低,近期可能承受了较多压力或情绪困扰,建议尽快寻求可靠支持或专业评估。', +}; + +const questions = [ + '快乐、心情愉快', + '平静和放松', + '精力充沛、充满活力', + '醒来时清新、休息充分', + '日常生活中有感兴趣的事情', +]; + +export function WHO5Result({ answers }: WHO5ResultProps) { + const answersMap: { [key: number]: string } = {}; + answers.forEach((answer, index) => { + answersMap[index + 1] = answer; + }); + + const results = calculateWHO5Results({ answers: answersMap }); + + const levelClass = results.level === 'high' + ? 'border-green-200 bg-green-50 text-green-800' + : results.level === 'moderate' + ? 'border-blue-200 bg-blue-50 text-blue-800' + : results.level === 'low' + ? 'border-yellow-200 bg-yellow-50 text-yellow-800' + : 'border-red-200 bg-red-50 text-red-800'; + + return ( +
+
+

WHO-5 幸福感结果

+
+ + + +
+
+ +
+

结果解释

+

{levelDescription[results.level]}

+ {results.needsAttention && ( +

+ WHO-5通常建议:原始总分低于13分,或任一题为0-1分时,可以进一步做情绪筛查或寻求专业意见。 +

+ )} +
+ +
+

分项情况

+
+ {results.itemScores.map((score, index) => ( +
+ {index + 1}. {questions[index]} + + {score}/5 + +
+ ))} +
+
+ +
+

+ 注:WHO-5用于幸福感筛查和追踪,不等同于临床诊断。若结果偏低,请结合近期生活事件、睡眠、压力和其他量表综合判断。 +

+
+
+ ); +} + +interface MetricCardProps { + title: string; + value: React.ReactNode; +} + +function MetricCard({ title, value }: MetricCardProps) { + return ( +
+ {title} + {value} +
+ ); +} diff --git a/components/questionnaire/result/public/AnswerList.tsx b/components/questionnaire/result/public/AnswerList.tsx new file mode 100644 index 0000000..cbe78a7 --- /dev/null +++ b/components/questionnaire/result/public/AnswerList.tsx @@ -0,0 +1,43 @@ +import { Questionnaire } from '@/types'; +import { useScopedI18n } from '@/locales/client'; + +interface AnswerListProps { + questions: Questionnaire['questions']; + answers: string[]; // array of selected option values in order + renderOptions: (id: number) => { id: number; content: string; value: string }[]; +} + +export function AnswerList({ questions, answers, renderOptions }: AnswerListProps) { + const t = useScopedI18n('common'); + + if (!questions || questions.length === 0) return null; + + return ( +
+

{t('answerList.title')}

+
+ {questions.map((q, idx) => { + const selectedValue = answers[idx]; + + const optionContent = selectedValue !== undefined ? (() => { + const opts = renderOptions(q.id) || []; + const found = opts.find(o => String(o.value) === String(selectedValue)); + return found ? found.content : `${t('answerList.option')} ${selectedValue}`; + })() : t('answerList.unanswered'); + + return ( +
+ {idx + 1}. {q.content} + + {selectedValue !== undefined ? `${optionContent}` : t('answerList.unanswered')} + +
+ ); + })} +
+
+ ); +} diff --git a/components/questionnaire/result/public/ResultContainer.tsx b/components/questionnaire/result/public/ResultContainer.tsx new file mode 100644 index 0000000..793d20f --- /dev/null +++ b/components/questionnaire/result/public/ResultContainer.tsx @@ -0,0 +1,133 @@ +import Link from 'next/link'; +import { ReactNode } from 'react'; +import { Button } from '@/components/ui/button'; +import { useScopedI18n } from '@/locales/client'; +import { Copy, Download, FileText } from 'lucide-react'; +import { toast } from 'sonner'; +import { Questionnaire } from '@/types'; + +interface ResultContainerProps { + title: string; + id: string; + children: ReactNode; + questionnaire?: Questionnaire; + answers?: string[]; + questionnaireResults?: Record; +} + +export function ResultContainer({ title, id, children, questionnaire, answers, questionnaireResults }: ResultContainerProps) { + const t = useScopedI18n( + 'component.questionnaire.result.public.resultContainer' + ); + + const handleCopyResultLink = async () => { + try { + await navigator.clipboard.writeText( + `${window.location.origin}${window.location.pathname}`, + ); + toast.success(t('copySuccess')); + } catch { + toast.error(t('copyError')); + } + }; + + const buildResultMarkdown = () => { + if (!questionnaire || !answers || !questionnaireResults) { + return null; + } + + const currentTime = new Date().toLocaleString(); + + let resultData = `# ${t('copyTemplate.title')}\n\n`; + resultData += `## ${t('copyTemplate.basicInfo')}\n`; + resultData += `- ${t('copyTemplate.questionnaireName')}: ${questionnaire.title}\n`; + resultData += `- ${t('copyTemplate.questionnaireId')}: ${id}\n`; + resultData += `- ${t('copyTemplate.assessmentTime')}: ${currentTime}\n`; + resultData += `- ${t('copyTemplate.questionCount')}: ${questionnaire.questions.length}\n\n`; + + resultData += `## ${t('copyTemplate.questionsAndAnswers')}\n`; + Object.entries(questionnaireResults).forEach(([question, answer], index) => { + resultData += `${index + 1}. ${question}\n ${t('copyTemplate.answer')}: ${answer}\n\n`; + }); + + resultData += `## ${t('copyTemplate.usage')}\n`; + resultData += `${t('copyTemplate.disclaimer')}\n\n`; + resultData += `${t('copyTemplate.source')}: ${t('copyTemplate.platform')}\n`; + resultData += `${t('copyTemplate.website')}: ${window.location.origin}\n`; + + return resultData; + }; + + const handleCopyResultData = async () => { + const resultData = buildResultMarkdown(); + if (!resultData) { + toast.error(t('copyResultDataError')); + return; + } + + try { + await navigator.clipboard.writeText(resultData); + toast.success(t('copyResultDataSuccess')); + } catch { + toast.error(t('copyResultDataError')); + } + }; + + const handleDownloadResultData = () => { + const resultData = buildResultMarkdown(); + if (!resultData) { + toast.error(t('downloadResultDataError')); + return; + } + + const blob = new Blob([resultData], { type: 'text/markdown;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `${id}-result.md`; + anchor.click(); + URL.revokeObjectURL(url); + toast.success(t('downloadResultDataSuccess')); + }; + return ( +
+
+

+ {title} - {t('resultText')} +

+ +
+
{children}
+
+ +
+ +
+ + + +
+
+
+
+ ); +} diff --git a/components/questionnaire/test/QuestionnaireTest.tsx b/components/questionnaire/test/QuestionnaireTest.tsx new file mode 100644 index 0000000..c3826eb --- /dev/null +++ b/components/questionnaire/test/QuestionnaireTest.tsx @@ -0,0 +1,238 @@ +'use client'; + +import { useState, useEffect, useRef, useCallback } from 'react'; +import { Question } from '@/components/questionnaire/test/public/Question'; +import { Navigation } from '@/components/questionnaire/test/public/Navigation'; +import { ProgressPanel } from '@/components/questionnaire/test/public/ProgressPanel'; +import { ProgressBar } from '@/components/questionnaire/test/public/ProgressBar'; +import { saveDraft, loadDraft, clearDraft } from '@/lib/storage'; +import { saveResult } from '@/lib/result-storage'; +import { Questionnaire as QuestionnaireType, QuestionType } from '@/types'; +import { useRouter } from 'next/navigation'; +import { toast } from "sonner" + +interface QuestionnaireProps { + questionnaire: QuestionnaireType; + id: string; +} + +export function Questionnaire({ + questionnaire, + id, +}: QuestionnaireProps) { + const router = useRouter(); + // State management + const [currentPage, setCurrentPage] = useState(1); + const [answers, setAnswers] = useState<{ [key: number]: string }>(() => { + // Load saved answers from local storage + const savedAnswers = loadDraft(id); + return savedAnswers || {}; + }); + // Create refs to reference each question element + const questionRefs = useRef<{ [key: number]: HTMLDivElement | null }>({}); + // Flag to indicate whether the questionnaire has been submitted + const hasSubmittedRef = useRef(false); + + // Save answers when component unmounts + useEffect(() => { + return () => { + // If user hasn't submitted yet, persist draft on unmount + if (!hasSubmittedRef.current && Object.keys(answers).length > 0) { + saveDraft(id, answers); + } + }; + }, [id, answers]); + + + + // Initialize question data - using real questionnaire data + const generateQuestions = (): QuestionType[] => { + + // Check the questionnaire for question data + if (!questionnaire.questions || questionnaire.questions.length === 0) { + // If real data is not available, simulated data is used + throw new Error('Questionnaire data not found'); + } + + // Use real questionnaire data + return questionnaire.questions.map((q, index: number) => { + const options = questionnaire.renderOptions(q.id) + return { + id: index + 1, + content: q.content, + options: options, + } + }); + }; + + const [questions, setQuestions] = useState([]); + const [activePanelQuestion, setActivePanelQuestion] = useState( + null + ); + // Control the display state of the progress panel + const [showProgressPanel, setShowProgressPanel] = useState(true); + + // Number of questions per page + const questionsPerPage = 5; + // Total number of pages + const totalPages = Math.ceil((questions.length || 0) / questionsPerPage); + // Questions on the current page + const currentQuestions = questions.slice( + (currentPage - 1) * questionsPerPage, + currentPage * questionsPerPage + ); + + // Number of answered questions + const answeredCount = Object.keys(answers).length; + // Calculate completion percentage + const completionPercentage = questions.length + ? (answeredCount / questions.length) * 100 + : 0; + + // This generateQuestions function changes every time useEffect runs + // Solution is to move it inside useEffect or wrap it with useCallback + const generateQuestionsCallback = useCallback(generateQuestions, [ + questionnaire, + ]); + + useEffect(() => { + setQuestions(generateQuestionsCallback()); + // Reset the refs object to reassign when the list of issues changes + questionRefs.current = {}; + }, [id, questionnaire, generateQuestionsCallback]); + + + const handleSelect = (questionId: number, value: string) => { + const newAnswers = { + ...answers, + [questionId]: value, + }; + setAnswers(newAnswers); + // Auto-save answers + if (Object.keys(newAnswers).length < questions.length) { + saveDraft(id, newAnswers); + } + }; + + const goToPage = (page: number) => { + if (page >= 1 && page <= totalPages) { + setCurrentPage(page); + window.scrollTo(0, 0); + } + }; + + const goToQuestion = (questionId: number) => { + // Calculate which page the question is on + const page = Math.ceil(questionId / questionsPerPage); + + // If already on the page, scroll to the question + if (currentPage === page) { + scrollToQuestion(questionId); + } else { + // Otherwise, switch page first, then scroll to question after page loads + setCurrentPage(page); + + // Use setTimeout to ensure DOM is updated before scrolling + setTimeout(() => { + scrollToQuestion(questionId); + }, 100); + } + + // Set highlight effect + setActivePanelQuestion(questionId); + setTimeout(() => { + setActivePanelQuestion(null); + }, 1500); + }; + + // Scroll to specific question position + const scrollToQuestion = (questionId: number) => { + const questionElement = questionRefs.current[questionId]; + if (questionElement) { + // Get question element's position relative to viewport + const rect = questionElement.getBoundingClientRect(); + + // Calculate scroll position with slight offset for better visual effect + const scrollTop = window.pageYOffset + rect.top - 100; + + // Smooth scroll to question position + window.scrollTo({ + top: scrollTop, + behavior: 'smooth', + }); + } + }; + + const handleSubmit = () => { + // Check if all questions are answered first + if (answeredCount < questions.length) { + toast("请先完成所有题目"); + return; + } + + if (answers) { + // Mark as submitted to prevent saving draft on unmount + hasSubmittedRef.current = true; + + // Clear draft before navigation + clearDraft(id); + + // Store answers in this browser tab instead of putting them in the URL. + const resultAnswers = questions.map((q) => answers[q.id] ?? '0'); + saveResult(id, resultAnswers); + + router.push(`/questionnaire/${id}/result`); + } + + return; + }; + + // Toggle progress panel visibility + const toggleProgressPanel = () => { + setShowProgressPanel((prev) => !prev); + }; + + const setQuestionRef = + (questionId: number) => (el: HTMLDivElement | null) => { + questionRefs.current[questionId] = el; + }; + + return ( +
+

{questionnaire.title}

+ + + + + +
+ {currentQuestions.map((question) => ( + + ))} +
+ + + +
+ ); +} diff --git a/components/questionnaire/test/private/ADHDCalculator.tsx b/components/questionnaire/test/private/ADHDCalculator.tsx new file mode 100644 index 0000000..61c332b --- /dev/null +++ b/components/questionnaire/test/private/ADHDCalculator.tsx @@ -0,0 +1,89 @@ +import { QuestionType } from "@/types"; + +/** + * ADHD Self-Report Scale calculator parameters interface + */ +interface ADHDCalculatorProps { + /** Answer data, key is question ID, value is selected score */ + answers: { [key: number]: string }; + /** Questions list */ + questions: QuestionType[]; +} + +/** + * Calculate ADHD Self-Report Scale (ASRS-v1.1) results + * + * @param answers - User answer data, containing question ID and selected score + * @returns Calculation results, including total score, factor scores, severity level and other information + */ +export const calculateADHDResults = ({ answers }: ADHDCalculatorProps): any => { + // ADHD ASRS calculation logic + let totalScore = 0; + + // Subscale score initialization: inattention and hyperactivity-impulsivity + let inattentionScore = 0; // Inattention subscale score (questions 1-9) + let hyperactivityScore = 0; // Hyperactivity-Impulsivity subscale score (questions 10-18) + + // Part A screening questions (questions 1-6) - critical for screening + let partAScore = 0; + let partAPositive = 0; // Count of positive responses in Part A + + // Calculate total score and subscale scores + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + totalScore += scoreValue; + + // Part A screening questions (questions 1-6) + if (questionNum >= 1 && questionNum <= 6) { + partAScore += scoreValue; + // Part A positive criteria: questions 1-4 score ≥2, questions 5-6 score ≥3 + if ((questionNum >= 1 && questionNum <= 4 && scoreValue >= 2) || + (questionNum >= 5 && questionNum <= 6 && scoreValue >= 3)) { + partAPositive++; + } + } + + // Inattention subscale (questions 1-9) + if (questionNum >= 1 && questionNum <= 9) { + inattentionScore += scoreValue; + } + // Hyperactivity-Impulsivity subscale (questions 10-18) + else if (questionNum >= 10 && questionNum <= 18) { + hyperactivityScore += scoreValue; + } + }); + + // Determine screening result based on Part A + const screeningPositive = partAPositive >= 4; + + // Determine severity level based on total score + let severity = "low"; + if (totalScore >= 24 && totalScore <= 36) { + severity = "mild"; + } else if (totalScore >= 37 && totalScore <= 54) { + severity = "moderate"; + } else if (totalScore >= 55) { + severity = "high"; + } + + // Calculate factor scores + const factorScores: { [key: string]: number } = { + "inattention": inattentionScore, + "hyperactivity": hyperactivityScore, + "partA": partAScore + }; + + // Return complete calculation results + return { + totalScore, + factorScores, + positiveItemCount: Object.values(answers).filter(score => parseInt(score) >= 2).length, + positiveItemAverage: totalScore / Object.keys(answers).length, + isSevere: severity === "high", + severity, + screeningPositive, + partAPositive, + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/AttachmentCalculator.tsx b/components/questionnaire/test/private/AttachmentCalculator.tsx new file mode 100644 index 0000000..bbf1dee --- /dev/null +++ b/components/questionnaire/test/private/AttachmentCalculator.tsx @@ -0,0 +1,40 @@ +const avoidanceItems = [1, 2, 3, 4, 5, 6]; +const anxietyItems = [7, 8, 9]; +const reverseAvoidanceItems = new Set([1, 2, 3, 4]); + +function scoreItem(answers: string[], item: number) { + const raw = Number(answers[item - 1] || 1); + return reverseAvoidanceItems.has(item) ? 8 - raw : raw; +} + +function average(values: number[]) { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +export function calculateAttachmentResults(answers: string[]) { + const avoidance = average(avoidanceItems.map((item) => scoreItem(answers, item))); + const anxiety = average(anxietyItems.map((item) => Number(answers[item - 1] || 1))); + + let pattern: + | 'secure' + | 'preoccupied' + | 'dismissive' + | 'fearful' = 'secure'; + + const highAvoidance = avoidance >= 4; + const highAnxiety = anxiety >= 4; + + if (highAvoidance && highAnxiety) { + pattern = 'fearful'; + } else if (highAvoidance) { + pattern = 'dismissive'; + } else if (highAnxiety) { + pattern = 'preoccupied'; + } + + return { + avoidance, + anxiety, + pattern, + }; +} diff --git a/components/questionnaire/test/private/BDI2Calculator.tsx b/components/questionnaire/test/private/BDI2Calculator.tsx new file mode 100644 index 0000000..3d83fd0 --- /dev/null +++ b/components/questionnaire/test/private/BDI2Calculator.tsx @@ -0,0 +1,82 @@ +import { QuestionType } from "@/types"; + +interface BDI2CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateBDI2Results = ({ answers }: BDI2CalculatorProps): any => { + // BDI-II calculation logic + let totalScore = 0; + let suicidalIdeation = false; + + // Calculate total score (simple sum) + Object.entries(answers).forEach(([questionId, score]) => { + const scoreValue = parseInt(score); + totalScore += scoreValue; + + // Check question 9 (suicidal ideation) + if (parseInt(questionId) === 9 && scoreValue >= 1) { + suicidalIdeation = true; + } + }); + + // Determine depression severity level + let severity = "minimal"; + if (totalScore >= 29) { + severity = "severe"; + } else if (totalScore >= 20) { + severity = "moderate"; + } else if (totalScore >= 14) { + severity = "mild"; + } + + // Analyze item scores + const itemAnalysis = Object.entries(answers).map(([questionId, score]) => ({ + questionId: parseInt(questionId), + score: parseInt(score), + isHigh: parseInt(score) >= 2 // Scores of 2 or above are considered high score items + })); + + const highScoreItems = itemAnalysis.filter(item => item.isHigh); + + // Categorize analysis of different symptom domains + const emotionalItems = [1, 2, 4, 5, 10, 14]; // Emotional symptoms + const cognitiveItems = [3, 6, 8, 13, 19]; // Cognitive symptoms + const somaticItems = [15, 16, 18, 20, 21]; // Somatic symptoms + const behavioralItems = [7, 9, 11, 12, 17]; // Behavioral symptoms + + const getSubscaleScore = (items: number[]) => { + return items.reduce((sum, itemId) => { + return sum + (answers[itemId] ? parseInt(answers[itemId]) : 0); + }, 0); + }; + + const emotionalScore = getSubscaleScore(emotionalItems); + const cognitiveScore = getSubscaleScore(cognitiveItems); + const somaticScore = getSubscaleScore(somaticItems); + const behavioralScore = getSubscaleScore(behavioralItems); + + return { + totalScore, + severity, + suicidalIdeation, + itemAnalysis, + highScoreItemCount: highScoreItems.length, + emotionalScore, + cognitiveScore, + somaticScore, + behavioralScore, + factorScores: { + "emotional": emotionalScore, + "cognitive": cognitiveScore, + "somatic": somaticScore, + "behavioral": behavioralScore + }, + positiveItemCount: highScoreItems.length, + positiveItemAverage: highScoreItems.length > 0 + ? highScoreItems.reduce((sum, item) => sum + item.score, 0) / highScoreItems.length + : 0, + isSevere: severity === "severe" || suicidalIdeation + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/BigFiveCalculator.tsx b/components/questionnaire/test/private/BigFiveCalculator.tsx new file mode 100644 index 0000000..f81826c --- /dev/null +++ b/components/questionnaire/test/private/BigFiveCalculator.tsx @@ -0,0 +1,85 @@ +import { + BigFiveDomain, + IpipNeoItem, + ipipNeo120Items, + ipipNeo300Items, +} from '@/questionairies/bigfive/neo-data'; + +const reverseItems = new Set([ + 2, 4, 6, 8, 10, 12, 16, 18, 19, 20, 22, 24, 26, 28, 29, 30, 32, 34, + 36, 38, 39, 44, 46, 48, 49, +]); + +const dimensions = { + extraversion: [1, 6, 11, 16, 21, 26, 31, 36, 41, 46], + agreeableness: [2, 7, 12, 17, 22, 27, 32, 37, 42, 47], + conscientiousness: [3, 8, 13, 18, 23, 28, 33, 38, 43, 48], + emotionalStability: [4, 9, 14, 19, 24, 29, 34, 39, 44, 49], + openness: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50], +}; + +export function calculateBigFiveResults(answers: string[]) { + const scoreItem = (questionId: number) => { + const raw = Number(answers[questionId - 1] || 0); + if (!raw) return 0; + return reverseItems.has(questionId) ? 6 - raw : raw; + }; + + return Object.fromEntries( + Object.entries(dimensions).map(([key, items]) => { + const score = items.reduce((sum, item) => sum + scoreItem(item), 0); + return [ + key, + { + score, + average: score / items.length, + }, + ]; + }), + ); +} + +export const ipipNeoItemsByVersion = { + 120: ipipNeo120Items, + 300: ipipNeo300Items, +} as const; + +export function calculateIpipNeoResults( + answers: string[], + items: IpipNeoItem[], +) { + const domainTotals = new Map(); + const facetTotals = new Map(); + + items.forEach((item, index) => { + const raw = Number(answers[index] || 0); + const score = raw ? (item.reverse ? 6 - raw : raw) : 0; + + const domain = domainTotals.get(item.domain) || { score: 0, count: 0 }; + domain.score += score; + domain.count += 1; + domainTotals.set(item.domain, domain); + + const facet = facetTotals.get(item.facet) || { score: 0, count: 0 }; + facet.score += score; + facet.count += 1; + facetTotals.set(item.facet, facet); + }); + + const toResults = (totals: Map) => + Object.fromEntries( + [...totals.entries()].map(([key, value]) => [ + key, + { + score: value.score, + average: value.count ? value.score / value.count : 0, + itemCount: value.count, + }, + ]), + ); + + return { + domains: toResults(domainTotals), + facets: toResults(facetTotals), + }; +} diff --git a/components/questionnaire/test/private/CRTCalculator.tsx b/components/questionnaire/test/private/CRTCalculator.tsx new file mode 100644 index 0000000..cc9b94e --- /dev/null +++ b/components/questionnaire/test/private/CRTCalculator.tsx @@ -0,0 +1,29 @@ +const correctAnswers: Record = { + 1: 'B', + 2: 'A', + 3: 'B', + 4: 'A', + 5: 'B', + 6: 'B', + 7: 'B', +}; + +export function calculateCRTResults(answers: string[]) { + const items = answers.map((answer, index) => { + const questionId = index + 1; + return { + questionId, + selected: answer, + correct: correctAnswers[questionId], + isCorrect: answer === correctAnswers[questionId], + }; + }); + + const score = items.filter((item) => item.isCorrect).length; + + return { + score, + total: Object.keys(correctAnswers).length, + items, + }; +} diff --git a/components/questionnaire/test/private/CareerAnchorsCalculator.tsx b/components/questionnaire/test/private/CareerAnchorsCalculator.tsx new file mode 100644 index 0000000..af557f2 --- /dev/null +++ b/components/questionnaire/test/private/CareerAnchorsCalculator.tsx @@ -0,0 +1,55 @@ +export type CareerAnchor = + | "technical" + | "managerial" + | "autonomy" + | "security" + | "entrepreneurial" + | "service" + | "challenge" + | "lifestyle"; + +const anchorItems: Record = { + technical: [1, 2, 3, 4, 5], + managerial: [6, 7, 8, 9, 10], + autonomy: [11, 12, 13, 14, 15], + security: [16, 17, 18, 19, 20], + entrepreneurial: [21, 22, 23, 24, 25], + service: [26, 27, 28, 29, 30], + challenge: [31, 32, 33, 34, 35], + lifestyle: [36, 37, 38, 39, 40], +}; + +export const anchorNames: Record = { + technical: "技术/职能能力", + managerial: "综合管理能力", + autonomy: "自主/独立", + security: "安全/稳定", + entrepreneurial: "创业创造", + service: "服务/奉献", + challenge: "纯粹挑战", + lifestyle: "生活方式", +}; + +function average(values: number[]) { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +export function calculateCareerAnchorsResults(answers: string[]) { + const scores = Object.fromEntries( + Object.entries(anchorItems).map(([anchor, items]) => [ + anchor, + average(items.map((item) => Number(answers[item - 1] || 1))), + ]), + ) as Record; + + const ranked = (Object.keys(scores) as CareerAnchor[]) + .map((id) => ({ id, name: anchorNames[id], score: scores[id] })) + .sort((a, b) => b.score - a.score); + + return { + scores, + ranked, + primary: ranked[0], + secondary: ranked[1], + }; +} diff --git a/components/questionnaire/test/private/DASS21Calculator.tsx b/components/questionnaire/test/private/DASS21Calculator.tsx new file mode 100644 index 0000000..922c10c --- /dev/null +++ b/components/questionnaire/test/private/DASS21Calculator.tsx @@ -0,0 +1,95 @@ +import { QuestionType } from "@/types"; + +interface DASS21CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateDASS21Results = ({ answers }: DASS21CalculatorProps): any => { + // DASS-21 calculation logic + // Depression dimension items: 3, 5, 10, 13, 16, 17, 21 + // Anxiety dimension items: 2, 4, 7, 9, 15, 19, 20 + // Stress dimension items: 1, 6, 8, 11, 12, 14, 18 + + const depressionItems = [3, 5, 10, 13, 16, 17, 21]; + const anxietyItems = [2, 4, 7, 9, 15, 19, 20]; + const stressItems = [1, 6, 8, 11, 12, 14, 18]; + + let depressionScore = 0; + let anxietyScore = 0; + let stressScore = 0; + let totalScore = 0; + + // Calculate dimension scores + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + totalScore += scoreValue; + + if (depressionItems.includes(questionNum)) { + depressionScore += scoreValue; + } else if (anxietyItems.includes(questionNum)) { + anxietyScore += scoreValue; + } else if (stressItems.includes(questionNum)) { + stressScore += scoreValue; + } + }); + + // DASS-21 scores need to be multiplied by 2 for comparison with DASS-42 + const finalDepressionScore = depressionScore * 2; + const finalAnxietyScore = anxietyScore * 2; + const finalStressScore = stressScore * 2; + + // Determine severity level for each dimension + const getDepressionSeverity = (score: number) => { + if (score <= 9) return "normal"; + if (score <= 13) return "mild"; + if (score <= 20) return "moderate"; + if (score <= 27) return "severe"; + return "extremely_severe"; + }; + + const getAnxietySeverity = (score: number) => { + if (score <= 7) return "normal"; + if (score <= 9) return "mild"; + if (score <= 14) return "moderate"; + if (score <= 19) return "severe"; + return "extremely_severe"; + }; + + const getStressSeverity = (score: number) => { + if (score <= 14) return "normal"; + if (score <= 18) return "mild"; + if (score <= 25) return "moderate"; + if (score <= 33) return "severe"; + return "extremely_severe"; + }; + + const depressionSeverity = getDepressionSeverity(finalDepressionScore); + const anxietySeverity = getAnxietySeverity(finalAnxietyScore); + const stressSeverity = getStressSeverity(finalStressScore); + + // Determine overall severity level + const isAnySevere = depressionSeverity === "severe" || depressionSeverity === "extremely_severe" || + anxietySeverity === "severe" || anxietySeverity === "extremely_severe" || + stressSeverity === "severe" || stressSeverity === "extremely_severe"; + + return { + totalScore, + depressionScore: finalDepressionScore, + anxietyScore: finalAnxietyScore, + stressScore: finalStressScore, + depressionSeverity, + anxietySeverity, + stressSeverity, + factorScores: { + "depression": finalDepressionScore, + "anxiety": finalAnxietyScore, + "stress": finalStressScore + }, + positiveItemCount: Object.values(answers).filter(score => parseInt(score) >= 2).length, + positiveItemAverage: totalScore / Object.keys(answers).length, + isSevere: isAnySevere + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/DarkTriadCalculator.tsx b/components/questionnaire/test/private/DarkTriadCalculator.tsx new file mode 100644 index 0000000..b3caab5 --- /dev/null +++ b/components/questionnaire/test/private/DarkTriadCalculator.tsx @@ -0,0 +1,28 @@ +const dimensions = { + machiavellianism: [1, 2, 3, 4, 5, 6, 7, 8, 9], + narcissism: [10, 11, 12, 13, 14, 15, 16, 17, 18], + psychopathy: [19, 20, 21, 22, 23, 24, 25, 26, 27], +} as const; + +const reverseItems = new Set([4, 8, 11, 15, 17, 20, 25]); + +function scoreItem(answers: string[], item: number) { + const raw = Number(answers[item - 1] || 1); + return reverseItems.has(item) ? 6 - raw : raw; +} + +function average(scores: number[]) { + return scores.reduce((sum, score) => sum + score, 0) / scores.length; +} + +function dimensionAverage(answers: string[], items: readonly number[]) { + return average(items.map((item) => scoreItem(answers, item))); +} + +export function calculateDarkTriadResults(answers: string[]) { + return { + machiavellianism: dimensionAverage(answers, dimensions.machiavellianism), + narcissism: dimensionAverage(answers, dimensions.narcissism), + psychopathy: dimensionAverage(answers, dimensions.psychopathy), + }; +} diff --git a/components/questionnaire/test/private/EmpathyCalculator.tsx b/components/questionnaire/test/private/EmpathyCalculator.tsx new file mode 100644 index 0000000..715b8bb --- /dev/null +++ b/components/questionnaire/test/private/EmpathyCalculator.tsx @@ -0,0 +1,30 @@ +const subscales = { + perspectiveTaking: [1, 2, 3, 4, 5, 6, 7], + empathicConcern: [8, 9, 10, 11, 12, 13, 14], + personalDistress: [15, 16, 17, 18, 19, 20, 21], + fantasy: [22, 23, 24, 25, 26, 27, 28], +} as const; + +const reverseItems = new Set([2, 4, 6, 10, 13, 17, 20, 24, 26, 28]); + +function scoreItem(answers: string[], item: number) { + const raw = Number(answers[item - 1] || 1); + return reverseItems.has(item) ? 6 - raw : raw; +} + +function average(scores: number[]) { + return scores.reduce((sum, score) => sum + score, 0) / scores.length; +} + +function subscaleAverage(answers: string[], items: readonly number[]) { + return average(items.map((item) => scoreItem(answers, item))); +} + +export function calculateEmpathyResults(answers: string[]) { + return { + perspectiveTaking: subscaleAverage(answers, subscales.perspectiveTaking), + empathicConcern: subscaleAverage(answers, subscales.empathicConcern), + personalDistress: subscaleAverage(answers, subscales.personalDistress), + fantasy: subscaleAverage(answers, subscales.fantasy), + }; +} diff --git a/components/questionnaire/test/private/FisherCalculator.tsx b/components/questionnaire/test/private/FisherCalculator.tsx new file mode 100644 index 0000000..2b49f75 --- /dev/null +++ b/components/questionnaire/test/private/FisherCalculator.tsx @@ -0,0 +1,44 @@ +const dimensions = { + explorer: [1, 5, 9, 13, 17, 21, 25, 29, 33, 37], + builder: [2, 6, 10, 14, 18, 22, 26, 30, 34, 38], + director: [3, 7, 11, 15, 19, 23, 27, 31, 35, 39], + negotiator: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40], +} as const; + +export type FisherDimension = keyof typeof dimensions; + +const dimensionNames: Record = { + explorer: "探索者", + builder: "建设者", + director: "指挥者", + negotiator: "协商者", +}; + +function scoreDimension(answers: string[], items: readonly number[]) { + return items.reduce((sum, item) => sum + Number(answers[item - 1] || 1), 0); +} + +export function calculateFisherResults(answers: string[]) { + const scores = Object.fromEntries( + Object.entries(dimensions).map(([dimension, items]) => [ + dimension, + scoreDimension(answers, items), + ]), + ) as Record; + + const ranked = (Object.keys(scores) as FisherDimension[]) + .map((id) => ({ + id, + name: dimensionNames[id], + score: scores[id], + percentage: Math.round(((scores[id] - 10) / 30) * 100), + })) + .sort((a, b) => b.score - a.score); + + return { + scores, + ranked, + primary: ranked[0], + secondary: ranked[1], + }; +} diff --git a/components/questionnaire/test/private/GAD7Calculator.tsx b/components/questionnaire/test/private/GAD7Calculator.tsx new file mode 100644 index 0000000..7aea87f --- /dev/null +++ b/components/questionnaire/test/private/GAD7Calculator.tsx @@ -0,0 +1,49 @@ +import { QuestionType } from "@/types"; + +interface GAD7CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateGAD7Results = ({ answers }: GAD7CalculatorProps): any => { + // GAD-7 calculation logic + let totalScore = 0; + + // Calculate total score (simple sum) + Object.entries(answers).forEach(([, score]) => { + const scoreValue = parseInt(score); + totalScore += scoreValue; + }); + + // Determine anxiety severity level + let severity = "minimal"; + if (totalScore >= 15) { + severity = "severe"; + } else if (totalScore >= 10) { + severity = "moderate"; + } else if (totalScore >= 5) { + severity = "mild"; + } + + // Analyze item scores + const itemAnalysis = Object.entries(answers).map(([questionId, score]) => ({ + questionId: parseInt(questionId), + score: parseInt(score), + isHigh: parseInt(score) >= 2 // Scores of 2 or above are considered high score items + })); + + const highScoreItems = itemAnalysis.filter(item => item.isHigh); + + return { + totalScore, + severity, + itemAnalysis, + highScoreItemCount: highScoreItems.length, + factorScores: {}, // GAD-7 is a single-factor scale + positiveItemCount: highScoreItems.length, + positiveItemAverage: highScoreItems.length > 0 + ? highScoreItems.reduce((sum, item) => sum + item.score, 0) / highScoreItems.length + : 0, + isSevere: severity === "severe" || severity === "moderate" + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/GDCalculator.tsx b/components/questionnaire/test/private/GDCalculator.tsx new file mode 100644 index 0000000..19c7909 --- /dev/null +++ b/components/questionnaire/test/private/GDCalculator.tsx @@ -0,0 +1,109 @@ +import { QuestionType } from "@/types"; + +/** + * Gender Dysphoria Questionnaire calculator parameters interface + */ +interface GDCalculatorProps { + /** Answer data, key is question ID, value is selected score */ + answers: { [key: number]: string }; + /** Questions list */ + questions: QuestionType[]; +} + +/** + * Calculate Gender Dysphoria Questionnaire (GDQ) results + * + * @param answers - User answer data, containing question ID and selected score + * @returns Calculation results, including total score, factor scores, and other information + */ +export const calculateGDResults = ({ answers }: GDCalculatorProps): any => { + // Gender Dysphoria calculation logic + let totalRawScore = 0; + + // Reverse-scored items (questions where agreement indicates comfort with assigned gender) + const reverseItems = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + + // Calculate total score with reverse scoring + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + if (reverseItems.includes(questionNum)) { + // Reverse score: 7 becomes 1, 6 becomes 2, etc. + totalRawScore += (8 - scoreValue); + } else { + totalRawScore += scoreValue; + } + }); + + // Calculate factor scores based on conceptual dimensions + let genderIdentityScore = 0; // Questions about internal gender identity + let socialRoleScore = 0; // Questions about social gender roles + let physicalDysphoriaScore = 0; // Questions about physical characteristics + let genderExpressionScore = 0; // Questions about gender expression + + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + let scoreValue = parseInt(score); + + // Apply reverse scoring if needed + if (reverseItems.includes(questionNum)) { + scoreValue = 8 - scoreValue; + } + + // Assign to factor scores based on question content + if ([2, 4, 6, 17, 18, 20, 22, 24, 27].includes(questionNum)) { + genderIdentityScore += scoreValue; + } else if ([5, 7, 12, 19, 21, 26].includes(questionNum)) { + socialRoleScore += scoreValue; + } else if ([3, 9, 10, 13, 15, 16].includes(questionNum)) { + physicalDysphoriaScore += scoreValue; + } else if ([1, 8, 11, 14, 23, 25].includes(questionNum)) { + genderExpressionScore += scoreValue; + } + }); + + // Determine general interpretation level + const totalPossibleScore = Object.keys(answers).length * 7; + const scorePercentage = (totalRawScore / totalPossibleScore) * 100; + + let interpretation = "low"; + if (scorePercentage >= 30 && scorePercentage < 50) { + interpretation = "mild"; + } else if (scorePercentage >= 50 && scorePercentage < 70) { + interpretation = "moderate"; + } else if (scorePercentage >= 70) { + interpretation = "high"; + } + + // Calculate factor scores + const factorScores: { [key: string]: number } = { + "genderIdentity": genderIdentityScore, + "socialRole": socialRoleScore, + "physicalDysphoria": physicalDysphoriaScore, + "genderExpression": genderExpressionScore + }; + + // Count items with scores above neutral (>4 after reverse scoring) + const elevatedItems = Object.entries(answers).filter(([questionId, score]) => { + const questionNum = parseInt(questionId); + let scoreValue = parseInt(score); + + if (reverseItems.includes(questionNum)) { + scoreValue = 8 - scoreValue; + } + + return scoreValue > 4; + }).length; + + // Return complete calculation results + return { + totalScore: totalRawScore, + factorScores, + positiveItemCount: elevatedItems, + positiveItemAverage: totalRawScore / Object.keys(answers).length, + isSevere: interpretation === "high", + interpretation, + scorePercentage: Math.round(scorePercentage), + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/GritCalculator.tsx b/components/questionnaire/test/private/GritCalculator.tsx new file mode 100644 index 0000000..783d40a --- /dev/null +++ b/components/questionnaire/test/private/GritCalculator.tsx @@ -0,0 +1,40 @@ +const reverseItems = new Set([1, 3, 5, 6]); +const perseveranceItems = [2, 4, 7, 8]; +const consistencyItems = [1, 3, 5, 6]; + +function scoreItem(answers: string[], item: number) { + const raw = Number(answers[item - 1] || 1); + return reverseItems.has(item) ? 6 - raw : raw; +} + +function average(values: number[]) { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +export function calculateGritResults(answers: string[]) { + const itemScores = Array.from({ length: 8 }, (_, index) => + scoreItem(answers, index + 1), + ); + const totalAverage = average(itemScores); + const perseveranceAverage = average( + perseveranceItems.map((item) => scoreItem(answers, item)), + ); + const consistencyAverage = average( + consistencyItems.map((item) => scoreItem(answers, item)), + ); + + let level: 'low' | 'moderate' | 'high' = 'moderate'; + if (totalAverage < 3) { + level = 'low'; + } else if (totalAverage >= 4) { + level = 'high'; + } + + return { + itemScores, + totalAverage, + perseveranceAverage, + consistencyAverage, + level, + }; +} diff --git a/components/questionnaire/test/private/HEXACOCalculator.tsx b/components/questionnaire/test/private/HEXACOCalculator.tsx new file mode 100644 index 0000000..c0671eb --- /dev/null +++ b/components/questionnaire/test/private/HEXACOCalculator.tsx @@ -0,0 +1,41 @@ +const domains = { + honestyHumility: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + emotionality: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + extraversion: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], + agreeableness: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], + conscientiousness: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], + openness: [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], +} as const; + +const reverseItems = new Set([ + 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, + 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, + 42, 44, 46, 48, 50, + 52, 54, 56, 58, 60, +]); + +function scoreItem(answers: string[], item: number) { + const raw = Number(answers[item - 1] || 1); + return reverseItems.has(item) ? 6 - raw : raw; +} + +function average(scores: number[]) { + return scores.reduce((sum, score) => sum + score, 0) / scores.length; +} + +function domainAverage(answers: string[], items: readonly number[]) { + return average(items.map((item) => scoreItem(answers, item))); +} + +export function calculateHEXACOResults(answers: string[]) { + return { + honestyHumility: domainAverage(answers, domains.honestyHumility), + emotionality: domainAverage(answers, domains.emotionality), + extraversion: domainAverage(answers, domains.extraversion), + agreeableness: domainAverage(answers, domains.agreeableness), + conscientiousness: domainAverage(answers, domains.conscientiousness), + openness: domainAverage(answers, domains.openness), + }; +} diff --git a/components/questionnaire/test/private/ISICalculator.tsx b/components/questionnaire/test/private/ISICalculator.tsx new file mode 100644 index 0000000..4b687e4 --- /dev/null +++ b/components/questionnaire/test/private/ISICalculator.tsx @@ -0,0 +1,49 @@ +import { QuestionType } from "@/types"; + +interface ISICalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateISIResults = ({ answers }: ISICalculatorProps): any => { + // ISI calculation logic + let totalScore = 0; + + // Calculate total score (simple sum) + Object.entries(answers).forEach(([, score]) => { + const scoreValue = parseInt(score); + totalScore += scoreValue; + }); + + // Determine insomnia severity level + let severity = "no_insomnia"; + if (totalScore >= 22) { + severity = "severe"; + } else if (totalScore >= 15) { + severity = "moderate"; + } else if (totalScore >= 8) { + severity = "subthreshold"; + } + + // Analyze item scores + const itemAnalysis = Object.entries(answers).map(([questionId, score]) => ({ + questionId: parseInt(questionId), + score: parseInt(score), + isHigh: parseInt(score) >= 3 // Scores of 3 or above are considered high score items + })); + + const highScoreItems = itemAnalysis.filter(item => item.isHigh); + + return { + totalScore, + severity, + itemAnalysis, + highScoreItemCount: highScoreItems.length, + factorScores: {}, + positiveItemCount: highScoreItems.length, + positiveItemAverage: highScoreItems.length > 0 + ? highScoreItems.reduce((sum, item) => sum + item.score, 0) / highScoreItems.length + : 0, + isSevere: severity === "severe" || severity === "moderate" + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/MaximizerCalculator.tsx b/components/questionnaire/test/private/MaximizerCalculator.tsx new file mode 100644 index 0000000..443d6f9 --- /dev/null +++ b/components/questionnaire/test/private/MaximizerCalculator.tsx @@ -0,0 +1,32 @@ +const highStandardItems = [1, 4, 5, 10, 13]; +const searchItems = [2, 3, 6, 8, 9, 12]; +const difficultyItems = [7, 11]; + +function average(scores: number[]) { + return scores.reduce((sum, score) => sum + score, 0) / scores.length; +} + +function scoreItems(answers: string[], items: number[]) { + return average(items.map((item) => Number(answers[item - 1] || 1))); +} + +export function calculateMaximizerResults(answers: string[]) { + const itemScores = answers.slice(0, 13).map((answer) => Number(answer || 1)); + const averageScore = average(itemScores); + + let level: 'satisficer' | 'balanced' | 'maximizer' = 'balanced'; + if (averageScore < 3.25) { + level = 'satisficer'; + } else if (averageScore >= 4.75) { + level = 'maximizer'; + } + + return { + itemScores, + average: averageScore, + highStandards: scoreItems(answers, highStandardItems), + search: scoreItems(answers, searchItems), + difficulty: scoreItems(answers, difficultyItems), + level, + }; +} diff --git a/components/questionnaire/test/private/NPDCalculator.tsx b/components/questionnaire/test/private/NPDCalculator.tsx new file mode 100644 index 0000000..d499835 --- /dev/null +++ b/components/questionnaire/test/private/NPDCalculator.tsx @@ -0,0 +1,92 @@ +import { QuestionType } from "@/types"; + +/** + * Narcissistic Personality Inventory calculator parameters interface + */ +interface NPDCalculatorProps { + /** Answer data, key is question ID, value is selected score */ + answers: { [key: number]: string }; + /** Questions list */ + questions: QuestionType[]; +} + +/** + * Calculate Narcissistic Personality Inventory (NPI-16) results + * + * @param answers - User answer data, containing question ID and selected score + * @returns Calculation results, including total score, factor scores, severity level and other information + */ +export const calculateNPDResults = ({ answers }: NPDCalculatorProps): any => { + // NPI-16 calculation logic + let totalScore = 0; + + // Subscale score initialization based on NPI factors + let leadershipAuthorityScore = 0; // Leadership/Authority subscale + let grandioseExhibitionismScore = 0; // Grandiose Exhibitionism subscale + let entitlementScore = 0; // Entitlement subscale + + // Calculate total score and subscale scores + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + totalScore += scoreValue; + + // Assign to subscales based on NPI-16 factor structure + // Leadership/Authority: questions 1, 8, 10, 11, 12 + if ([1, 8, 10, 11, 12].includes(questionNum)) { + leadershipAuthorityScore += scoreValue; + } + // Grandiose Exhibitionism: questions 2, 3, 7, 15 + else if ([2, 3, 7, 15].includes(questionNum)) { + grandioseExhibitionismScore += scoreValue; + } + // Entitlement: questions 4, 5, 6, 9, 13, 14, 16 + else if ([4, 5, 6, 9, 13, 14, 16].includes(questionNum)) { + entitlementScore += scoreValue; + } + }); + + // Determine interpretation level based on total score + // Based on research, average scores typically range from 2-8 in general population + let interpretation = "low"; + if (totalScore >= 4 && totalScore <= 7) { + interpretation = "average"; + } else if (totalScore >= 8 && totalScore <= 11) { + interpretation = "above_average"; + } else if (totalScore >= 12) { + interpretation = "high"; + } + + // Calculate percentile approximation + let percentile = 0; + if (totalScore <= 2) percentile = 25; + else if (totalScore <= 5) percentile = 50; + else if (totalScore <= 8) percentile = 75; + else if (totalScore <= 11) percentile = 90; + else percentile = 95; + + // Calculate factor scores + const factorScores: { [key: string]: number } = { + "leadership": leadershipAuthorityScore, + "exhibitionism": grandioseExhibitionismScore, + "entitlement": entitlementScore + }; + + // Identify dominant traits + const dominantTrait = Object.entries(factorScores).reduce((a, b) => + factorScores[a[0]] > factorScores[b[0]] ? a : b + )[0]; + + // Return complete calculation results + return { + totalScore, + factorScores, + positiveItemCount: totalScore, // For NPI, each item is either 0 or 1 + positiveItemAverage: totalScore / Object.keys(answers).length, + isSevere: interpretation === "high", + interpretation, + percentile, + dominantTrait, + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/NeedForCognitionCalculator.tsx b/components/questionnaire/test/private/NeedForCognitionCalculator.tsx new file mode 100644 index 0000000..8f15043 --- /dev/null +++ b/components/questionnaire/test/private/NeedForCognitionCalculator.tsx @@ -0,0 +1,39 @@ +const reverseItems = new Set([3, 4, 5, 7, 8, 9, 12, 16, 17]); + +export function calculateNeedForCognitionResults(answers: string[]) { + const itemScores = answers.slice(0, 18).map((answer, index) => { + const raw = Number(answer || 1); + const item = index + 1; + return reverseItems.has(item) ? 6 - raw : raw; + }); + + const total = itemScores.reduce((sum, score) => sum + score, 0); + const average = total / itemScores.length; + + let level: 'low' | 'moderate' | 'high' = 'moderate'; + if (average < 3) { + level = 'low'; + } else if (average >= 4) { + level = 'high'; + } + + const thinkingItems = [1, 2, 6, 10, 11, 18].map( + (item) => itemScores[item - 1], + ); + const challengeItems = [3, 4, 5, 7, 8, 9, 12, 13, 14, 15, 16, 17].map( + (item) => itemScores[item - 1], + ); + + return { + itemScores, + total, + average, + thinkingAverage: + thinkingItems.reduce((sum, score) => sum + score, 0) / + thinkingItems.length, + challengeAverage: + challengeItems.reduce((sum, score) => sum + score, 0) / + challengeItems.length, + level, + }; +} diff --git a/components/questionnaire/test/private/OEPSCalculator.tsx b/components/questionnaire/test/private/OEPSCalculator.tsx new file mode 100644 index 0000000..6dc3d5e --- /dev/null +++ b/components/questionnaire/test/private/OEPSCalculator.tsx @@ -0,0 +1,97 @@ +type EnneagramType = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + +const directKeys: Record = { + 1: 1, 10: 1, 19: 1, 28: 1, + 2: 2, 11: 2, 20: 2, 29: 2, + 3: 3, 12: 3, 21: 3, 30: 3, + 4: 4, 13: 4, 22: 4, 31: 4, + 5: 5, 14: 5, 23: 5, 32: 5, + 6: 6, 15: 6, 24: 6, 33: 6, + 7: 7, 16: 7, 25: 7, 34: 7, + 8: 8, 17: 8, 26: 8, 35: 8, + 9: 9, 18: 9, 27: 9, 36: 9, +}; + +const bipolarKeys: Record = { + 37: { left: [8], right: [6] }, + 38: { left: [1], right: [4, 7] }, + 39: { left: [9], right: [3] }, + 40: { left: [7], right: [5] }, + 41: { left: [1], right: [7] }, + 42: { left: [2], right: [5] }, + 43: { left: [7, 8], right: [9] }, + 44: { left: [5], right: [2, 4] }, + 45: { left: [8], right: [2, 9] }, + 46: { right: [1] }, + 47: { right: [2] }, + 48: { left: [3] }, + 49: { right: [4] }, + 50: { right: [5] }, + 51: { right: [6] }, + 52: { left: [7] }, + 53: { left: [8] }, + 54: { left: [9] }, +}; + +export const enneagramTypeInfo: Record = { + 1: { name: '1号 改革者', shortName: '改革者', description: '追求正确、负责、自律和改进。' }, + 2: { name: '2号 助人者', shortName: '助人者', description: '重视关系、照顾他人和被需要。' }, + 3: { name: '3号 成就者', shortName: '成就者', description: '重视目标、效率、表现和认可。' }, + 4: { name: '4号 自我型', shortName: '自我型', description: '重视真实、独特、情绪深度和个人意义。' }, + 5: { name: '5号 探索者', shortName: '探索者', description: '重视知识、理解、独立和能力感。' }, + 6: { name: '6号 忠诚者', shortName: '忠诚者', description: '重视安全、支持、可靠性和风险预判。' }, + 7: { name: '7号 享乐者', shortName: '享乐者', description: '重视自由、可能性、新体验和快乐。' }, + 8: { name: '8号 挑战者', shortName: '挑战者', description: '重视力量、直接、掌控和保护边界。' }, + 9: { name: '9号 调停者', shortName: '调停者', description: '重视和谐、稳定、舒适和避免冲突。' }, +}; + +function addScore( + scores: Record, + type: EnneagramType, + value: number, +) { + scores[type].total += value; + scores[type].count += 1; +} + +export function calculateOEPSResults(answers: string[]) { + const scores = Object.fromEntries( + ([1, 2, 3, 4, 5, 6, 7, 8, 9] as EnneagramType[]).map((type) => [ + type, + { total: 0, count: 0 }, + ]), + ) as Record; + + answers.forEach((answer, index) => { + const questionId = index + 1; + const value = Number(answer || 0); + if (!value) return; + + const directType = directKeys[questionId]; + if (directType) { + addScore(scores, directType, value); + return; + } + + const key = bipolarKeys[questionId]; + if (!key) return; + + key.left?.forEach((type) => addScore(scores, type, 6 - value)); + key.right?.forEach((type) => addScore(scores, type, value)); + }); + + const ranked = (Object.entries(scores) as [string, { total: number; count: number }][]) + .map(([type, score]) => ({ + type: Number(type) as EnneagramType, + total: score.total, + count: score.count, + average: score.count ? score.total / score.count : 0, + ...enneagramTypeInfo[Number(type) as EnneagramType], + })) + .sort((a, b) => b.average - a.average); + + return { + ranked, + top: ranked[0], + }; +} diff --git a/components/questionnaire/test/private/PHQ9Calculator.tsx b/components/questionnaire/test/private/PHQ9Calculator.tsx new file mode 100644 index 0000000..1b6cda4 --- /dev/null +++ b/components/questionnaire/test/private/PHQ9Calculator.tsx @@ -0,0 +1,64 @@ +import { QuestionType } from "@/types"; + +interface PHQ9CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculatePHQ9Results = ({ answers }: PHQ9CalculatorProps): any => { + // PHQ-9 calculation logic + let totalScore = 0; + let suicidalIdeation = false; + + // Calculate total score (simple sum) + Object.entries(answers).forEach(([questionId, score]) => { + const scoreValue = parseInt(score); + totalScore += scoreValue; + + // Check question 9 (suicidal ideation) + if (parseInt(questionId) === 9 && scoreValue >= 1) { + suicidalIdeation = true; + } + }); + + // Determine depression severity level + let severity = "minimal"; + if (totalScore >= 20) { + severity = "severe"; + } else if (totalScore >= 15) { + severity = "moderately_severe"; + } else if (totalScore >= 10) { + severity = "moderate"; + } else if (totalScore >= 5) { + severity = "mild"; + } + + // Analyze item scores + const itemAnalysis = Object.entries(answers).map(([questionId, score]) => ({ + questionId: parseInt(questionId), + score: parseInt(score), + isHigh: parseInt(score) >= 2 // Scores of 2 or above are considered high score items + })); + + const highScoreItems = itemAnalysis.filter(item => item.isHigh); + + // Determine possibility of major depressive episode (at least 5 symptoms, including at least one of the first two) + const coreSymptoms = itemAnalysis.slice(0, 2).filter(item => item.score >= 2); + const otherSymptoms = itemAnalysis.slice(2).filter(item => item.score >= 2); + const majorDepressionCriteria = coreSymptoms.length >= 1 && (coreSymptoms.length + otherSymptoms.length) >= 5; + + return { + totalScore, + severity, + suicidalIdeation, + itemAnalysis, + highScoreItemCount: highScoreItems.length, + majorDepressionCriteria, + factorScores: {}, // PHQ-9 is a single-factor scale + positiveItemCount: highScoreItems.length, + positiveItemAverage: highScoreItems.length > 0 + ? highScoreItems.reduce((sum, item) => sum + item.score, 0) / highScoreItems.length + : 0, + isSevere: severity === "severe" || severity === "moderately_severe" || suicidalIdeation + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/PSS10Calculator.tsx b/components/questionnaire/test/private/PSS10Calculator.tsx new file mode 100644 index 0000000..a3ce79e --- /dev/null +++ b/components/questionnaire/test/private/PSS10Calculator.tsx @@ -0,0 +1,86 @@ +import { QuestionType } from "@/types"; + +interface PSS10CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculatePSS10Results = ({ answers }: PSS10CalculatorProps): any => { + // PSS-10 calculation logic + let totalScore = 0; + + // Reverse scoring items (4, 5, 7, 8) + const reverseItems = [4, 5, 7, 8]; + + // Calculate total score + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + if (reverseItems.includes(questionNum)) { + // Reverse scoring: 0->4, 1->3, 2->2, 3->1, 4->0 + totalScore += (4 - scoreValue); + } else { + totalScore += scoreValue; + } + }); + + // Determine stress level (based on reference values from research literature) + let severity = "low"; + if (totalScore >= 27) { + severity = "high"; + } else if (totalScore >= 14) { + severity = "moderate"; + } + + // Analyze item scores + const itemAnalysis = Object.entries(answers).map(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + const isReverse = reverseItems.includes(questionNum); + const actualScore = isReverse ? (4 - scoreValue) : scoreValue; + + return { + questionId: questionNum, + originalScore: scoreValue, + actualScore: actualScore, + isReverse: isReverse, + isHigh: actualScore >= 3 // Scores of 3 or above are considered high score items + }; + }); + + const highScoreItems = itemAnalysis.filter(item => item.isHigh); + + // Calculate subscale scores + const stressPerceptionItems = [1, 2, 3, 6, 9, 10]; // Stress perception items + const copingAbilityItems = [4, 5, 7, 8]; // Coping ability items + + let stressPerceptionScore = 0; + let copingAbilityScore = 0; + + itemAnalysis.forEach(item => { + if (stressPerceptionItems.includes(item.questionId)) { + stressPerceptionScore += item.actualScore; + } else if (copingAbilityItems.includes(item.questionId)) { + copingAbilityScore += item.actualScore; + } + }); + + return { + totalScore, + severity, + itemAnalysis, + highScoreItemCount: highScoreItems.length, + stressPerceptionScore, // Stress perception score (0-24 points) + copingAbilityScore, // Coping ability score (0-16 points) + factorScores: { + "stress_perception": stressPerceptionScore, + "coping_ability": copingAbilityScore + }, + positiveItemCount: highScoreItems.length, + positiveItemAverage: highScoreItems.length > 0 + ? highScoreItems.reduce((sum, item) => sum + item.actualScore, 0) / highScoreItems.length + : 0, + isSevere: severity === "high" + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/RIASECCalculator.tsx b/components/questionnaire/test/private/RIASECCalculator.tsx new file mode 100644 index 0000000..144b758 --- /dev/null +++ b/components/questionnaire/test/private/RIASECCalculator.tsx @@ -0,0 +1,31 @@ +export const riasecTypes = { + R: { name: '现实型', items: [1, 2, 3, 4, 5, 6, 7, 8] }, + I: { name: '研究型', items: [9, 10, 11, 12, 13, 14, 15, 16] }, + A: { name: '艺术型', items: [17, 18, 19, 20, 21, 22, 23, 24] }, + S: { name: '社会型', items: [25, 26, 27, 28, 29, 30, 31, 32] }, + E: { name: '企业型', items: [33, 34, 35, 36, 37, 38, 39, 40] }, + C: { name: '常规型', items: [41, 42, 43, 44, 45, 46, 47, 48] }, +} as const; + +export function calculateRIASECResults(answers: string[]) { + const scores = Object.fromEntries( + Object.entries(riasecTypes).map(([code, type]) => { + const score = type.items.reduce( + (sum, item) => sum + Number(answers[item - 1] || 0), + 0, + ); + return [code, { score, average: score / type.items.length }]; + }), + ); + + const ranking = Object.entries(scores).sort( + ([codeA, resultA], [codeB, resultB]) => + resultB.score - resultA.score || codeA.localeCompare(codeB), + ); + + return { + scores, + ranking, + hollandCode: ranking.slice(0, 3).map(([code]) => code).join(''), + }; +} diff --git a/components/questionnaire/test/private/SCL90Calculator.tsx b/components/questionnaire/test/private/SCL90Calculator.tsx new file mode 100644 index 0000000..d5229d4 --- /dev/null +++ b/components/questionnaire/test/private/SCL90Calculator.tsx @@ -0,0 +1,70 @@ +import { QuestionType } from "@/types"; + +interface SCL90CalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateSCL90Results = ({ answers }: SCL90CalculatorProps): any => { + // SCL-90 calculation logic + const totalScore = Object.values(answers).reduce((sum, score) => sum + parseInt(score), 0); + const positiveItemCount = Object.values(answers).filter(score => parseInt(score) >= 2).length; + const negativeItemCount = Object.values(answers).filter(score => parseInt(score) === 1).length; + const positiveItemAverage = positiveItemCount > 0 + ? (totalScore - negativeItemCount) / positiveItemCount + : 0; + + // SCL-90 factor grouping (based on standard factor structure) + const factorMapping: { [key: string]: number[] } = { + "somatization": [1, 4, 12, 27, 40, 42, 48, 49, 52, 53, 56, 58], // Somatization + "obsessive": [3, 9, 10, 28, 38, 45, 46, 51, 55, 65], // Obsessive-compulsive + "interpersonal": [6, 21, 34, 36, 37, 41, 61, 69, 73], // Interpersonal sensitivity + "depression": [5, 14, 15, 20, 22, 26, 29, 30, 31, 32, 54, 71, 79], // Depression + "anxiety": [2, 17, 23, 33, 39, 57, 72, 78, 80, 86], // Anxiety + "hostility": [11, 24, 63, 67, 74, 81], // Hostility + "phobic": [13, 25, 47, 50, 70, 75, 82], // Phobic anxiety + "paranoid": [8, 18, 43, 68, 76, 83], // Paranoid ideation + "psychotic": [7, 16, 35, 62, 77, 84, 85, 87, 88, 90], // Psychoticism + "other": [19, 44, 59, 60, 64, 66, 89] // Other + }; + + // Calculate factor scores + const factorScores: { [key: string]: number } = {}; + + Object.entries(factorMapping).forEach(([factor, questionIndexes]) => { + let factorSum = 0; + let validQuestionCount = 0; + + questionIndexes.forEach((index: number) => { + if (answers[index]) { + factorSum += parseInt(answers[index]); + validQuestionCount++; + } + }); + + factorScores[factor] = validQuestionCount > 0 + ? factorSum / validQuestionCount + : 0; + }); + + const isSevere = totalScore > 160 || positiveItemCount > 43; + + // Determine overall severity level + let severity = "normal"; + if (totalScore >= 160 && positiveItemCount >= 43) { + severity = "severe"; + } else if (totalScore >= 120 || positiveItemCount >= 30) { + severity = "moderate"; + } else if (totalScore >= 90 || positiveItemCount >= 20) { + severity = "mild"; + } + + return { + totalScore, + factorScores, + positiveItemCount, + positiveItemAverage, + isSevere, + severity + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/SDSCalculator.tsx b/components/questionnaire/test/private/SDSCalculator.tsx new file mode 100644 index 0000000..0e0c99a --- /dev/null +++ b/components/questionnaire/test/private/SDSCalculator.tsx @@ -0,0 +1,47 @@ +import { QuestionType } from "@/types"; + +interface SDSCalculatorProps { + answers: { [key: number]: string }; + questions: QuestionType[]; +} + +export const calculateSDSResults = ({ answers }: SDSCalculatorProps): any => { + // SDS Depression Self-rating Scale calculation logic + const reverseItems = [2, 5, 6, 11, 12, 14, 16, 17, 18, 20]; // Reverse scoring items + let totalScore = 0; + + // Calculate total score + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); + const scoreValue = parseInt(score); + + if (reverseItems.includes(questionNum)) { + // Reverse scoring: 1->4, 2->3, 3->2, 4->1 + totalScore += (5 - scoreValue); + } else { + totalScore += scoreValue; + } + }); + + // Calculate standard score + const standardScore = Math.round(totalScore * 1.25); + + // Determine depression level + let severity = "normal"; + if (standardScore >= 53 && standardScore <= 62) { + severity = "mild"; + } else if (standardScore >= 63 && standardScore <= 72) { + severity = "moderate"; + } else if (standardScore > 72) { + severity = "severe"; + } + + return { + totalScore: standardScore, + factorScores: {}, + positiveItemCount: 0, + positiveItemAverage: 0, + isSevere: severity === "severe", + severity: severity + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/private/SchwartzCalculator.tsx b/components/questionnaire/test/private/SchwartzCalculator.tsx new file mode 100644 index 0000000..b1f8865 --- /dev/null +++ b/components/questionnaire/test/private/SchwartzCalculator.tsx @@ -0,0 +1,112 @@ +export type SchwartzValue = + | "selfDirection" + | "stimulation" + | "hedonism" + | "achievement" + | "power" + | "security" + | "conformity" + | "tradition" + | "benevolence" + | "universalism"; + +export type SchwartzHigherOrder = + | "opennessToChange" + | "conservation" + | "selfEnhancement" + | "selfTranscendence"; + +const valueItems: Record = { + selfDirection: [1, 11, 21], + stimulation: [2, 12, 22], + hedonism: [3, 13, 23], + achievement: [4, 14, 24], + power: [5, 15, 25], + security: [6, 16, 26], + conformity: [7, 17, 27], + tradition: [8, 18, 28], + benevolence: [9, 19, 29], + universalism: [10, 20, 30], +}; + +export const valueNames: Record = { + selfDirection: "自主", + stimulation: "刺激", + hedonism: "享乐", + achievement: "成就", + power: "权力", + security: "安全", + conformity: "遵从", + tradition: "传统", + benevolence: "仁慈", + universalism: "普世主义", +}; + +export const higherOrderNames: Record = { + opennessToChange: "开放变化", + conservation: "保守稳定", + selfEnhancement: "自我提升", + selfTranscendence: "自我超越", +}; + +function average(values: number[]) { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +function scoreItems(answers: string[], items: number[]) { + return average(items.map((item) => Number(answers[item - 1] || 1))); +} + +export function calculateSchwartzResults(answers: string[]) { + const valueScores = Object.fromEntries( + Object.entries(valueItems).map(([value, items]) => [ + value, + scoreItems(answers, items), + ]), + ) as Record; + + const higherOrderScores: Record = { + opennessToChange: average([ + valueScores.selfDirection, + valueScores.stimulation, + valueScores.hedonism, + ]), + conservation: average([ + valueScores.security, + valueScores.conformity, + valueScores.tradition, + ]), + selfEnhancement: average([ + valueScores.achievement, + valueScores.power, + valueScores.hedonism, + ]), + selfTranscendence: average([ + valueScores.benevolence, + valueScores.universalism, + ]), + }; + + const rankedValues = (Object.keys(valueScores) as SchwartzValue[]) + .map((id) => ({ + id, + name: valueNames[id], + score: valueScores[id], + })) + .sort((a, b) => b.score - a.score); + + const rankedHigherOrders = (Object.keys(higherOrderScores) as SchwartzHigherOrder[]) + .map((id) => ({ + id, + name: higherOrderNames[id], + score: higherOrderScores[id], + })) + .sort((a, b) => b.score - a.score); + + return { + valueScores, + higherOrderScores, + rankedValues, + rankedHigherOrders, + }; +} diff --git a/components/questionnaire/test/private/SelfControlCalculator.tsx b/components/questionnaire/test/private/SelfControlCalculator.tsx new file mode 100644 index 0000000..85bc6dd --- /dev/null +++ b/components/questionnaire/test/private/SelfControlCalculator.tsx @@ -0,0 +1,36 @@ +const reverseItems = new Set([2, 3, 5, 6, 7, 8, 10, 11, 12]); + +export function calculateSelfControlResults(answers: string[]) { + const itemScores = answers.slice(0, 13).map((answer, index) => { + const raw = Number(answer || 1); + const item = index + 1; + return reverseItems.has(item) ? 6 - raw : raw; + }); + + const total = itemScores.reduce((sum, score) => sum + score, 0); + const average = total / itemScores.length; + + let level: 'low' | 'moderate' | 'high' = 'moderate'; + if (average < 3) { + level = 'low'; + } else if (average >= 4) { + level = 'high'; + } + + const impulseItems = [1, 2, 5, 10, 11].map((item) => itemScores[item - 1]); + const executionItems = [3, 4, 6, 7, 8, 9, 12, 13].map( + (item) => itemScores[item - 1], + ); + + return { + itemScores, + total, + average, + impulseAverage: + impulseItems.reduce((sum, score) => sum + score, 0) / impulseItems.length, + executionAverage: + executionItems.reduce((sum, score) => sum + score, 0) / + executionItems.length, + level, + }; +} diff --git a/components/questionnaire/test/private/SelfEsteemCalculator.tsx b/components/questionnaire/test/private/SelfEsteemCalculator.tsx new file mode 100644 index 0000000..3793a63 --- /dev/null +++ b/components/questionnaire/test/private/SelfEsteemCalculator.tsx @@ -0,0 +1,24 @@ +const reverseItems = new Set([2, 5, 6, 8, 9]); + +export function calculateSelfEsteemResults(answers: string[]) { + const itemScores = answers.slice(0, 10).map((answer, index) => { + const raw = Number(answer || 0); + const item = index + 1; + return reverseItems.has(item) ? 3 - raw : raw; + }); + + const total = itemScores.reduce((sum, score) => sum + score, 0); + + let level: 'low' | 'moderate' | 'high' = 'moderate'; + if (total <= 14) { + level = 'low'; + } else if (total >= 25) { + level = 'high'; + } + + return { + total, + itemScores, + level, + }; +} diff --git a/components/questionnaire/test/private/VIACalculator.tsx b/components/questionnaire/test/private/VIACalculator.tsx new file mode 100644 index 0000000..05aa8d4 --- /dev/null +++ b/components/questionnaire/test/private/VIACalculator.tsx @@ -0,0 +1,111 @@ +export type VIAStrength = + | "creativity" | "curiosity" | "judgment" | "loveOfLearning" | "perspective" + | "bravery" | "perseverance" | "honesty" | "zest" + | "love" | "kindness" | "socialIntelligence" + | "teamwork" | "fairness" | "leadership" + | "forgiveness" | "humility" | "prudence" | "selfRegulation" + | "appreciation" | "gratitude" | "hope" | "humor" | "spirituality"; + +export type VIAVirtue = "wisdom" | "courage" | "humanity" | "justice" | "temperance" | "transcendence"; + +const strengthItems: Record = { + creativity: [1, 2], + curiosity: [3, 4], + judgment: [5, 6], + loveOfLearning: [7, 8], + perspective: [9, 10], + bravery: [11, 12], + perseverance: [13, 14], + honesty: [15, 16], + zest: [17, 18], + love: [19, 20], + kindness: [21, 22], + socialIntelligence: [23, 24], + teamwork: [25, 26], + fairness: [27, 28], + leadership: [29, 30], + forgiveness: [31, 32], + humility: [33, 34], + prudence: [35, 36], + selfRegulation: [37, 38], + appreciation: [39, 40], + gratitude: [41, 42], + hope: [43, 44], + humor: [45, 46], + spirituality: [47, 48], +}; + +const virtueStrengths: Record = { + wisdom: ["creativity", "curiosity", "judgment", "loveOfLearning", "perspective"], + courage: ["bravery", "perseverance", "honesty", "zest"], + humanity: ["love", "kindness", "socialIntelligence"], + justice: ["teamwork", "fairness", "leadership"], + temperance: ["forgiveness", "humility", "prudence", "selfRegulation"], + transcendence: ["appreciation", "gratitude", "hope", "humor", "spirituality"], +}; + +export const strengthNames: Record = { + creativity: "创造力", + curiosity: "好奇心", + judgment: "判断力", + loveOfLearning: "热爱学习", + perspective: "洞察力", + bravery: "勇敢", + perseverance: "坚持", + honesty: "真诚", + zest: "热情", + love: "爱", + kindness: "善良", + socialIntelligence: "社交智慧", + teamwork: "团队精神", + fairness: "公平", + leadership: "领导力", + forgiveness: "宽恕", + humility: "谦逊", + prudence: "审慎", + selfRegulation: "自我调节", + appreciation: "审美", + gratitude: "感恩", + hope: "希望", + humor: "幽默", + spirituality: "灵性", +}; + +export const virtueNames: Record = { + wisdom: "智慧", + courage: "勇气", + humanity: "仁爱", + justice: "正义", + temperance: "节制", + transcendence: "超越", +}; + +function average(values: number[]) { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +export function calculateVIAResults(answers: string[]) { + const strengthScores = Object.fromEntries( + Object.entries(strengthItems).map(([strength, items]) => [ + strength, + average(items.map((item) => Number(answers[item - 1] || 1))), + ]), + ) as Record; + + const virtueScores = Object.fromEntries( + Object.entries(virtueStrengths).map(([virtue, strengths]) => [ + virtue, + average(strengths.map((strength) => strengthScores[strength])), + ]), + ) as Record; + + const rankedStrengths = (Object.keys(strengthScores) as VIAStrength[]) + .map((id) => ({ id, name: strengthNames[id], score: strengthScores[id] })) + .sort((a, b) => b.score - a.score); + + const rankedVirtues = (Object.keys(virtueScores) as VIAVirtue[]) + .map((id) => ({ id, name: virtueNames[id], score: virtueScores[id] })) + .sort((a, b) => b.score - a.score); + + return { strengthScores, virtueScores, rankedStrengths, rankedVirtues }; +} diff --git a/components/questionnaire/test/private/WHO5Calculator.tsx b/components/questionnaire/test/private/WHO5Calculator.tsx new file mode 100644 index 0000000..8dd9d6d --- /dev/null +++ b/components/questionnaire/test/private/WHO5Calculator.tsx @@ -0,0 +1,32 @@ +interface WHO5CalculatorProps { + answers: { [key: number]: string }; +} + +export const calculateWHO5Results = ({ answers }: WHO5CalculatorProps) => { + const itemScores = Array.from({ length: 5 }, (_, index) => { + const questionId = index + 1; + return Number.parseInt(answers[questionId] ?? "0", 10); + }); + + const rawScore = itemScores.reduce((sum, score) => sum + score, 0); + const percentageScore = rawScore * 4; + const hasVeryLowItem = itemScores.some((score) => score <= 1); + + let level: "high" | "moderate" | "low" | "very_low" = "high"; + if (rawScore <= 7) { + level = "very_low"; + } else if (rawScore <= 12) { + level = "low"; + } else if (rawScore <= 17) { + level = "moderate"; + } + + return { + rawScore, + percentageScore, + itemScores, + hasVeryLowItem, + level, + needsAttention: rawScore <= 12 || hasVeryLowItem, + }; +}; diff --git a/components/questionnaire/test/private/YBOCSCalculator.tsx b/components/questionnaire/test/private/YBOCSCalculator.tsx new file mode 100644 index 0000000..b28ace3 --- /dev/null +++ b/components/questionnaire/test/private/YBOCSCalculator.tsx @@ -0,0 +1,77 @@ +import { QuestionType } from "@/types"; + +/** + * Yale-Brown Obsessive Compulsive Scale calculator parameters interface + */ +interface YBOCSCalculatorProps { + /** Answer data, key is question ID, value is selected score */ + answers: { [key: number]: string }; + /** Questions list */ + questions: QuestionType[]; +} + +/** + * Calculate Yale-Brown Obsessive Compulsive Scale (Y-BOCS) results + * + * @param answers - User answer data, containing question ID and selected score + * @returns Calculation results, including total score, factor scores, severity level and other information + */ +export const calculateYBOCSResults = ({ answers }: YBOCSCalculatorProps): any => { + // Y-BOCS Obsessive Compulsive Scale calculation logic + let totalScore = 0; + + // Subscale score initialization: obsessions (questions 1-5) and compulsions (questions 6-10) + let obsessionScore = 0; // Obsessions subscale score + let compulsionScore = 0; // Compulsions subscale score + + // Calculate total score and subscale scores + Object.entries(answers).forEach(([questionId, score]) => { + const questionNum = parseInt(questionId); // Question number + const scoreValue = parseInt(score); // Selected score value + + totalScore += scoreValue; // Accumulate total score + + // Subscale score calculation: distinguish between obsessions and compulsions + if (questionNum >= 1 && questionNum <= 5) { + // Obsessions subscale (questions 1-5) + obsessionScore += scoreValue; + } else if (questionNum >= 6 && questionNum <= 10) { + // Compulsions subscale (questions 6-10) + compulsionScore += scoreValue; + } + }); + + // Determine severity level of obsessive-compulsive symptoms based on total score + // 0-7: Normal + // 8-15: Mild obsessive-compulsive symptoms + // 16-23: Moderate obsessive-compulsive symptoms + // 24-31: Severe obsessive-compulsive symptoms + // 32-40: Extremely severe obsessive-compulsive symptoms + let severity = "normal"; // Default to normal + if (totalScore >= 8 && totalScore <= 15) { + severity = "mild"; // Mild obsessive-compulsive symptoms + } else if (totalScore >= 16 && totalScore <= 23) { + severity = "moderate"; // Moderate obsessive-compulsive symptoms + } else if (totalScore >= 24 && totalScore <= 31) { + severity = "severe"; // Severe obsessive-compulsive symptoms + } else if (totalScore >= 32) { + severity = "extreme"; // Extremely severe obsessive-compulsive symptoms + } + + // Calculate factor scores (obsessions subscale and compulsions subscale) + // These factor scores can be used to analyze symptom characteristics and intervention directions + const factorScores: { [key: string]: number } = { + "obsession": obsessionScore, // Obsessions score + "compulsion": compulsionScore // Compulsions score + }; + + // Return complete calculation results + return { + totalScore, // Total score + factorScores, // Factor scores (including obsessions and compulsions) + positiveItemCount: Object.values(answers).filter(score => parseInt(score) >= 1).length, // Positive item count (number of items with score ≥1) + positiveItemAverage: totalScore / Object.keys(answers).length, // Positive item average score + isSevere: severity === "severe" || severity === "extreme", // Whether it's severe or extremely severe symptoms + severity // Severity level classification + }; +}; \ No newline at end of file diff --git a/components/questionnaire/test/public/Navigation.tsx b/components/questionnaire/test/public/Navigation.tsx new file mode 100644 index 0000000..ba51f9b --- /dev/null +++ b/components/questionnaire/test/public/Navigation.tsx @@ -0,0 +1,47 @@ +'use client'; +import { useScopedI18n } from '@/locales/client'; +import { Button } from '@/components/ui/button'; + +interface NavigationProps { + currentPage: number; + totalPages: number; + goToPage: (page: number) => void; + onSubmit: () => void; + isLastPage: boolean; +} + +export function Navigation({ + currentPage, + totalPages, + goToPage, + onSubmit, + isLastPage, +}: NavigationProps) { + const t = useScopedI18n('component.questionnaire.test.public.navigation'); + return ( +
+ + + + {t('pageInfo', { currentPage, totalPages })} + + + {isLastPage ? ( + + ) : ( + + )} +
+ ); +} diff --git a/components/questionnaire/test/public/ProgressBar.tsx b/components/questionnaire/test/public/ProgressBar.tsx new file mode 100644 index 0000000..b9cb6e5 --- /dev/null +++ b/components/questionnaire/test/public/ProgressBar.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +interface ProgressBarProps { + completionPercentage: number; +} + +export function ProgressBar({ completionPercentage }: ProgressBarProps) { + return ( +
+
+
+ ); +} diff --git a/components/questionnaire/test/public/ProgressPanel.tsx b/components/questionnaire/test/public/ProgressPanel.tsx new file mode 100644 index 0000000..b32b9a4 --- /dev/null +++ b/components/questionnaire/test/public/ProgressPanel.tsx @@ -0,0 +1,94 @@ +import { useScopedI18n } from '@/locales/client'; +import { Option } from '@/types'; +interface Question { + id: number; + content: string; + options: Option[]; +} + +interface ProgressPanelProps { + questions: Question[]; + answers: { [key: number]: string }; + activePanelQuestion: number | null; + goToQuestion: (questionId: number) => void; + showProgressPanel: boolean; + toggleProgressPanel: () => void; + completionPercentage: number; +} + +export function ProgressPanel({ + questions, + answers, + activePanelQuestion, + goToQuestion, + showProgressPanel, + toggleProgressPanel, + completionPercentage, +}: ProgressPanelProps) { + const t = useScopedI18n('component.questionnaire.test.public.progressPanel'); + return ( + <> + + +
+
+
+ {t('completionProgress')} + {Math.round(completionPercentage)}% +
+
+
+
+
+ +
+ {questions.map((_, i) => ( + + ))} +
+
+ + ); +} diff --git a/components/questionnaire/test/public/Question.tsx b/components/questionnaire/test/public/Question.tsx new file mode 100644 index 0000000..d4f0810 --- /dev/null +++ b/components/questionnaire/test/public/Question.tsx @@ -0,0 +1,41 @@ +import { forwardRef } from 'react'; +import { QuestionType } from '@/types'; + +interface QuestionProps { + question: QuestionType; + answer?: string; + onSelect: (questionId: number, option: string) => void; +} + +export const Question = forwardRef( + ({ question, answer, onSelect }, ref) => { + return ( +
+

+ {question.id}. {question.content} +

+
+ {question.options.map((option) => ( + + ))} +
+
+ ); + } +); + +Question.displayName = 'Question'; \ No newline at end of file diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..83ff6f4 --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-sm border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent text-primary border border-primary [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/button.tsx b/components/ui/button.tsx new file mode 100644 index 0000000..f2a2f91 --- /dev/null +++ b/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-95", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 cursor-pointer", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..d05bbc6 --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..ec51e9c --- /dev/null +++ b/components/ui/dropdown-menu.tsx @@ -0,0 +1,257 @@ +"use client" + +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..03295ca --- /dev/null +++ b/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx new file mode 100644 index 0000000..a98cc9f --- /dev/null +++ b/components/ui/sonner.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { Toaster as Sonner, ToasterProps } from 'sonner'; + +const Toaster = ({ ...props }: ToasterProps) => { + return ( + + ); +}; + +export { Toaster }; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..499cf1d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,22 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), + ...compat.config({ + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@next/next/no-img-element": "off", + }, + }), +]; + +export default eslintConfig; diff --git a/hooks/useQuestionnaire.ts b/hooks/useQuestionnaire.ts new file mode 100644 index 0000000..c8811dc --- /dev/null +++ b/hooks/useQuestionnaire.ts @@ -0,0 +1,6 @@ +import { questionnairesZh } from '@/questionairies/zh'; + +export function useQuestionnaire(id?: string) { + if (!id) return questionnairesZh; + return questionnairesZh.find((q) => q.id === id); +} diff --git a/lib/result-storage.ts b/lib/result-storage.ts new file mode 100644 index 0000000..96e07fe --- /dev/null +++ b/lib/result-storage.ts @@ -0,0 +1,39 @@ +const RESULT_STORAGE_KEY = 'questionnaire_result'; + +interface StoredResult { + answers: string[]; + savedAt: string; +} + +function resultKey(questionnaireId: string) { + return `${RESULT_STORAGE_KEY}_${questionnaireId}`; +} + +export function saveResult(questionnaireId: string, answers: string[]) { + try { + const result: StoredResult = { + answers, + savedAt: new Date().toISOString(), + }; + sessionStorage.setItem(resultKey(questionnaireId), JSON.stringify(result)); + return true; + } catch (error) { + console.error('Failed to save result:', error); + return false; + } +} + +export function loadResult(questionnaireId: string): string[] | null { + try { + const raw = sessionStorage.getItem(resultKey(questionnaireId)); + if (!raw) { + return null; + } + + const parsed = JSON.parse(raw) as Partial; + return Array.isArray(parsed.answers) ? parsed.answers : null; + } catch (error) { + console.error('Failed to load result:', error); + return null; + } +} diff --git a/lib/storage.ts b/lib/storage.ts new file mode 100644 index 0000000..c1cd46d --- /dev/null +++ b/lib/storage.ts @@ -0,0 +1,78 @@ +const STORAGE_KEY = 'questionnaire'; + +function save( + usage: string, + questionnaireType: string, + answers: { [key: number]: string }, +) { + try { + const drafts = JSON.parse( + localStorage.getItem(`${STORAGE_KEY}_${usage}`) || '{}', + ); + drafts[questionnaireType] = { + answers, + savedAt: new Date().toISOString(), + }; + localStorage.setItem(`${STORAGE_KEY}_${usage}`, JSON.stringify(drafts)); + return true; + } catch (error) { + console.error('Failed to save draft:', error); + return false; + } +} + +function load( + usage: string, + questionnaireType: string, +): { [key: number]: string } | null { + try { + const drafts = JSON.parse( + localStorage.getItem(`${STORAGE_KEY}_${usage}`) || '{}', + ); + return drafts[questionnaireType]?.answers || null; + } catch (error) { + console.error('Failed to load draft:', error); + return null; + } +} + +function clear(usage: string, questionnaireType: string) { + try { + const key = `${STORAGE_KEY}_${usage}`; + const drafts = JSON.parse(localStorage.getItem(key) || '{}'); + + if (!(questionnaireType in drafts)) { + return false; + } + + delete drafts[questionnaireType]; + + if (Object.keys(drafts).length === 0) { + localStorage.removeItem(key); + } else { + localStorage.setItem(key, JSON.stringify(drafts)); + } + + return true; + } catch (error) { + console.error('Failed to clear draft:', error); + return false; + } +} + +export function saveDraft( + questionnaireType: string, + answers: { [key: number]: string }, +) { + return save('draft', questionnaireType, answers); +} + +export function loadDraft( + questionnaireType: string, +): { [key: number]: string } | null { + return load('draft', questionnaireType); +} + +export function clearDraft(questionnaireType: string) { + return clear('draft', questionnaireType); +} diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/locales/client.ts b/locales/client.ts new file mode 100644 index 0000000..38b4be5 --- /dev/null +++ b/locales/client.ts @@ -0,0 +1,6 @@ +"use client" +import { createI18nClient } from 'next-international/client'; + +export const { useI18n, useScopedI18n, I18nProviderClient } = createI18nClient({ + zh: () => import('./zh/index'), +}) diff --git a/locales/server.ts b/locales/server.ts new file mode 100644 index 0000000..3183df9 --- /dev/null +++ b/locales/server.ts @@ -0,0 +1,5 @@ +import { createI18nServer } from 'next-international/server' + +export const { getI18n, getScopedI18n, getStaticParams } = createI18nServer({ + zh: () => import('./zh/index'), +}) diff --git a/locales/zh/app.ts b/locales/zh/app.ts new file mode 100644 index 0000000..ae14232 --- /dev/null +++ b/locales/zh/app.ts @@ -0,0 +1,21 @@ +export default { + questionnaire: { + page: { + title: '量表详情', + introduction: '简介', + questionCount: '问题数', + evaluationTime: '测评时间', + instructions: '测试说明', + scoringMethod: '评分方法', + dimensions: '维度说明', + notes: '注意事项', + references: '参考资料', + startSurvey: '开始测评', + }, + result: { + resultNotFoundTitle: '结果未找到', + resultNotFoundDesc: '无法获取你的测评结果,请重新完成测评。', + retryTest: '重新测评', + }, + }, +} as const; diff --git a/locales/zh/common.ts b/locales/zh/common.ts new file mode 100644 index 0000000..a83dca6 --- /dev/null +++ b/locales/zh/common.ts @@ -0,0 +1,43 @@ +export default { + severity: { + normal: '正常', + mild: '轻度', + moderate: '中度', + severe: '重度', + minimal: '最低程度', + extremely_severe: '极重度', + }, + labels: { + total_score: '总分', + severity_level: '严重程度', + result_interpretation: '结果解释', + professional_advice: '专业建议', + evaluation_result: '评估结果', + high_score_items: '高分项目', + needs_attention: '需要关注', + }, + crisis: { + emergency_alert: '紧急提醒', + suicide_risk: '自伤风险', + hotline_contact: '请立即联系心理危机干预热线或身边可信任的人', + immediate_actions: '需要立即采取行动', + professional_help: '立即寻求专业帮助', + }, + disclaimers: { + screening_tool: '这是一个筛查工具', + not_diagnostic: '结果不构成诊断', + consult_professional: '请咨询专业人士', + }, + advice: { + maintain_good_state: '保持良好心理状态', + self_management: '自我管理策略', + professional_treatment: '建议专业治疗', + lifestyle_management: '生活方式管理', + social_support: '社会支持', + }, + answerList: { + title: '作答明细', + option: '选项', + unanswered: '未作答', + }, +} as const; diff --git a/locales/zh/component.ts b/locales/zh/component.ts new file mode 100644 index 0000000..fbcdac5 --- /dev/null +++ b/locales/zh/component.ts @@ -0,0 +1,65 @@ +export default { + questionnaire: { + result: { + public: { + resultContainer: { + resultText: '测评结果', + backToDetail: '返回重新作答', + copyResultData: '复制完整记录', + copyResultDataSuccess: '完整测评记录已复制', + copyResultDataError: '复制测评记录失败', + downloadResultData: '下载 MD', + downloadResultDataSuccess: 'MD 文件已下载', + downloadResultDataError: '下载 MD 失败', + copyTemplate: { + title: '量表测评记录', + basicInfo: '基本信息', + questionnaireName: '量表名称', + questionnaireId: '量表ID', + assessmentTime: '测评时间', + questionCount: '题目数量', + questionsAndAnswers: '题目与答案', + answer: '答案', + usage: '说明', + disclaimer: + '本记录仅用于自我了解和后续分析参考,不构成医疗诊断。如有明显痛苦或风险,请咨询专业人员。', + source: '数据来源', + platform: '心理量表测试', + website: '网站', + }, + completeTest: '完成测评', + copyResultLink: '复制页面链接', + copySuccess: '页面链接已复制', + copyError: '复制链接失败', + }, + }, + }, + test: { + public: { + navigation: { + previousPage: '上一页', + pageInfo: '第 {currentPage} / {totalPages} 页', + submit: '提交', + nextPage: '下一页', + }, + progressPanel: { + hideNav: '隐藏题目导航', + showNav: '显示题目导航', + completionProgress: '完成进度:', + jumpToQuestion: '跳转到第 {questionNumber} 题', + }, + }, + }, + list: { + title: '量表列表', + searchPlaceholder: '搜索量表或想了解的问题...', + detailButton: '开始', + noMatch: '未找到匹配的量表', + }, + }, + navBar: { + title: '量表测试', + introduce: '首页', + questionsList: '量表', + }, +} as const; diff --git a/locales/zh/components.ts b/locales/zh/components.ts new file mode 100644 index 0000000..8a1bc55 --- /dev/null +++ b/locales/zh/components.ts @@ -0,0 +1,27 @@ +import ocdResult from './result/ocd'; +import scl90Result from './result/scl90'; +import sdsResult from './result/sds'; +import phq9Result from './result/phq9'; +import bdi2Result from './result/bdi2'; +import gad7Result from './result/gad7'; +import dass21Result from './result/dass21'; +import isiResult from './result/isi'; +import pss10Result from './result/pss10'; +import adhdResult from './result/adhd'; +import gdResult from './result/gd'; +import npdResult from './result/npd'; + +export default { + ocdResult, + scl90Result, + sdsResult, + phq9Result, + bdi2Result, + gad7Result, + dass21Result, + isiResult, + pss10Result, + adhdResult, + gdResult, + npdResult, +} as const; \ No newline at end of file diff --git a/locales/zh/index.ts b/locales/zh/index.ts new file mode 100644 index 0000000..b8caa20 --- /dev/null +++ b/locales/zh/index.ts @@ -0,0 +1,11 @@ +import app from './app'; +import component from './component'; +import components from './components'; +import common from './common'; + +export default { + app, + component, + components, + common, +} as const; diff --git a/locales/zh/result/adhd.ts b/locales/zh/result/adhd.ts new file mode 100644 index 0000000..a4c0589 --- /dev/null +++ b/locales/zh/result/adhd.ts @@ -0,0 +1,29 @@ +export default { + totalScore: '总分', + inattention: '注意力不集中', + hyperactivity: '多动冲动', + partAScore: 'A部分得分', + screeningResult: '筛查结果', + severityLevel: '严重程度', + positiveScreen: '阳性筛查', + negativeScreen: '阴性筛查', + partAPositiveResponses: 'A部分阳性回答', + basedOnTotalScore: '基于总分', + recommendations: '建议', + importantNotes: '重要提示', + severityLevels: { + low: '低风险', + mild: '轻度症状', + moderate: '中度症状', + high: '重度症状', + }, + notes: { + screening: '此量表是筛查工具,不能提供ADHD诊断', + symptoms: 'ADHD症状必须在7岁前出现,并在多个环境中造成功能损害', + evaluation: '需要合格的医疗专业人员进行全面评估才能诊断', + }, + recommendationTexts: { + positive: '结果提示可能存在ADHD症状。建议咨询医疗专业人员进行适当评估。', + negative: '结果未提示明显的ADHD症状。但如果您有担忧,建议咨询医疗专业人员。', + }, +} as const; \ No newline at end of file diff --git a/locales/zh/result/bdi2.ts b/locales/zh/result/bdi2.ts new file mode 100644 index 0000000..71a882c --- /dev/null +++ b/locales/zh/result/bdi2.ts @@ -0,0 +1,101 @@ +export default { + title: 'BDI-II 评估结果', + severity: { + minimal: '无/最低程度', + mild: '轻度抑郁', + moderate: '中度抑郁', + severe: '重度抑郁', + }, + severityDescriptions: { + minimal: '您目前的抑郁水平很低,没有显著的抑郁症状。', + mild: '您可能存在轻度抑郁症状,建议关注自己的情绪状态并考虑寻求支持。', + moderate: '您可能存在中度抑郁症状,建议寻求专业心理健康服务。', + severe: '您可能存在重度抑郁症状,强烈建议立即寻求专业医疗帮助。', + }, + subscales: { + emotional: '情感症状', + cognitive: '认知症状', + somatic: '躯体症状', + behavioral: '行为症状', + }, + subscaleDescriptions: { + emotional: '悲伤、绝望、内疚等情感表现', + cognitive: '自我批评、决断困难、注意力问题', + somatic: '疲劳、睡眠、食欲、性兴趣问题', + behavioral: '兴趣丧失、社交退缩、激越表现', + }, + labels: { + symptom_dimension_analysis: '症状维度分析', + item_detailed_analysis: '项目详细分析', + assessment_result: '评估结果', + result_interpretation: '结果解释', + professional_advice: '专业建议', + scoring_criteria: 'BDI-II 评分标准', + emergency_reminder: '紧急提醒', + suicide_risk_attention: '⚠️ 自杀风险 - 需要立即关注', + important_reminder: '重要提醒', + }, + scoring: { + range_0_13: '0-13分:无/最低程度抑郁', + range_14_19: '14-19分:轻度抑郁', + range_20_28: '20-28分:中度抑郁', + range_29_63: '29-63分:重度抑郁', + }, + crisis: { + suicide_warning: '您报告了自杀想法。这是非常严重的信号,请立即寻求帮助:', + hotline: '• 立即联系心理危机干预热线:400-161-9995', + hospital: '• 前往最近的医院急诊科', + doctor: '• 联系您的医生或心理健康专家', + support: '• 与信任的家人或朋友在一起,不要独处', + }, + advice: { + maintain_good_state: '保持良好状态', + maintain_good_state_item_1: '• 继续保持健康的生活方式', + maintain_good_state_item_2: '• 维持良好的社交关系', + maintain_good_state_item_3: '• 定期进行自我评估', + maintain_good_state_item_4: '• 学习压力管理技巧', + depression_management: '抑郁症状管理建议', + daily_management: '日常生活管理', + daily_management_item_1: '• 建立规律的作息时间', + daily_management_item_2: '• 设定小而可实现的目标', + daily_management_item_3: '• 参与愉快的活动', + daily_management_item_4: '• 适度运动,如散步、瑜伽', + daily_management_item_5: '• 保持营养均衡的饮食', + social_support: '社会支持', + social_support_item_1: '• 与亲友保持联系', + social_support_item_2: '• 参加支持小组', + social_support_item_3: '• 寻求专业心理咨询', + social_support_item_4: '• 考虑认知行为疗法', + social_support_item_5: '• 必要时接受药物治疗', + professional_treatment: '专业治疗建议', + professional_treatment_item_1: '• 寻求精神科医生或心理治疗师帮助', + professional_treatment_item_2: '• 考虑系统性心理治疗(如CBT、IPT)', + professional_treatment_item_3: '• 评估是否需要抗抑郁药物治疗', + professional_treatment_item_4: '• 建立危机干预计划', + professional_treatment_item_5: '• 定期随访监测症状变化', + }, + disclaimer: 'BDI-II是筛查工具,不能替代专业诊断。抑郁症是可以治疗的疾病,及早寻求专业帮助可以显著改善症状和生活质量。', + symptoms: { + 0: '悲伤', + 1: '悲观', + 2: '既往失败', + 3: '快感缺失', + 4: '内疚感', + 5: '惩罚感', + 6: '自我厌恶', + 7: '自我指责', + 8: '自杀想法或愿望', + 9: '哭泣', + 10: '激越', + 11: '兴趣缺失', + 12: '犹豫不决', + 13: '无价值感', + 14: '精力缺失', + 15: '睡眠模式改变', + 16: '易激惹', + 17: '食欲改变', + 18: '注意力集中困难', + 19: '疲劳或疲倦', + 20: '对性的兴趣缺失', + }, +} as const; diff --git a/locales/zh/result/dass21.ts b/locales/zh/result/dass21.ts new file mode 100644 index 0000000..1269c6f --- /dev/null +++ b/locales/zh/result/dass21.ts @@ -0,0 +1,86 @@ +export default { + title: 'DASS-21 评估结果', + dimensions: { + depression: '抑郁', + anxiety: '焦虑', + stress: '压力', + }, + severity: { + normal: '正常', + mild: '轻度', + moderate: '中度', + severe: '重度', + extremely_severe: '极重度', + }, + descriptions: { + depression: '情绪低落、绝望、生活缺乏意义等', + anxiety: '自主神经系统唤起、肌肉紧张等', + stress: '持续紧张、易激惹、过度反应等', + }, + labels: { + total_score: '总分', + depression_score: '抑郁分数', + anxiety_score: '焦虑分数', + stress_score: '压力分数', + three_dimension_analysis: '三维度详细分析', + scoring_criteria: '评分标准', + result_interpretation_advice: '结果解释与建议', + overall_assessment: '整体评估', + depression_dimension: '抑郁维度', + anxiety_dimension: '焦虑维度', + stress_dimension: '压力维度', + important_reminder: '重要提醒', + note: '注意', + }, + scoring: { + depression: { + normal: '正常:0-9分', + mild: '轻度:10-13分', + moderate: '中度:14-20分', + severe: '重度:21-27分', + extremely_severe: '极重度:28+分', + }, + anxiety: { + normal: '正常:0-7分', + mild: '轻度:8-9分', + moderate: '中度:10-14分', + severe: '重度:15-19分', + extremely_severe: '极重度:20+分', + }, + stress: { + normal: '正常:0-14分', + mild: '轻度:15-18分', + moderate: '中度:19-25分', + severe: '重度:26-33分', + extremely_severe: '极重度:34+分', + }, + }, + assessment: { + severe_message: '您在一个或多个维度上显示出较高的困扰水平,建议寻求专业心理健康服务的帮助。', + normal_message: '您的整体情绪状态在可接受范围内,继续保持良好的心理健康习惯。', + }, + advice: { + depression_dimension: '抑郁维度建议', + depression_item_1: '• 建立规律的日常作息和活动计划', + depression_item_2: '• 参与愉快的活动,即使最初没有兴趣', + depression_item_3: '• 与亲友保持联系,寻求社会支持', + depression_item_4: '• 适度运动,如散步、瑜伽等', + depression_severe: '• 建议尽快寻求专业心理治疗或医疗帮助', + anxiety_dimension: '焦虑维度建议', + anxiety_item_1: '• 练习深呼吸和渐进性肌肉放松', + anxiety_item_2: '• 学习正念冥想技巧', + anxiety_item_3: '• 识别和挑战焦虑想法', + anxiety_item_4: '• 逐步面对恐惧情境,避免完全回避', + anxiety_severe: '• 考虑认知行为疗法或药物治疗', + stress_dimension: '压力维度建议', + stress_item_1: '• 学习时间管理和优先级设定', + stress_item_2: '• 练习压力管理技巧,如冥想、瑜伽', + stress_item_3: '• 设定现实可行的目标和期望', + stress_item_4: '• 寻找健康的压力释放方式', + stress_severe: '• 考虑压力管理课程或专业咨询', + }, + warning: { + severe_distress: '您的得分显示存在显著的心理困扰,强烈建议寻求专业心理健康服务。早期干预可以有效改善症状并提高生活质量。', + }, + disclaimer: 'DASS-21是筛查工具,不能替代专业诊断。如果您对结果有疑问或需要帮助,请咨询合格的心理健康专业人员。', +} as const; diff --git a/locales/zh/result/gad7.ts b/locales/zh/result/gad7.ts new file mode 100644 index 0000000..d872f7a --- /dev/null +++ b/locales/zh/result/gad7.ts @@ -0,0 +1,64 @@ +export default { + title: '评估结果', + severity: { + minimal: '最低水平', + mild: '轻度焦虑', + moderate: '中度焦虑', + severe: '重度焦虑', + }, + severityDescriptions: { + minimal: '您的焦虑水平很低,目前没有显著的焦虑症状。', + mild: '您可能存在轻度焦虑症状,建议关注自己的情绪状态。', + moderate: '您可能存在中度焦虑症状,建议考虑寻求专业帮助。', + severe: '您可能存在重度焦虑症状,强烈建议寻求专业医疗帮助。', + }, + labels: { + total_score: '总分', + high_score_items: '高分项目数', + anxiety_level: '焦虑程度', + result_interpretation: '结果解释', + scoring_criteria: '评分标准', + item_analysis: '项目分析', + professional_advice: '专业建议', + high_score_item_alert: '高分项目提示', + needs_attention: '需要关注', + maintain_good_state: '保持良好状态', + self_management_advice: '自我管理建议', + seek_professional_help: '建议寻求专业帮助', + note: '注意', + unknown: '未知', + }, + scoring: { + range_0_4: '• 0-4分:最低水平焦虑', + range_5_9: '• 5-9分:轻度焦虑', + range_10_14: '• 10-14分:中度焦虑', + range_15_21: '• 15-21分:重度焦虑', + }, + highScoreAlert: { + message: '您在 {count} 个项目上得分较高(≥2分),这些项目反映的症状在过去两周内出现频率较高,建议重点关注。', + }, + advice: { + maintain_good_state: '保持良好状态', + maintain_good_state_item_1: '• 继续保持健康的生活方式', + maintain_good_state_item_2: '• 适度运动和充足睡眠', + maintain_good_state_item_3: '• 学习压力管理技巧', + maintain_good_state_item_4: '• 维持良好的社交关系', + self_management_advice: '自我管理建议', + self_management_item_1: '• 练习深呼吸和放松技巧', + self_management_item_2: '• 规律运动,如散步、瑜伽等', + self_management_item_3: '• 保持规律的作息时间', + self_management_item_4: '• 限制咖啡因和酒精摄入', + self_management_item_5: '• 与信任的人分享您的感受', + professional_help_message: '建议寻求专业帮助:您的得分提示存在{severity},建议咨询心理医生或精神科医生进行进一步评估。', + }, + disclaimer: 'GAD-7量表仅供参考,不能替代专业医生的诊断。如有疑问,请咨询专业医疗人员。', + questions: { + 0: '感到紧张、担心或焦虑', + 1: '无法停止或控制担心', + 2: '对各种各样的事情担心过多', + 3: '很难放松下来', + 4: '坐立不安,难以静坐', + 5: '变得容易烦恼或易怒', + 6: '感到好像有什么可怕的事情会发生', + }, +} as const; diff --git a/locales/zh/result/gd.ts b/locales/zh/result/gd.ts new file mode 100644 index 0000000..efee43a --- /dev/null +++ b/locales/zh/result/gd.ts @@ -0,0 +1,36 @@ +export default { + totalScore: '总分', + scorePercentage: '得分百分比', + elevatedItems: '升高项目', + genderIdentity: '性别认同', + socialRole: '社会角色', + physicalDysphoria: '身体性别焦虑', + genderExpression: '性别表达', + overallAssessment: '整体评估', + recommendations: '建议', + importantNotes: '重要提示', + understandingResults: '理解您的结果', + factorScores: '因子得分', + interpretationLevels: { + low: '低', + mild: '轻度', + moderate: '中度', + high: '高', + }, + factorDescriptions: { + genderIdentity: '对个人内在性别感受的体验', + socialRole: '对基于出生时指定性别的社会期望的舒适度', + physicalDysphoria: '对身体特征和体型的感受', + genderExpression: '以各种方式表达性别的舒适度', + }, + notes: { + purpose: '此问卷仅用于教育和自我反思目的', + substitute: '它不能替代专业评估或诊断', + complexity: '性别认同是一个复杂而个人化的体验,在个体间差异很大', + professional: '如果您感到痛苦,请考虑咨询合格的心理健康专业人员', + }, + recommendationTexts: { + high: '结果提示显著的性别相关担忧。建议咨询专门从事性别认同问题的合格心理健康专业人员,寻求支持和指导。', + low: '结果提示相对较低的性别相关痛苦。但是,性别认同是复杂而个人化的 - 如果您有疑问或担忧,请考虑咨询支持性的咨询师。', + }, +} as const; \ No newline at end of file diff --git a/locales/zh/result/isi.ts b/locales/zh/result/isi.ts new file mode 100644 index 0000000..b45357f --- /dev/null +++ b/locales/zh/result/isi.ts @@ -0,0 +1,26 @@ +export default { + title: 'ISI 失眠评估结果', + severity: { + no_insomnia: '无临床意义失眠', + subthreshold: '亚临床失眠', + moderate: '中度失眠', + severe: '重度失眠', + }, + labels: { + total_score: '总分', + high_score_items: '高分项目数', + insomnia_level: '失眠程度', + result_interpretation: '结果解释', + scoring_criteria: 'ISI 评分标准', + unknown: '未知', + }, + scoring: { + range_0_7: '• 0-7分:无临床意义失眠', + range_8_14: '• 8-14分:亚临床失眠', + range_15_21: '• 15-21分:中度失眠', + range_22_28: '• 22-28分:重度失眠', + }, + advice: { + sleep_specialist_message: '建议咨询睡眠专科医生,寻求专业的睡眠治疗方案。', + }, +} as const; diff --git a/locales/zh/result/npd.ts b/locales/zh/result/npd.ts new file mode 100644 index 0000000..ce622a7 --- /dev/null +++ b/locales/zh/result/npd.ts @@ -0,0 +1,45 @@ +export default { + totalScore: '总分', + percentile: '百分位', + leadership: '领导力', + exhibitionism: '表现欲', + narcissisticTraitsLevel: '自恋特质水平', + dominantTrait: '主导特质', + entitlement: '特权感', + interpretation: '解释', + understandingTraits: '理解自恋特质', + factorBreakdown: '因子分析', + importantNotes: '重要提示', + healthyVsProblematic: '健康自恋 vs. 问题自恋', + interpretationLevels: { + low: '低于平均水平', + average: '平均水平', + above_average: '高于平均水平', + high: '高水平', + }, + traitLabels: { + leadership: '领导力/权威', + exhibitionism: '夸大表现欲', + entitlement: '特权感', + }, + factorDescriptions: { + leadership: '渴望领导和对他人拥有权威', + exhibitionism: '需要他人的关注和赞美', + entitlement: '认为自己应当受到特殊待遇和特权', + }, + notes: { + continuum: '此量表测量连续体上的自恋特质,而非临床障碍', + adaptive: '每个人都在某种程度上具有自恋特质,在某些情况下可能是适应性的', + disorder: '高分并不一定表示人格障碍', + purpose: '此工具仅用于教育和研究目的,不用于临床诊断', + population: '普通人群的平均分数通常在2-8分之间', + }, + healthyAspects: '健康方面:自信、领导力、自我倡导、雄心', + potentialConcerns: '潜在担忧:同理心困难、关系问题、利用他人', + balanceKey: '平衡是关键 - 健康的自尊与对他人的考虑往往是最适应性的。', + recommendationTexts: { + high: '结果提示自恋特质较高。请记住这是人格评估,不是临床诊断。自恋特质在某些情况下可能是适应性的,但如果过度可能会影响人际关系。', + above_average: '结果提示高于平均水平的自恋特质。这相对常见,可以包括自信和领导力等积极方面,与对他人的考虑保持平衡。', + low: '结果提示相对较低的自恋特质,表明有平衡的自我感知和对他人的考虑。这提示健康的自尊而没有过度的自我关注。', + }, +} as const; \ No newline at end of file diff --git a/locales/zh/result/ocd.ts b/locales/zh/result/ocd.ts new file mode 100644 index 0000000..1cc2b06 --- /dev/null +++ b/locales/zh/result/ocd.ts @@ -0,0 +1,13 @@ +export default { + totalScore: '总分', + obsessionsScore: '强迫意念分', + compulsionsScore: '强迫行为分', + severity: '严重程度', + severityLevel: { + 1: '正常 / 亚临床', + 2: '轻度', + 3: '中度', + 4: '重度', + 5: '极重度', + }, +} as const; diff --git a/locales/zh/result/phq9.ts b/locales/zh/result/phq9.ts new file mode 100644 index 0000000..e9bb0d8 --- /dev/null +++ b/locales/zh/result/phq9.ts @@ -0,0 +1,83 @@ +export default { + title: '评估结果', + severity: { + minimal: '无/最低程度', + mild: '轻度抑郁', + moderate: '中度抑郁', + moderately_severe: '中重度抑郁', + severe: '重度抑郁', + }, + severityDescriptions: { + minimal: '您的抑郁水平很低,目前没有显著的抑郁症状。', + mild: '您可能存在轻度抑郁症状,建议关注自己的情绪状态。', + moderate: '您可能存在中度抑郁症状,建议考虑寻求专业帮助。', + moderately_severe: '您可能存在中重度抑郁症状,强烈建议寻求专业医疗帮助。', + severe: '您可能存在重度抑郁症状,请立即寻求专业医疗帮助。', + }, + labels: { + total_score: '总分', + high_score_items: '高分项目数', + depression_level: '抑郁程度', + result_interpretation: '结果解释', + scoring_criteria: '评分标准', + item_analysis: '项目分析', + professional_advice: '专业建议', + emergency_reminder: '紧急提醒', + high_score_item_analysis: '高分项目分析', + needs_immediate_attention: '⚠️ 需要立即关注', + needs_attention: '需要关注', + unknown: '未知', + note: '注意', + }, + scoring: { + range_0_4: '• 0-4分:无/最低程度抑郁', + range_5_9: '• 5-9分:轻度抑郁', + range_10_14: '• 10-14分:中度抑郁', + range_15_19: '• 15-19分:中重度抑郁', + range_20_27: '• 20-27分:重度抑郁', + }, + crisis: { + suicide_warning: '您报告了自伤或自杀的想法。这是一个严重的信号,请立即寻求专业帮助:', + hotline: '• 立即联系心理危机干预热线:400-161-9995', + hospital: '• 前往最近的医院急诊科', + doctor: '• 联系您的医生或心理健康专家', + support: '• 与信任的家人或朋友在一起,不要独处', + }, + clinical: { + major_depression_warning: '注意:您的症状模式符合主要抑郁发作的筛查标准,强烈建议寻求专业医疗评估。', + }, + highScoreAnalysis: { + message: '您在 {count} 个项目上得分较高(≥2分),这些症状在过去两周内出现频率较高。这些项目反映的问题领域可能是您需要重点关注和改善的方面。', + }, + advice: { + maintain_good_state: '保持良好状态', + maintain_good_state_item_1: '• 继续保持健康的生活方式', + maintain_good_state_item_2: '• 规律运动和充足睡眠', + maintain_good_state_item_3: '• 维持良好的社交关系', + maintain_good_state_item_4: '• 学习压力管理技巧', + self_management_advice: '自我管理建议', + self_management_item_1: '• 建立规律的日常作息', + self_management_item_2: '• 适度运动,如散步、游泳等', + self_management_item_3: '• 保持健康饮食,避免过量饮酒', + self_management_item_4: '• 与亲友保持联系,寻求社会支持', + self_management_item_5: '• 尝试放松技巧,如冥想、深呼吸', + self_management_item_6: '• 设定现实可行的目标', + professional_treatment: '专业治疗建议', + professional_treatment_item_1: '• 心理治疗:认知行为疗法、人际治疗等', + professional_treatment_item_2: '• 药物治疗:抗抑郁药物(需医生处方)', + professional_treatment_item_3: '• 综合治疗:结合心理和药物治疗', + professional_treatment_item_4: '• 定期随访:监测症状变化和治疗效果', + }, + disclaimer: 'PHQ-9量表仅供参考,不能替代专业医生的诊断。抑郁症是可以治疗的疾病,及早干预效果更好。', + questions: { + 0: '做事时提不起劲或没有兴趣', + 1: '感到心情低落、沮丧或绝望', + 2: '入睡困难、睡不安稳或睡眠过多', + 3: '感觉疲倦或没有活力', + 4: '食欲不振或吃太多', + 5: '觉得自己很糟糕,或觉得自己很失败,或让自己或家人失望', + 6: '对事物专注有困难,例如阅读报纸或看电视时', + 7: '动作或说话速度缓慢到别人已经察觉?或正好相反——比平时更加烦躁或坐立不安,动来动去', + 8: '有不如死掉或用某种方式伤害自己的念头', + }, +} as const; diff --git a/locales/zh/result/pss10.ts b/locales/zh/result/pss10.ts new file mode 100644 index 0000000..6c21f94 --- /dev/null +++ b/locales/zh/result/pss10.ts @@ -0,0 +1,90 @@ +export default { + title: '评估结果', + severity: { + low: '低压力', + moderate: '中等压力', + high: '高压力', + }, + severityDescriptions: { + low: '您目前的压力感知水平较低,表明您能够较好地应对生活中的挑战。', + moderate: '您目前的压力感知水平处于中等程度,需要关注压力管理。', + high: '您目前的压力感知水平较高,建议积极采取压力管理措施,必要时寻求专业帮助。', + }, + labels: { + total_score: '总分', + stress_perception: '压力感知', + coping_ability: '应对能力', + stress_level: '压力水平', + result_interpretation: '结果解释', + score_interpretation: '得分解释', + reference_standards: '参考标准', + subscale_analysis: '分量表分析', + item_analysis: '项目分析', + stress_management_advice: '压力管理建议', + high_score_reminder: '高分项目提示', + reverse_scoring: '反向计分', + original_score: '原始得分', + actual_score: '实际得分', + high_stress: '高压力', + note: '注意', + }, + scoring: { + total_range: '总分范围:0-40分,得分越高表示感知到的压力越大', + stress_perception_desc: '压力感知:反映您对生活中不可预测和不可控事件的感知', + coping_ability_desc: '应对能力:反映您对自己处理问题能力的信心', + range_0_13: '0-13分:低压力水平', + range_14_26: '14-26分:中等压力水平', + range_27_40: '27-40分:高压力水平', + }, + scoreInterpretation: { + low_level: '低', + moderate_level: '中', + high_level: '高', + low_desc: '压力水平较低', + moderate_desc: '压力水平中等', + high_desc: '压力水平较高', + }, + subscales: { + stress_perception_title: '压力感知', + stress_perception_desc: '反映您对生活中不可预测性、不可控制性和超负荷情况的感知程度。得分越高,表明您感到生活中的压力和挑战越多。', + coping_ability_title: '应对能力', + coping_ability_desc: '反映您对自己应对能力的信心和控制感的缺失程度。得分越高,表明您越感到缺乏应对能力和控制感。', + }, + highScoreAnalysis: { + message: '您在 {count} 个项目上得分较高(≥3分),这些方面的压力感知较为强烈,建议重点关注这些领域的压力管理。', + }, + advice: { + maintain_good_state: '保持良好状态', + maintain_good_state_item_1: '• 继续保持现有的压力管理策略', + maintain_good_state_item_2: '• 定期进行自我评估', + maintain_good_state_item_3: '• 帮助他人管理压力', + maintain_good_state_item_4: '• 分享成功的应对经验', + stress_management_strategies: '压力管理策略', + short_term_strategies: '短期缓解策略', + short_term_item_1: '• 深呼吸练习和放松技巧', + short_term_item_2: '• 适度运动,如散步、瑜伽', + short_term_item_3: '• 听音乐、冥想或正念练习', + short_term_item_4: '• 与朋友交流或寻求支持', + short_term_item_5: '• 充足的睡眠和休息', + long_term_strategies: '长期管理策略', + long_term_item_1: '• 建立规律的生活作息', + long_term_item_2: '• 学习时间管理技巧', + long_term_item_3: '• 培养爱好和兴趣', + long_term_item_4: '• 建立良好的社会支持网络', + long_term_item_5: '• 定期评估和调整目标', + high_stress_warning: '高压力提醒:您的压力水平较高,建议寻求专业帮助,如心理咨询师或压力管理专家的指导。长期的高压力可能影响身心健康。', + }, + disclaimer: 'PSS-10量表评估的是主观压力感知,不同个体对相同压力源的感知可能不同。重要的是学会有效的压力管理技巧,提高应对能力。', + questions: { + 0: '因为发生了意想不到的事情而感到心烦意乱', + 1: '感到无法控制生活中的重要事情', + 2: '感到紧张和压力', + 3: '自信地处理个人问题', + 4: '感到事情正朝着希望的方向发展', + 5: '发现无法应付所有必须做的事情', + 6: '能够控制生活中令人烦恼的事情', + 7: '感到掌控着整个局面', + 8: '因为无法控制的事情而感到生气', + 9: '感到困难重重,以至于无法克服它们', + }, +} as const; diff --git a/locales/zh/result/scl90.ts b/locales/zh/result/scl90.ts new file mode 100644 index 0000000..1b1df61 --- /dev/null +++ b/locales/zh/result/scl90.ts @@ -0,0 +1,43 @@ +export default { + factors: { + somatization: '躯体化', + obsessive: '强迫症状', + interpersonal: '人际关系敏感', + depression: '抑郁', + anxiety: '焦虑', + hostility: '敌对', + phobic: '恐怖', + paranoid: '偏执', + psychotic: '精神病性', + other: '其他', + }, + labels: { + overall_assessment: '总体评估', + positive_item_count: '阳性项目数', + positive_symptom_average: '阳性症状均分', + factor_analysis: '因子分析', + }, + clinical: { + rating_criteria: '评分标准', + judgment_criteria: '判断标准', + rating_scale: '1 = 没有 | 2 = 很轻 | 3 = 中等 | 4 = 偏重 | 5 = 严重', + total_score_criteria: '总分 ≥ 160 分或阳性项目数 ≥ 43 个:提示可能存在心理问题', + factor_score_2: '因子分 ≥ 2 分:该因子异常,需要关注', + factor_score_3: '因子分 ≥ 3 分:该因子严重异常,建议寻求专业帮助', + }, + warnings: { + severe_condition: '您的症状可能表明存在严重问题,请立即寻求专业帮助。', + unknown_level: '未知', + }, + description: { + total_score: '总分反映整体心理困扰水平', + positive_items: '评分高于"没有"的项目数量', + positive_average: '阳性项目的平均分', + }, + interpretation: { + normal: '您的心理状态处于正常范围', + mild: '您可能存在轻度心理症状', + moderate: '您可能存在中度心理症状', + severe: '您可能存在重度心理症状', + }, +} as const; diff --git a/locales/zh/result/sds.ts b/locales/zh/result/sds.ts new file mode 100644 index 0000000..b739640 --- /dev/null +++ b/locales/zh/result/sds.ts @@ -0,0 +1,56 @@ +export default { + title: '评估结果', + severity: { + normal: '正常', + mild: '轻度抑郁', + moderate: '中度抑郁', + severe: '重度抑郁', + }, + severityDescriptions: { + normal: '您的得分在正常范围内,没有显著的抑郁症状。', + mild: '您可能存在轻度抑郁症状,建议关注自己的心理状态,必要时寻求专业帮助。', + moderate: '您可能存在中度抑郁症状,建议尽快咨询专业的心理医生。', + severe: '您可能存在重度抑郁症状,强烈建议立即寻求专业医疗帮助。', + }, + labels: { + raw_total_score: '原始总分', + standard_score: '标准分', + depression_level: '抑郁程度', + result_interpretation: '结果解释', + scoring_criteria: '评分标准', + detailed_analysis: '详细分析', + scale_description: '量表说明', + scoring_method: '计分方式', + professional_advice: '专业建议', + important_reminder: '重要提醒', + note: '注意', + unknown: '未知', + }, + scoring: { + range_0_52: '标准分 ≤ 52:正常范围', + range_53_62: '标准分 53-62:轻度抑郁', + range_63_72: '标准分 63-72:中度抑郁', + range_73_plus: '标准分 ≥ 73:重度抑郁', + }, + scaleInfo: { + description_1: 'SDS量表包含20个项目,涵盖情感、躯体、精神运动和心理方面的症状', + description_2: '其中10个项目为正向计分,10个项目为反向计分', + description_3: '原始分数乘以1.25得到标准分,便于与其他研究结果比较', + positive_items: '1、3、4、7、8、9、10、13、15、19题', + reverse_items: '2、5、6、11、12、14、16、17、18、20题', + option_scoring: '很少=1分,有些时间=2分,相当多时间=3分,绝大部分时间=4分', + reverse_scoring: '4分→1分,3分→2分,2分→3分,1分→4分', + }, + advice: { + high_score_title: '如果您的得分较高,建议:', + seek_professional: '寻求专业心理医生或精神科医生的帮助', + share_feelings: '与信任的家人或朋友分享您的感受', + maintain_routine: '保持规律的作息和适度的运动', + avoid_substances: '避免酗酒和滥用药物', + suicide_help: '如有自杀想法,请立即寻求帮助', + }, + warnings: { + depression_reminder: '您的得分提示可能存在抑郁症状,请尽快联系专业心理医生或精神科医生进行进一步评估和治疗。', + }, + disclaimer: '本量表仅供参考,不能替代专业医生的诊断。如有疑问,请咨询专业医疗人员。', +} as const; diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..d4b6ea8 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,18 @@ +import { createI18nMiddleware } from 'next-international/middleware'; +import { NextRequest } from 'next/server'; + +const I18nMiddleware = createI18nMiddleware({ + locales: ['zh'], + defaultLocale: 'zh', + urlMappingStrategy: 'rewrite', +}); + +export function middleware(request: NextRequest) { + return I18nMiddleware(request); +} + +export const config = { + matcher: [ + '/((?!api|_next/static|_next/image|favicon.ico|apple-icon\\.png|icon0\\.svg|icon1\\.png).*)', + ], +}; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..94647ad --- /dev/null +++ b/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = { + output: 'standalone', +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..f95ef56 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "questionnaire-test", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint app components hooks lib locales questionairies types middleware.ts next.config.ts" + }, + "dependencies": { + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-slot": "^1.2.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.501.0", + "next": "15.5.18", + "next-international": "^1.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "sonner": "^2.0.5", + "tailwind-merge": "^3.2.0", + "tw-animate-css": "^1.2.7" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "15.5.18", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e5ccd99 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4563 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + postcss: 8.5.10 + +importers: + + .: + dependencies: + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.15 + version: 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': + specifier: ^1.2.0 + version: 1.3.0(@types/react@19.2.17)(react@19.2.7) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.501.0 + version: 0.501.0(react@19.2.7) + next: + specifier: 15.5.18 + version: 15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next-international: + specifier: ^1.3.1 + version: 1.3.1 + react: + specifier: ^19.0.0 + version: 19.2.7 + react-dom: + specifier: ^19.0.0 + version: 19.2.7(react@19.2.7) + sonner: + specifier: ^2.0.5 + version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + tailwind-merge: + specifier: ^3.2.0 + version: 3.6.0 + tw-animate-css: + specifier: ^1.2.7 + version: 1.4.0 + devDependencies: + '@eslint/eslintrc': + specifier: ^3 + version: 3.3.5 + '@tailwindcss/postcss': + specifier: ^4 + version: 4.3.1 + '@types/node': + specifier: ^20 + version: 20.19.43 + '@types/react': + specifier: ^19 + version: 19.2.17 + '@types/react-dom': + specifier: ^19 + version: 19.2.3(@types/react@19.2.17) + eslint: + specifier: ^9 + version: 9.39.4(jiti@2.7.0) + eslint-config-next: + specifier: 15.5.18 + version: 15.5.18(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + tailwindcss: + specifier: ^4 + version: 4.3.1 + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@next/env@15.5.18': + resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} + + '@next/eslint-plugin-next@15.5.18': + resolution: {integrity: sha512-w4MYq8M26a8PNrfto0JosLf5/3ssln1rsyP96g2DkC8uFVymStM5DLSz5ElxxrPRg2XnTMnFo3kREFlhYvxhWw==} + + '@next/swc-darwin-arm64@15.5.18': + resolution: {integrity: sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.18': + resolution: {integrity: sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.18': + resolution: {integrity: sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@15.5.18': + resolution: {integrity: sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@15.5.18': + resolution: {integrity: sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@15.5.18': + resolution: {integrity: sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@15.5.18': + resolution: {integrity: sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.18': + resolution: {integrity: sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + + '@radix-ui/react-arrow@1.1.10': + resolution: {integrity: sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.10': + resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.13': + resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.18': + resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.10': + resolution: {integrity: sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.18': + resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.3.1': + resolution: {integrity: sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.12': + resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.6': + resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.13': + resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.2': + resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.1': + resolution: {integrity: sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.19.43': + resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + + '@typescript-eslint/eslint-plugin@8.61.1': + resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.61.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.61.1': + resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.12.1: + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.3.3: + resolution: {integrity: sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.1: + resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + engines: {node: '>= 0.4'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@15.5.18: + resolution: {integrity: sha512-HuoJU6uUPD00eyiud78IBnT4HLhztFj2V+ild2Uon5ZUrYZKe0Olu2QRD99e9IgL4/H1eg5Onka3BsfRW2U0Xw==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.13.0: + resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + international-types@0.8.1: + resolution: {integrity: sha512-tajBCAHo4I0LIFlmQ9ZWfjMWVyRffzuvfbXCd6ssFt5u1Zw15DN0UBpVTItXdNa1ls+cpQt3Yw8+TxsfGF8JcA==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lucide-react@0.501.0: + resolution: {integrity: sha512-E2KoyhW59fCb/yUbR3rbDer83fqn7a8NG91ZhIot2yWaPHjPyGzzsNKh40N//GezYShAuycf3TcQksRQznIsRw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.14: + resolution: {integrity: sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next-international@1.3.1: + resolution: {integrity: sha512-ydU9jQe+4MohMWltbZae/yuWeKhmp0QKQqJNNi8WCCMwrly03qfMAHw/tWbT2qgAlG++CxF5jMXmGQZgOHeVOw==} + + next@15.5.18: + resolution: {integrity: sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.11': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.11.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@next/env@15.5.18': {} + + '@next/eslint-plugin-next@15.5.18': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@15.5.18': + optional: true + + '@next/swc-darwin-x64@15.5.18': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.18': + optional: true + + '@next/swc-linux-arm64-musl@15.5.18': + optional: true + + '@next/swc-linux-x64-gnu@15.5.18': + optional: true + + '@next/swc-linux-x64-musl@15.5.18': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.18': + optional: true + + '@next/swc-win32-x64-msvc@15.5.18': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/rect@1.1.2': {} + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.16.1': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + + '@tailwindcss/postcss@4.3.1': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + postcss: 8.5.10 + tailwindcss: 4.3.1 + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.19.43': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.61.1 + eslint: 9.39.4(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.61.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.61.1': {} + + '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.61.1': + dependencies: + '@typescript-eslint/types': 8.61.1 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.12.1: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + brace-expansion@1.1.15: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001799: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.1 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.3: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.1: + dependencies: + es-abstract-get: 1.0.0 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escape-string-regexp@4.0.0: {} + + eslint-config-next@15.5.18(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 15.5.18 + '@rushstack/eslint-patch': 1.16.1 + '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4(jiti@2.7.0)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + get-tsconfig: 4.14.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.7.0)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.12.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.4(jiti@2.7.0) + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.3 + eslint: 9.39.4(jiti@2.7.0) + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + international-types@0.8.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.8.5 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lucide-react@0.501.0(react@19.2.7): + dependencies: + react: 19.2.7 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.15 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.14: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next-international@1.3.1: + dependencies: + client-only: 0.0.1 + international-types: 0.8.1 + server-only: 0.0.1 + + next@15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@next/env': 15.5.18 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001799 + postcss: 8.5.10 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.18 + '@next/swc-darwin-x64': 15.5.18 + '@next/swc-linux-arm64-gnu': 15.5.18 + '@next/swc-linux-arm64-musl': 15.5.18 + '@next/swc-linux-x64-gnu': 15.5.18 + '@next/swc-linux-x64-musl': 15.5.18 + '@next/swc-win32-arm64-msvc': 15.5.18 + '@next/swc-win32-x64-msvc': 15.5.18 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.10: + dependencies: + nanoid: 3.3.14 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.7(react@19.2.7): + dependencies: + react: 19.2.7 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + get-nonce: 1.0.1 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react@19.2.7: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + server-only@0.0.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(react@19.2.7): + dependencies: + client-only: 0.0.1 + react: 19.2.7 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.1: {} + + tapable@2.3.3: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tw-animate-css@1.4.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..6d37a53 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +allowBuilds: + sharp: true + unrs-resolver: true +overrides: + postcss: 8.5.10 diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/questionairies/adhd/zh.ts b/questionairies/adhd/zh.ts new file mode 100644 index 0000000..3230453 --- /dev/null +++ b/questionairies/adhd/zh.ts @@ -0,0 +1,68 @@ +import { Questionnaire } from "@/types"; + +export const adhd: Questionnaire = { + id: "adhd", + title: "成人ADHD自评量表 (ASRS-v1.1)", + description: "评估成人注意力缺陷多动障碍症状", + category: "认知", + tags: ["ADHD", "注意力", "执行功能", "筛查", "自评"], + time: "5-10分钟", + details: { + introduction: "成人ADHD自评量表(ASRS-v1.1)症状检查表是与世界卫生组织(WHO)以及包括以下精神科医生和研究人员团队的成人ADHD工作组合作开发的。该量表旨在根据DSM-IV标准评估成人ADHD症状的频率。请回答以下问题,根据您在过去6个月中的感受和行为表现对每个项目进行评分。", + questionCount: "18个项目", + evaluationTime: "约5-10分钟", + instructions: "对于每个问题,请选择最能描述您在过去6个月中感受和行为表现的回答。", + scoringMethod: [ + "每个问题评分为0-4分(从不=0,很少=1,有时=2,经常=3,非常频繁=4)。", + "A部分(问题1-6):筛查问题,4个或更多阳性回答提示ADHD症状。", + "B部分(问题7-18):额外症状评估。", + "总分范围:0-72分。", + "分数越高表示ADHD症状越严重。" + ], + dimensions: [ + { name: "注意力不集中", description: "难以维持注意力、遵循指示和组织任务。" }, + { name: "多动", description: "过度的运动活动、坐立不安、难以静坐。" }, + { name: "冲动", description: "不经思考就行动、打断他人、难以等待。" } + ], + notes: [ + "此量表是筛查工具,不能提供ADHD诊断。", + "如果您在此量表上得分较高,请考虑咨询医疗专业人员进行适当评估。", + "ADHD症状必须在7岁前出现,并在多个环境中造成功能损害才能诊断。" + ], + references: [ + { + text: "Kessler, R. C., Adler, L., Ames, M., Demler, O., Faraone, S., Hiripi, E., ... & Walters, E. E. (2005). The World Health Organization Adult ADHD Self-Report Scale (ASRS): a short screening scale for use in the general population. Psychological Medicine, 35(2), 245-256.", + url: "https://www.hcp.med.harvard.edu/ncs/asrs.php" + } + ] + }, + questions: [ + { id: 1, content: "在完成其中最艰难的部分之后,您在处理某一项目的最后细节时是否常常有困难?" }, + { id: 2, content: "您在完成具有组织性质的任务时,是否时常有困难把事情整理安排好?" }, + { id: 3, content: "您是否时常有困难记住约会或应做的事?" }, + { id: 4, content: "如果一件事需要多动脑筋,您是否常常躲避或推延开始做它?" }, + { id: 5, content: "如果您不得不长时间坐下,您是否常常蠕动不安或者手脚动个不停?" }, + { id: 6, content: "您是否时常感到过度活跃,强迫自己做事,就像上了发条的机器?" }, + { id: 7, content: "当您必须处理无聊或困难的项目时,是否常常犯粗心的错误?" }, + { id: 8, content: "当您在做无聊或重复性工作时,是否常常难以保持注意力?" }, + { id: 9, content: "即使别人直接和您说话,您是否常常难以集中注意力听他们说什么?" }, + { id: 10, content: "您是否常常在家里或工作中放错地方或难以找到东西?" }, + { id: 11, content: "您是否常常被周围的活动或噪音分散注意力?" }, + { id: 12, content: "在会议或其他预期您保持坐着的情况下,您是否常常离开座位?" }, + { id: 13, content: "您是否常常感到不安或坐立不安?" }, + { id: 14, content: "当您有时间放松时,是否常常难以放松和休息?" }, + { id: 15, content: "在社交场合,您是否常常发现自己说话太多?" }, + { id: 16, content: "在对话中,您是否常常在对方说完之前就抢着说完他们的句子?" }, + { id: 17, content: "在需要排队等候的情况下,您是否常常难以等待轮到您?" }, + { id: 18, content: "当别人忙碌时,您是否常常打断他们?" } + ], + renderOptions: () => { + return [ + { id: 1, content: '从不', value: '0' }, + { id: 2, content: '很少', value: '1' }, + { id: 3, content: '有时', value: '2' }, + { id: 4, content: '经常', value: '3' }, + { id: 5, content: '非常频繁', value: '4' }, + ]; + } +}; diff --git a/questionairies/attachment/zh.ts b/questionairies/attachment/zh.ts new file mode 100644 index 0000000..3addf30 --- /dev/null +++ b/questionairies/attachment/zh.ts @@ -0,0 +1,75 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '完全不同意', value: '1' }, + { id: 2, content: '不同意', value: '2' }, + { id: 3, content: '有点不同意', value: '3' }, + { id: 4, content: '不确定', value: '4' }, + { id: 5, content: '有点同意', value: '5' }, + { id: 6, content: '同意', value: '6' }, + { id: 7, content: '完全同意', value: '7' }, +]; + +export const attachment: Questionnaire = { + id: 'attachment', + title: 'ECR-RS 亲密关系依恋量表(9题)', + description: '评估亲密关系中的依恋焦虑和依恋回避倾向', + category: '人格', + tags: ['依恋', '自评'], + time: '3-5分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'ECR-RS 是一份简短的亲密关系依恋测量工具,主要评估两个方向:依恋焦虑和依恋回避。本版本用于了解你在亲密关系或重要关系中的一般模式。', + questionCount: '9个项目', + evaluationTime: '通常为3-5分钟', + instructions: + '请想着你最重要的亲密关系或重要他人作答。可以是伴侣、非常亲近的朋友,或当前对你影响最大的关系对象。', + scoringMethod: [ + '每题1-7分。', + '依恋回避:第1-6题平均分,其中第1-4题反向计分。', + '依恋焦虑:第7-9题平均分。分数越高表示该方向的不安全依恋倾向越明显。', + ], + dimensions: [ + { + name: '依恋回避', + description: '是否倾向于保持距离、减少依赖、回避过深的情感暴露。', + }, + { + name: '依恋焦虑', + description: '是否容易担心被拒绝、被忽视或关系不稳定。', + }, + ], + notes: [ + '依恋模式会随关系对象、阶段和安全感变化,不是固定标签。', + '高焦虑或高回避并不说明关系一定有问题,而是提示可以观察自己的安全感来源和互动习惯。', + '如果关系中存在伤害、控制或持续痛苦,请优先寻求现实支持或专业帮助。', + ], + references: [ + { + text: 'Fraley: Relationship Structures (ECR-RS) Questionnaire', + url: 'https://labs.psychology.illinois.edu/~rcfraley/measures/relstructures.htm', + }, + { + text: 'Fraley et al. (2011). The Experiences in Close Relationships-Relationship Structures questionnaire.', + url: 'https://doi.org/10.1037/a0022898', + }, + ], + }, + questions: [ + { id: 1, content: '我很容易向这个人表达自己的想法和感受。' }, + { id: 2, content: '在需要的时候,我愿意依靠这个人。' }, + { id: 3, content: '我愿意让这个人了解真实的我。' }, + { id: 4, content: '我通常能自在地和这个人亲近。' }, + { id: 5, content: '我不太愿意向这个人展示脆弱的一面。' }, + { id: 6, content: '当关系变得很亲近时,我有时会想拉开一点距离。' }, + { id: 7, content: '我担心这个人不如我在意对方那样在意我。' }, + { id: 8, content: '我担心自己会被这个人抛下或忽视。' }, + { id: 9, content: '我常常需要确认这个人是否真的重视我。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/bdi2/zh.ts b/questionairies/bdi2/zh.ts new file mode 100644 index 0000000..4e2d890 --- /dev/null +++ b/questionairies/bdi2/zh.ts @@ -0,0 +1,123 @@ +import { Questionnaire } from "@/types"; + +export const bdi2: Questionnaire = { + id: "bdi2", + title: "BDI-II Beck抑郁量表", + description: "抑郁症状评估的金标准工具", + category: "情绪", + tags: ["抑郁", "自评", "临床常用"], + time: "5-10分钟", + details: { + introduction: "Beck抑郁量表第二版(Beck Depression Inventory-II, BDI-II)是世界上最广泛使用的抑郁症筛查和评估工具之一。该量表由21个项目组成,每个项目描述抑郁症的一个特定症状,基于DSM-IV的抑郁症诊断标准设计。BDI-II具有良好的信效度,被认为是抑郁症状评估的金标准。", + questionCount: "21个项目", + evaluationTime: "通常为5-10分钟", + instructions: "这个问卷包含21组陈述。请仔细阅读每组陈述,然后从每组中选择一个最能描述您在过去两周内(包括今天)感受的陈述。每组陈述按严重程度排序,从0到3分。", + scoringMethod: [ + "总分:将21个项目得分相加,范围0-63分", + "严重程度:0-13分为最低程度/无抑郁,14-19分为轻度抑郁,20-28分为中度抑郁,29-63分为重度抑郁", + "临床意义:总分≥14分建议进一步评估,≥29分提示重度抑郁需要立即关注" + ], + dimensions: [ + { name: "情感症状", description: "悲伤、绝望、哭泣、易怒等情绪表现" }, + { name: "认知症状", description: "自我批评、内疚感、决断困难、注意力问题" }, + { name: "躯体症状", description: "疲劳、睡眠问题、食欲改变、性兴趣下降" }, + { name: "行为症状", description: "社交退缩、兴趣丧失、工作能力下降" } + ], + notes: [ + "该量表适用于13岁以上的青少年和成年人", + "如果总分≥29分或有自杀想法,需要立即寻求专业帮助", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Beck, A. T., Steer, R. A., & Brown, G. K. (1996). Manual for the Beck Depression Inventory-II. San Antonio, TX: Psychological Corporation.", + url: "https://www.pearsonassessments.com" + } + ] + }, + questions: [ + { id: 1, content: "悲伤" }, + { id: 2, content: "悲观" }, + { id: 3, content: "既往失败" }, + { id: 4, content: "快感缺失" }, + { id: 5, content: "内疚感" }, + { id: 6, content: "惩罚感" }, + { id: 7, content: "自我厌恶" }, + { id: 8, content: "自我指责" }, + { id: 9, content: "自杀想法或愿望" }, + { id: 10, content: "哭泣" }, + { id: 11, content: "激越" }, + { id: 12, content: "兴趣缺失" }, + { id: 13, content: "犹豫不决" }, + { id: 14, content: "无价值感" }, + { id: 15, content: "精力缺失" }, + { id: 16, content: "睡眠模式改变" }, + { id: 17, content: "易激惹" }, + { id: 18, content: "食欲改变" }, + { id: 19, content: "注意力集中困难" }, + { id: 20, content: "疲劳或疲倦" }, + { id: 21, content: "对性的兴趣缺失" } + ], + renderOptions: (id: number) => { + // Return different options based on different questions + switch (id) { + case 1: // Sadness + return [ + { id: 1, content: "我不感到悲伤", value: "0" }, + { id: 2, content: "我大部分时间感到悲伤", value: "1" }, + { id: 3, content: "我一直感到悲伤", value: "2" }, + { id: 4, content: "我太悲伤或不快乐,无法忍受", value: "3" } + ]; + case 2: // Pessimism + return [ + { id: 1, content: "我对未来不感到特别沮丧", value: "0" }, + { id: 2, content: "我对未来感到比以前更沮丧", value: "1" }, + { id: 3, content: "我不期望事情对我有好转", value: "2" }, + { id: 4, content: "我觉得未来对我来说是无望的,只会变得更糟", value: "3" } + ]; + case 3: // Past failure + return [ + { id: 1, content: "我不觉得自己是个失败者", value: "0" }, + { id: 2, content: "我比大多数人更经常地失败", value: "1" }, + { id: 3, content: "当我回顾我的生活时,我看到的都是失败", value: "2" }, + { id: 4, content: "我觉得自己作为一个人是完全失败的", value: "3" } + ]; + case 4: // Loss of pleasure + return [ + { id: 1, content: "我从事情中得到的满足感和以前一样多", value: "0" }, + { id: 2, content: "我从事情中得到的享受不如以前", value: "1" }, + { id: 3, content: "我从任何事情中得到的真正满足感都很少", value: "2" }, + { id: 4, content: "我从任何事情中都不满足或不快乐", value: "3" } + ]; + case 9: // Suicidal thoughts or wishes + return [ + { id: 1, content: "我没有任何自杀的想法", value: "0" }, + { id: 2, content: "我有自杀的想法,但不会实施", value: "1" }, + { id: 3, content: "我想自杀", value: "2" }, + { id: 4, content: "如果有机会,我会自杀", value: "3" } + ]; + case 16: // Changes in sleeping pattern + return [ + { id: 1, content: "我的睡眠没有任何变化", value: "0" }, + { id: 2, content: "我比平时睡得稍微多一些或少一些", value: "1" }, + { id: 3, content: "我比平时睡得多很多或少很多", value: "2" }, + { id: 4, content: "我几乎整天睡觉,或者我比平时早醒1-2小时且无法再入睡", value: "3" } + ]; + case 18: // Changes in appetite + return [ + { id: 1, content: "我的食欲没有任何变化", value: "0" }, + { id: 2, content: "我的食欲比以前稍微差一些或好一些", value: "1" }, + { id: 3, content: "我的食欲比以前差很多或好很多", value: "2" }, + { id: 4, content: "我完全没有食欲,或者我渴望一直吃东西", value: "3" } + ]; + default: + // Generic option template + return [ + { id: 1, content: "无此症状", value: "0" }, + { id: 2, content: "轻度", value: "1" }, + { id: 3, content: "中度", value: "2" }, + { id: 4, content: "重度", value: "3" } + ]; + } + } +}; diff --git a/questionairies/bigfive/neo-data.ts b/questionairies/bigfive/neo-data.ts new file mode 100644 index 0000000..994ceb5 --- /dev/null +++ b/questionairies/bigfive/neo-data.ts @@ -0,0 +1,473 @@ +export type BigFiveDomain = + | 'neuroticism' + | 'extraversion' + | 'openness' + | 'agreeableness' + | 'conscientiousness'; + +export interface IpipNeoItem { + id: number; + content: string; + domain: BigFiveDomain; + facet: string; + reverse: boolean; +} + +export const ipipNeoFacets = { + "N1": { name: "焦虑", domain: "neuroticism" as BigFiveDomain }, + "N2": { name: "愤怒", domain: "neuroticism" as BigFiveDomain }, + "N3": { name: "抑郁倾向", domain: "neuroticism" as BigFiveDomain }, + "N4": { name: "社交不自在", domain: "neuroticism" as BigFiveDomain }, + "N5": { name: "不节制", domain: "neuroticism" as BigFiveDomain }, + "N6": { name: "脆弱性", domain: "neuroticism" as BigFiveDomain }, + "E1": { name: "友好", domain: "extraversion" as BigFiveDomain }, + "E2": { name: "合群性", domain: "extraversion" as BigFiveDomain }, + "E3": { name: "自信表达", domain: "extraversion" as BigFiveDomain }, + "E4": { name: "活跃度", domain: "extraversion" as BigFiveDomain }, + "E5": { name: "刺激寻求", domain: "extraversion" as BigFiveDomain }, + "E6": { name: "愉悦", domain: "extraversion" as BigFiveDomain }, + "O1": { name: "想象力", domain: "openness" as BigFiveDomain }, + "O2": { name: "艺术兴趣", domain: "openness" as BigFiveDomain }, + "O3": { name: "情感丰富", domain: "openness" as BigFiveDomain }, + "O4": { name: "尝新性", domain: "openness" as BigFiveDomain }, + "O5": { name: "智性", domain: "openness" as BigFiveDomain }, + "O6": { name: "价值开放", domain: "openness" as BigFiveDomain }, + "A1": { name: "信任", domain: "agreeableness" as BigFiveDomain }, + "A2": { name: "真诚", domain: "agreeableness" as BigFiveDomain }, + "A3": { name: "利他", domain: "agreeableness" as BigFiveDomain }, + "A4": { name: "合作", domain: "agreeableness" as BigFiveDomain }, + "A5": { name: "谦逊", domain: "agreeableness" as BigFiveDomain }, + "A6": { name: "同情", domain: "agreeableness" as BigFiveDomain }, + "C1": { name: "自我效能", domain: "conscientiousness" as BigFiveDomain }, + "C2": { name: "条理性", domain: "conscientiousness" as BigFiveDomain }, + "C3": { name: "责任感", domain: "conscientiousness" as BigFiveDomain }, + "C4": { name: "成就追求", domain: "conscientiousness" as BigFiveDomain }, + "C5": { name: "自律", domain: "conscientiousness" as BigFiveDomain }, + "C6": { name: "谨慎", domain: "conscientiousness" as BigFiveDomain }, +} as const; + +export const ipipNeo120Items: IpipNeoItem[] = [ + { id: 1, content: "为很多事情感到担心。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 2, content: "很容易交到朋友。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 3, content: "有丰富的想象力。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 4, content: "信任其他人。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 5, content: "能成功完成任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 6, content: "很容易生气。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 7, content: "喜欢参加大型聚会。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 8, content: "认为艺术是很重要的。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 9, content: "会利用他人来达成自己的目的。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 10, content: "喜欢整洁。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 11, content: "时常感到忧郁。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 12, content: "承担责任。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 13, content: "能强烈感受到自己的情绪。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 14, content: "乐于帮助他人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 15, content: "信守承诺。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 16, content: "感觉自己很难接近别人。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 17, content: "总是很忙碌。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 18, content: "相比墨守成规,我更喜欢有变化。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: false }, + { id: 19, content: "喜欢和别人比试。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 20, content: "工作努力。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 21, content: "大吃大喝寻欢作乐。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 22, content: "喜欢追求刺激。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 23, content: "喜欢阅读有挑战性的材料。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 24, content: "相信自己比别人更好。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 25, content: "我总是有所准备。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 26, content: "容易慌张。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 27, content: "我的快乐会感染周围的人。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 28, content: "认为自己在政治方面非常自由开放。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: false }, + { id: 29, content: "同情流浪汉。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 30, content: "会不经过思考就开始行动。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 31, content: "担心会发生最糟糕的情况。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 32, content: "和其他人待在一起时,我觉得很自在。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 33, content: "喜欢天马行空地幻想。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 34, content: "相信他人的出发点是好的。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 35, content: "在自己所从事的领域出类拔萃。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 36, content: "容易被激怒。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 37, content: "在聚会上,我会和很多不同的人交谈。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 38, content: "我能发现别人看不到的美。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 39, content: "为了领先别人,我会采用作弊的方式。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 40, content: "经常忘记把东西放回原处。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 41, content: "不喜欢自己。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 42, content: "我会尝试去领导他人。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 43, content: "能感受到他人的情绪。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 44, content: "关心他人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 45, content: "我会实话实说。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 46, content: "害怕引起他人的注意。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 47, content: "总是在奔波。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 48, content: "我更喜欢做自己熟悉的事情。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 49, content: "会冲人大喊大叫。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 50, content: "我会超出预期地完成任务或工作。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 51, content: "很少过度放纵自己。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 52, content: "喜欢冒险。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 53, content: "会避免参与关于哲学的讨论。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 54, content: "觉得自己很了不起。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 55, content: "能执行自己制定的计划。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 56, content: "感到被各种事情压得喘不过气来。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 57, content: "我的生活很快乐。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 58, content: "相信没有绝对的对错之分。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: false }, + { id: 59, content: "我同情那些处境不如自己的人。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 60, content: "会做出草率的决定。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 61, content: "对很多事情感到害怕。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 62, content: "避免和他人接触。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 63, content: "喜欢做白日梦。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 64, content: "相信他人说的话。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 65, content: "能轻松完成任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 66, content: "发脾气。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 67, content: "更喜欢一个人独处。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 68, content: "不喜欢诗歌。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 69, content: "占别人的便宜。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 70, content: "我的房间很乱。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 71, content: "经常情绪低落。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 72, content: "掌控局面。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 73, content: "很少察觉到自己的情绪反应。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 74, content: "对他人的感受漠不关心。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 75, content: "破坏规则。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 76, content: "只有和朋友在一起的时候我才会感到自在。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 77, content: "我的闲暇时间非常充实。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 78, content: "不喜欢改变。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 79, content: "侮辱他人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 80, content: "在工作或学习上,我不会多做,过得去就行。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: true }, + { id: 81, content: "能轻松抵御诱惑。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 82, content: "喜欢不计后果地行事。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 83, content: "理解抽象概念对我来说有些困难。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 84, content: "对自己的评价很高。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 85, content: "浪费时间。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 86, content: "感觉自己处理事情力不从心。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 87, content: "热爱生活。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 88, content: "我在政治上比较保守。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 89, content: "对别人遇到的麻烦事不感兴趣。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 90, content: "仓促行事。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 91, content: "我很容易觉得压力大。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 92, content: "和他人保持距离。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 93, content: "喜欢陷入沉思。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 94, content: "不相信别人。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: true }, + { id: 95, content: "知道如何完成任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 96, content: "不会轻易被惹恼。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 97, content: "避开人多的地方。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 98, content: "不喜欢去美术馆。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 99, content: "给别人使坏。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 100, content: "我的东西放得到处都是。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 101, content: "对自己感到满意。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: true }, + { id: 102, content: "等着别人来带头。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 103, content: "不理解那些情绪化的人。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 104, content: "不愿为他人花费时间。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 105, content: "违背自己的承诺。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 106, content: "不会被复杂的社交情境所困扰。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: true }, + { id: 107, content: "喜欢慢慢来。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 108, content: "我喜欢传统的方式。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 109, content: "报复别人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 110, content: "对工作或学业不怎么投入时间和精力。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: true }, + { id: 111, content: "可以控制自己的欲望。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 112, content: "我的行为狂放不羁。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 113, content: "对理论性的讨论不感兴趣。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 114, content: "吹嘘自己的美德。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 115, content: "拖很久才开始做一件事。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 116, content: "在压力下能保持冷静。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 117, content: "看到生活中好的一面。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 118, content: "认为我们应该严惩犯罪。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 119, content: "我尽量不去想那些需要帮助的人。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 120, content: "做事不经过思考。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, +]; + +export const ipipNeo300Items: IpipNeoItem[] = [ + { id: 1, content: "担心事情。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 2, content: "容易生气。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 3, content: "经常感到忧郁。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 4, content: "我很容易被吓倒。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 5, content: "经常吃得太多。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 6, content: "容易惊慌。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 7, content: "轻松交友。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 8, content: "喜欢大型聚会。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 9, content: "负责。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 10, content: "我总是很忙。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 11, content: "爱兴奋。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 12, content: "散发出喜悦。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 13, content: "有生动的想象力。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 14, content: "相信艺术的重要性。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 15, content: "强烈地体验我的情绪。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 16, content: "比起常规,更喜欢多样性。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: false }, + { id: 17, content: "喜欢解决复杂的问题。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 18, content: "倾向于投票给自由派政治候选人。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: false }, + { id: 19, content: "相信别人。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 20, content: "永远不会偷税漏税。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: false }, + { id: 21, content: "让人们有宾至如归的感觉。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 22, content: "我很容易满足。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: false }, + { id: 23, content: "不喜欢成为关注的焦点。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: false }, + { id: 24, content: "同情无家可归者。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 25, content: "顺利完成任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 26, content: "喜欢订单。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 27, content: "尝试遵守规则。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 28, content: "直奔目标而去。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 29, content: "马上把家务事做完。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 30, content: "避免错误。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: false }, + { id: 31, content: "担心最坏的情况。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 32, content: "容易被激怒。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 33, content: "不喜欢我自己。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 34, content: "我害怕我会做错事。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 35, content: "不知道为什么我要做一些事情。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 36, content: "因事件而不知所措。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 37, content: "迅速与他人热络起来。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 38, content: "在聚会上与很多不同的人交谈。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 39, content: "尝试领导别人。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 40, content: "我总是在旅途中。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 41, content: "寻求冒险。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 42, content: "玩得很开心。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 43, content: "享受狂野的幻想飞行。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 44, content: "就像音乐一样。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 45, content: "感受别人的情绪。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 46, content: "喜欢参观新地方。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: false }, + { id: 47, content: "喜欢阅读具有挑战性的材料。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 48, content: "相信没有绝对的对与错。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: false }, + { id: 49, content: "相信别人都是善意的。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 50, content: "遵守规则。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: false }, + { id: 51, content: "预测他人的需求。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 52, content: "不能忍受对抗。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: false }, + { id: 53, content: "不喜欢谈论我自己。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: false }, + { id: 54, content: "对那些比我处境更糟的人表示同情。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 55, content: "在我所做的事情上表现出色。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 56, content: "喜欢收拾。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 57, content: "遵守我的承诺。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 58, content: "努力工作。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 59, content: "我时刻准备着。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 60, content: "谨慎选择我的用词。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: false }, + { id: 61, content: "我害怕很多事情。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 62, content: "容易心烦意乱。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 63, content: "我经常心情低落。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 64, content: "发现很难接近别人。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 65, content: "做一些事后让我后悔的事情。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 66, content: "感觉自己处理不了事情。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 67, content: "与人相处时感到舒服。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 68, content: "享受成为团体的一员。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 69, content: "可以说服别人做事。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 70, content: "空闲时间做很多事情。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 71, content: "爱行动。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 72, content: "表达童趣。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 73, content: "爱做白日梦。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 74, content: "看到别人可能没有注意到的事物的美丽。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 75, content: "对事业充满热情。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 76, content: "对很多事情都很感兴趣。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: false }, + { id: 77, content: "拥有丰富的词汇量。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 78, content: "相信罪犯应该得到帮助而不是惩罚。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: false }, + { id: 79, content: "相信人们所说的。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 80, content: "利用奉承来获得成功。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 81, content: "喜欢帮助别人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 82, content: "讨厌显得咄咄逼人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: false }, + { id: 83, content: "认为自己是一个普通人。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: false }, + { id: 84, content: "重视合作胜于竞争。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 85, content: "顺利地处理任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 86, content: "希望一切都“恰到好处”。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 87, content: "按时支付我的账单。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 88, content: "将计划转化为行动。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 89, content: "立即开始任务。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 90, content: "坚持我选择的路。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: false }, + { id: 91, content: "容易感到压力。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 92, content: "我经常心情不好。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 93, content: "对自己评价很低。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 94, content: "我害怕引起别人的注意。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 95, content: "继续狂欢吧。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 96, content: "我拿不定主意。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 97, content: "与他人相处时表现得轻松自在。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 98, content: "让其他人参与我正在做的事情。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 99, content: "寻求影响他人。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 100, content: "可以同时管理很多事情。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 101, content: "享受成为喧闹人群的一部分。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 102, content: "笑我的人生。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 103, content: "喜欢陷入沉思。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 104, content: "爱花。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 105, content: "享受审视我自己和我的生活。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 106, content: "喜欢开始新事物。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: false }, + { id: 107, content: "可以处理很多信息。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 108, content: "相信一种真正的宗教。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 109, content: "相信人基本上都是有道德的。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 110, content: "利用别人来达到我自己的目的。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 111, content: "我关心别人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 112, content: "舌头要锋利。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 113, content: "很少自吹自擂。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: false }, + { id: 114, content: "承受别人的痛苦。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: false }, + { id: 115, content: "我确信我的立场。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 116, content: "喜欢秩序和规律。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 117, content: "说实话。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 118, content: "全心全意地投入到任务中。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 119, content: "立即开始工作。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 120, content: "不假思索地投入事物。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 121, content: "陷入我的问题中。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: false }, + { id: 122, content: "发脾气吧。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: false }, + { id: 123, content: "经常情绪波动。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 124, content: "只有和朋友在一起才感觉舒服。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 125, content: "爱吃。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: false }, + { id: 126, content: "被情绪淹没。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: false }, + { id: 127, content: "让人们振作起来。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: false }, + { id: 128, content: "喜欢惊喜派对。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: false }, + { id: 129, content: "掌控一切。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: false }, + { id: 130, content: "反应快。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: false }, + { id: 131, content: "享受鲁莽的感觉。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 132, content: "热爱生活。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 133, content: "沉浸在我的幻想中吧。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 134, content: "享受大自然的美丽。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: false }, + { id: 135, content: "尝试去了解我自己。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: false }, + { id: 136, content: "更喜欢坚持我所知道的事情。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 137, content: "享受思考事物的乐趣。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: false }, + { id: 138, content: "倾向于投票给保守派政治候选人。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 139, content: "相信人性本善。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 140, content: "知道如何规避规则。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 141, content: "给大家说句好话。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: false }, + { id: 142, content: "顶撞别人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 143, content: "相信我比别人更好。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 144, content: "对别人的问题不感兴趣。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 145, content: "拿出好的解决方案。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 146, content: "按照计划做事。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: false }, + { id: 147, content: "听听我的良心吧。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: false }, + { id: 148, content: "做比我期望的更多的事情。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 149, content: "执行我的计划。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: false }, + { id: 150, content: "做出鲁莽的决定。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 151, content: "我不容易被事情打扰。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: true }, + { id: 152, content: "很少会被激怒。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 153, content: "感到绝望。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 154, content: "被我的话绊倒了。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: false }, + { id: 155, content: "很少放纵过度。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 156, content: "在压力下保持冷静。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 157, content: "我很难认识。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 158, content: "更喜欢一个人呆着。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 159, content: "等待其他人带路。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 160, content: "喜欢轻松点。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 161, content: "表现得狂野而疯狂。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 162, content: "看看生活光明的一面。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 163, content: "花时间反思事情。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: false }, + { id: 164, content: "不喜欢艺术。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 165, content: "很少情绪化。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 166, content: "不喜欢改变。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 167, content: "我对抽象的想法不感兴趣。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 168, content: "相信太多的税款都用来支持艺术家了。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 169, content: "认为一切都会好起来的。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: false }, + { id: 170, content: "作弊才能取得成功。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 171, content: "看不起别人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 172, content: "喜欢好好战斗。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 173, content: "高度评价我自己。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 174, content: "往往不喜欢心软的人。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 175, content: "知道如何把事情做好。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: false }, + { id: 176, content: "经常忘记把东西放回原来的位置。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 177, content: "打破规则。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 178, content: "为自己和他人设定高标准。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 179, content: "发现很难开始工作。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 180, content: "喜欢随心所欲地行动。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 181, content: "大部分时间我都很放松。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: true }, + { id: 182, content: "很少生气。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 183, content: "感觉自己的人生缺乏方向。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: false }, + { id: 184, content: "我不轻易感到尴尬。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: true }, + { id: 185, content: "轻松抵制诱惑。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 186, content: "可以处理复杂的问题。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 187, content: "经常在别人身边感到不舒服。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 188, content: "想要一个人呆着。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 189, content: "保持在后台。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 190, content: "喜欢慢慢来。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 191, content: "我愿意尝试任何事情一次。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 192, content: "大声笑。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 193, content: "很少做白日梦。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: true }, + { id: 194, content: "不喜欢诗。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 195, content: "我不容易受情绪影响。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 196, content: "不喜欢改变的想法。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 197, content: "避免哲学讨论。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 198, content: "相信法律应该得到严格执行。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 199, content: "不信任人。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: true }, + { id: 200, content: "让人们承受压力。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 201, content: "对别人的感受漠不关心。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 202, content: "对人大喊大叫。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 203, content: "对自己有很高的评价。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 204, content: "相信以眼还眼。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 205, content: "误判情况。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: true }, + { id: 206, content: "把我的房间弄得乱七八糟。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 207, content: "违背我的诺言。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 208, content: "要求品质。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: false }, + { id: 209, content: "浪费我的时间。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 210, content: "冲进事情。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 211, content: "我不容易被事件打扰。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: true }, + { id: 212, content: "我不轻易生气。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 213, content: "很少感到忧郁。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: true }, + { id: 214, content: "在不熟悉的环境中感到自在。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: true }, + { id: 215, content: "我能够控制自己的欲望。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 216, content: "知道如何应对。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 217, content: "避免与他人接触。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 218, content: "不喜欢拥挤的活动。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 219, content: "没什么可说的。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 220, content: "喜欢悠闲的生活方式。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 221, content: "寻求危险。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: false }, + { id: 222, content: "逗我的朋友们开心吧。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: false }, + { id: 223, content: "没有很好的想象力。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: true }, + { id: 224, content: "不喜欢去艺术博物馆。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 225, content: "很少注意到我的情绪反应。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 226, content: "我是一个习惯的生物。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 227, content: "难以理解抽象概念。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 228, content: "相信我们对犯罪分子的纵容太多了。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 229, content: "怀疑他人隐藏的动机。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: true }, + { id: 230, content: "假装关心别人。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 231, content: "让人感觉不舒服。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 232, content: "辱骂人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 233, content: "知道许多问题的答案。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 234, content: "尽量不要去想有需要的人。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 235, content: "不懂事。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: true }, + { id: 236, content: "把我的物品留在周围。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 237, content: "让其他人履行我的职责。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 238, content: "我没有很强的成功动力。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: true }, + { id: 239, content: "需要推动才能开始。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 240, content: "做一些疯狂的事情。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 241, content: "不要担心已经发生的事情。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: true }, + { id: 242, content: "保持我的冷静。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 243, content: "对自己感到舒服。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: true }, + { id: 244, content: "我不会被困难的社交场合所困扰。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: true }, + { id: 245, content: "永远不要花超出我承受能力的钱。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 246, content: "轻松克服挫折。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 247, content: "我对其他人并不真正感兴趣。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 248, content: "避开人群。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 249, content: "不喜欢引起别人的注意。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 250, content: "让事情按照自己的节奏进行。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 251, content: "永远不会去滑翔或蹦极。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: true }, + { id: 252, content: "我不容易被逗乐。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: true }, + { id: 253, content: "很少陷入沉思。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: true }, + { id: 254, content: "不喜欢音乐会。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 255, content: "很少经历情绪的高潮和低谷。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 256, content: "不喜欢新食物。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 257, content: "我对理论讨论不感兴趣。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 258, content: "相信我们应该严厉打击犯罪。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 259, content: "我对其他人保持警惕。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: true }, + { id: 260, content: "利用别人的优势。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 261, content: "背弃别人。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 262, content: "报复别人。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 263, content: "夸耀我的美德。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 264, content: "相信人们应该自力更生。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 265, content: "几乎没有贡献。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: true }, + { id: 266, content: "我不被乱七八糟的人打扰。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 267, content: "做与要求相反的事情。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 268, content: "做足够的工作来度过难关。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: true }, + { id: 269, content: "开始任务有困难。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 270, content: "不假思索地行动。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, + { id: 271, content: "轻松适应新情况。", domain: "neuroticism" as BigFiveDomain, facet: "N1", reverse: true }, + { id: 272, content: "很少抱怨。", domain: "neuroticism" as BigFiveDomain, facet: "N2", reverse: true }, + { id: 273, content: "我对自己很满意。", domain: "neuroticism" as BigFiveDomain, facet: "N3", reverse: true }, + { id: 274, content: "我能够为自己挺身而出。", domain: "neuroticism" as BigFiveDomain, facet: "N4", reverse: true }, + { id: 275, content: "永远不要挥霍。", domain: "neuroticism" as BigFiveDomain, facet: "N5", reverse: true }, + { id: 276, content: "即使在紧张的情况下也能保持冷静。", domain: "neuroticism" as BigFiveDomain, facet: "N6", reverse: true }, + { id: 277, content: "与他人保持距离。", domain: "extraversion" as BigFiveDomain, facet: "E1", reverse: true }, + { id: 278, content: "寻求安静。", domain: "extraversion" as BigFiveDomain, facet: "E2", reverse: true }, + { id: 279, content: "保留我的意见。", domain: "extraversion" as BigFiveDomain, facet: "E3", reverse: true }, + { id: 280, content: "慢慢反应。", domain: "extraversion" as BigFiveDomain, facet: "E4", reverse: true }, + { id: 281, content: "不喜欢吵闹的音乐。", domain: "extraversion" as BigFiveDomain, facet: "E5", reverse: true }, + { id: 282, content: "很少开玩笑。", domain: "extraversion" as BigFiveDomain, facet: "E6", reverse: true }, + { id: 283, content: "难以想象事情。", domain: "openness" as BigFiveDomain, facet: "O1", reverse: true }, + { id: 284, content: "不喜欢观看舞蹈表演。", domain: "openness" as BigFiveDomain, facet: "O2", reverse: true }, + { id: 285, content: "不懂感情用事的人。", domain: "openness" as BigFiveDomain, facet: "O3", reverse: true }, + { id: 286, content: "我执着于传统的方式。", domain: "openness" as BigFiveDomain, facet: "O4", reverse: true }, + { id: 287, content: "避免阅读困难的材料。", domain: "openness" as BigFiveDomain, facet: "O5", reverse: true }, + { id: 288, content: "喜欢在奏国歌时起立。", domain: "openness" as BigFiveDomain, facet: "O6", reverse: true }, + { id: 289, content: "相信人性本恶。", domain: "agreeableness" as BigFiveDomain, facet: "A1", reverse: true }, + { id: 290, content: "阻碍别人的计划。", domain: "agreeableness" as BigFiveDomain, facet: "A2", reverse: true }, + { id: 291, content: "没有时间为别人着想。", domain: "agreeableness" as BigFiveDomain, facet: "A3", reverse: true }, + { id: 292, content: "怀恨在心。", domain: "agreeableness" as BigFiveDomain, facet: "A4", reverse: true }, + { id: 293, content: "让自己成为众人瞩目的焦点。", domain: "agreeableness" as BigFiveDomain, facet: "A5", reverse: true }, + { id: 294, content: "受不了弱者。", domain: "agreeableness" as BigFiveDomain, facet: "A6", reverse: true }, + { id: 295, content: "看不到事情的后果。", domain: "conscientiousness" as BigFiveDomain, facet: "C1", reverse: true }, + { id: 296, content: "我不被混乱所困扰。", domain: "conscientiousness" as BigFiveDomain, facet: "C2", reverse: true }, + { id: 297, content: "歪曲事实。", domain: "conscientiousness" as BigFiveDomain, facet: "C3", reverse: true }, + { id: 298, content: "在工作上投入很少的时间和精力。", domain: "conscientiousness" as BigFiveDomain, facet: "C4", reverse: true }, + { id: 299, content: "推迟决定。", domain: "conscientiousness" as BigFiveDomain, facet: "C5", reverse: true }, + { id: 300, content: "经常在最后一刻制定计划。", domain: "conscientiousness" as BigFiveDomain, facet: "C6", reverse: true }, +]; diff --git a/questionairies/bigfive/neo120-zh.ts b/questionairies/bigfive/neo120-zh.ts new file mode 100644 index 0000000..795efcd --- /dev/null +++ b/questionairies/bigfive/neo120-zh.ts @@ -0,0 +1,56 @@ +import { Questionnaire } from '@/types'; +import { ipipNeo120Items } from './neo-data'; + +const options = [ + { id: 1, content: '非常不符合', value: '1' }, + { id: 2, content: '比较不符合', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '比较符合', value: '4' }, + { id: 5, content: '非常符合', value: '5' }, +]; + +export const bigfive120: Questionnaire = { + id: 'bigfive-120', + title: 'IPIP-NEO 五大人格量表(120题)', + description: '查看五大人格及30个细分面向,比50题版更细致', + category: '人格', + tags: ['120题', '自评'], + time: '20-30分钟', + details: { + introduction: + 'IPIP-NEO-120 是 Johnson 编制的五大人格短版,每个细分面向包含4个项目,共评估五个大维度和30个面向。本项目采用 IPIP 官方站收录、经过翻译与回译流程的普通话版本。结果用于自我探索,不是临床诊断。', + questionCount: '120个项目', + evaluationTime: '通常为20-30分钟', + instructions: + '请根据你平时的一贯表现作答。不要花太久推敲单题,选择最接近真实自己的选项。', + scoringMethod: [ + '每题1-5分,部分项目反向计分。', + '每个面向包含4个项目;每个大维度由6个面向、共24个项目构成。', + '结果显示平均分,便于在项目数量不同的版本之间理解相对倾向。', + ], + dimensions: [ + { name: '神经质', description: '情绪敏感、压力反应和负面情绪体验倾向。' }, + { name: '外向性', description: '社交投入、活跃度、自信表达和积极情绪。' }, + { name: '开放性', description: '想象力、审美、求知、尝新和价值开放。' }, + { name: '宜人性', description: '信任、真诚、合作、利他、谦逊和同情。' }, + { name: '尽责性', description: '效能、条理、责任、自律、成就追求和谨慎。' }, + ], + notes: [ + '人格维度没有绝对好坏,高低分代表不同的行为和体验风格。', + '普通话版本具有中国样本研究资料,但个人结果仍会受状态和作答方式影响。', + '本量表适合自我探索,不用于临床诊断或单独作为人员筛选结论。', + ], + references: [ + { + text: 'IPIP:普通话 IPIP-NEO-120 译本', + url: 'https://ipip.ori.org/Mandarin%20translation%20of%20IPIP-NEO-120.htm', + }, + { + text: 'IPIP:Johnson 120题计分键', + url: 'https://ipip.ori.org/30FacetNEO-PI-RItems.htm', + }, + ], + }, + questions: ipipNeo120Items.map(({ id, content }) => ({ id, content })), + renderOptions: () => options, +}; diff --git a/questionairies/bigfive/neo300-zh.ts b/questionairies/bigfive/neo300-zh.ts new file mode 100644 index 0000000..fb2e616 --- /dev/null +++ b/questionairies/bigfive/neo300-zh.ts @@ -0,0 +1,52 @@ +import { Questionnaire } from '@/types'; +import { ipipNeo300Items } from './neo-data'; + +const options = [ + { id: 1, content: '非常不符合', value: '1' }, + { id: 2, content: '比较不符合', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '比较符合', value: '4' }, + { id: 5, content: '非常符合', value: '5' }, +]; + +export const bigfive300: Questionnaire = { + id: 'bigfive-300', + title: 'IPIP-NEO 五大人格量表(300题)', + description: '最完整的五大人格长版,耗时较长,适合需要详细结果时使用', + category: '人格', + tags: ['300题', '自评'], + time: '45-70分钟', + details: { + introduction: + 'IPIP-NEO-300 使用 IPIP 的30个 NEO 对应面向,每个面向包含10个项目。它能提供比短版更细致的自我报告画像。本项目采用官方英文题键,并提供中文工作译本;该中文文本尚未进行独立的本土化信效度验证。', + questionCount: '300个项目', + evaluationTime: '通常为45-70分钟', + instructions: + '请根据长期、稳定的日常表现作答。题目较多,可以利用自动保存功能分段完成。', + scoringMethod: [ + '每题1-5分,按照 IPIP 官方题键处理正向与反向项目。', + '每个面向包含10个项目;每个大维度由6个面向、共60个项目构成。', + '结果显示五大维度与30个面向的平均分。', + ], + dimensions: [ + { name: '神经质', description: '情绪敏感、压力反应和负面情绪体验倾向。' }, + { name: '外向性', description: '社交投入、活跃度、自信表达和积极情绪。' }, + { name: '开放性', description: '想象力、审美、求知、尝新和价值开放。' }, + { name: '宜人性', description: '信任、真诚、合作、利他、谦逊和同情。' }, + { name: '尽责性', description: '效能、条理、责任、自律、成就追求和谨慎。' }, + ], + notes: [ + '人格维度没有绝对好坏,高低分代表不同的行为和体验风格。', + '300题中文文本是工作译本,适合个人探索,不应视为标准化中文版。', + '本量表不用于临床诊断或单独作为人员筛选结论。', + ], + references: [ + { + text: 'IPIP:300题 NEO 对应面向与计分键', + url: 'https://ipip.ori.org/newNEOFacetsKey.htm', + }, + ], + }, + questions: ipipNeo300Items.map(({ id, content }) => ({ id, content })), + renderOptions: () => options, +}; diff --git a/questionairies/bigfive/zh.ts b/questionairies/bigfive/zh.ts new file mode 100644 index 0000000..eb33067 --- /dev/null +++ b/questionairies/bigfive/zh.ts @@ -0,0 +1,102 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不符合', value: '1' }, + { id: 2, content: '比较不符合', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '比较符合', value: '4' }, + { id: 5, content: '非常符合', value: '5' }, +]; + +export const bigfive: Questionnaire = { + id: 'bigfive', + title: 'IPIP Big Five 五大人格量表(50题)', + description: '快速了解五大人格维度,适合第一次进行人格测试', + category: '人格', + tags: ['自评'], + time: '8-12分钟', + details: { + introduction: + 'IPIP Big Five 50题版来自 International Personality Item Pool 的五因素人格项目池,用于快速了解五大人格维度的相对倾向。结果反映的是自我报告下的人格特征,不是诊断。', + questionCount: '50个项目', + evaluationTime: '通常为8-12分钟', + instructions: + '请根据你平时的一贯表现作答,而不是只根据今天的心情。每题从“非常不符合”到“非常符合”中选择最贴近自己的选项。', + scoringMethod: [ + '每题1-5分,部分项目反向计分。', + '每个维度包含10个项目,得分范围10-50分。', + '得分越高,表示该维度特征越明显;中间分数表示该特征处于相对平衡状态。', + ], + dimensions: [ + { name: '外向性', description: '社交主动性、活跃度、表达欲和积极情绪。' }, + { name: '宜人性', description: '信任、合作、体贴、同理心和亲社会倾向。' }, + { name: '尽责性', description: '自律、条理、可靠性、计划性和目标坚持。' }, + { name: '情绪稳定性', description: '情绪平稳、压力耐受和较少焦虑烦躁。' }, + { name: '开放性', description: '想象力、审美、好奇心、创造力和新经验接纳度。' }, + ], + notes: [ + '人格测评没有绝对好坏,高低分代表不同风格。', + '结果会受到当下状态、自我理解和作答诚实度影响。', + '该量表适合作为自我探索材料,不用于临床诊断或职业筛选定论。', + ], + references: [ + { + text: 'International Personality Item Pool: 50-item Big-Five factor markers', + url: 'https://ipip.ori.org/new_ipip-50-item-scale.htm', + }, + ], + }, + questions: [ + { id: 1, content: '我是聚会或集体活动中比较活跃的人。' }, + { id: 2, content: '我不太关心别人的感受。' }, + { id: 3, content: '我总是提前做好准备。' }, + { id: 4, content: '我容易感到压力很大。' }, + { id: 5, content: '我的词汇量比较丰富。' }, + { id: 6, content: '我平时话不多。' }, + { id: 7, content: '我对别人很感兴趣。' }, + { id: 8, content: '我常把东西随手乱放。' }, + { id: 9, content: '我大多数时候比较放松。' }, + { id: 10, content: '我理解抽象概念有困难。' }, + { id: 11, content: '在人群中我通常感觉自在。' }, + { id: 12, content: '我有时会冒犯别人。' }, + { id: 13, content: '我很注意细节。' }, + { id: 14, content: '我很少感到忧郁或沮丧。' }, + { id: 15, content: '我有丰富的想象力。' }, + { id: 16, content: '我经常保持低调,不愿成为焦点。' }, + { id: 17, content: '我会体谅别人的感受。' }, + { id: 18, content: '我经常把事情弄得一团糟。' }, + { id: 19, content: '我容易担心。' }, + { id: 20, content: '我对抽象想法不太感兴趣。' }, + { id: 21, content: '我常主动开启谈话。' }, + { id: 22, content: '我不太关心别人的问题。' }, + { id: 23, content: '我会立即把家务或任务处理好。' }, + { id: 24, content: '我的情绪很容易被打扰。' }, + { id: 25, content: '我有很多想法。' }, + { id: 26, content: '我很少主动说话。' }, + { id: 27, content: '我心肠比较软。' }, + { id: 28, content: '我经常忘记把东西放回原处。' }, + { id: 29, content: '我很容易心烦。' }, + { id: 30, content: '我想象力不太丰富。' }, + { id: 31, content: '我会和许多不同的人交谈。' }, + { id: 32, content: '我对别人没有太多兴趣。' }, + { id: 33, content: '我喜欢有秩序。' }, + { id: 34, content: '我的情绪经常起伏。' }, + { id: 35, content: '我能很快理解复杂想法。' }, + { id: 36, content: '我不喜欢把注意力集中到自己身上。' }, + { id: 37, content: '我愿意花时间帮助别人。' }, + { id: 38, content: '我有时会逃避自己的责任。' }, + { id: 39, content: '我的情绪变化比较频繁。' }, + { id: 40, content: '我会使用一些比较复杂或有深度的词语。' }, + { id: 41, content: '我不介意成为大家关注的中心。' }, + { id: 42, content: '我能感受到别人的情绪变化。' }, + { id: 43, content: '我会按计划行事。' }, + { id: 44, content: '我容易被激怒。' }, + { id: 45, content: '我会花时间思考事物背后的意义。' }, + { id: 46, content: '和陌生人在一起时我会比较沉默。' }, + { id: 47, content: '我会让别人感到自在。' }, + { id: 48, content: '我做事经常不够细致。' }, + { id: 49, content: '我经常感到忧郁。' }, + { id: 50, content: '我脑子里常常充满各种想法。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/career-anchors/zh.ts b/questionairies/career-anchors/zh.ts new file mode 100644 index 0000000..e051a00 --- /dev/null +++ b/questionairies/career-anchors/zh.ts @@ -0,0 +1,104 @@ +import { Questionnaire } from "@/types"; + +const options = [ + { id: 1, content: "完全不同意", value: "1" }, + { id: 2, content: "不太同意", value: "2" }, + { id: 3, content: "有点同意", value: "3" }, + { id: 4, content: "比较同意", value: "4" }, + { id: 5, content: "非常同意", value: "5" }, +]; + +export const careerAnchors: Questionnaire = { + id: "career-anchors", + title: "Career Anchors 职业锚", + description: "识别职业选择中最难放弃的核心动机、价值和能力偏好", + category: "职业", + tags: ["自评"], + time: "6-8分钟", + evaluation: { + academicRecognition: "B", + retestSuitable: true, + recommendedInterval: "1年", + }, + details: { + introduction: + "职业锚理论由Edgar Schein提出,用来描述一个人在职业发展中逐渐稳定下来的核心价值、动机和自我能力认知。职业锚不是职业兴趣本身,而是你在选择工作环境、角色和发展路径时最不愿意牺牲的东西。", + questionCount: "40个项目", + evaluationTime: "通常6-8分钟", + instructions: + "请根据你对理想工作和职业发展的真实偏好作答。如果你尚未有很多工作经历,可以按你对未来工作的期待回答。", + scoringMethod: [ + "每题1-5分,分别计入8个职业锚。", + "每个职业锚由5题组成,取平均分,范围1-5分。", + "结果重点看最高分和第二高分,它们通常代表你做职业选择时最重要的约束条件。", + ], + dimensions: [ + { name: "技术/职能能力", description: "希望在专业领域持续精进,成为可靠专家。" }, + { name: "综合管理能力", description: "希望整合资源、带团队、承担更大组织责任。" }, + { name: "自主/独立", description: "希望拥有较高自由度,自己决定工作方式。" }, + { name: "安全/稳定", description: "重视稳定收入、可预期环境和长期保障。" }, + { name: "创业创造", description: "希望创造新产品、业务或组织,并拥有主导权。" }, + { name: "服务/奉献", description: "希望工作能服务某种使命、价值或社会目标。" }, + { name: "纯粹挑战", description: "被高难度问题、竞争和突破限制所驱动。" }, + { name: "生活方式", description: "希望职业与家庭、健康、自由时间和整体生活平衡。" }, + ], + notes: [ + "职业锚适合用于职业规划,不是能力测验,也不直接决定你适合哪个行业。", + "高分职业锚之间可能冲突,例如自主独立与安全稳定,需要结合现实机会取舍。", + "职业锚会随经历更清晰,早期结果可作为假设而非最终结论。", + ], + references: [ + { + text: "Schein Career Anchors overview", + url: "https://www.businessballs.com/personal-and-professional-development/career-anchors-edgar-schein/", + }, + { + text: "Career anchors and occupational choices", + url: "https://pmc.ncbi.nlm.nih.gov/articles/PMC5600941/", + }, + ], + }, + questions: [ + { id: 1, content: "我希望在某个专业领域做到很深,而不只是做泛泛的管理。" }, + { id: 2, content: "我喜欢通过专业能力解决别人解决不了的问题。" }, + { id: 3, content: "成为某个领域的专家会让我有成就感。" }, + { id: 4, content: "如果不能持续提升专业技能,我会对工作失去兴趣。" }, + { id: 5, content: "我更愿意因专业水平而被认可,而不是只因头衔被认可。" }, + { id: 6, content: "我希望未来能负责更大的团队或组织目标。" }, + { id: 7, content: "协调人、资源和策略对我有吸引力。" }, + { id: 8, content: "我愿意承担管理决策带来的复杂责任。" }, + { id: 9, content: "我希望自己的职业路径能逐步扩大影响范围。" }, + { id: 10, content: "我喜欢让不同专业的人一起完成共同目标。" }, + { id: 11, content: "我希望工作方式有较大自主权。" }, + { id: 12, content: "严格受控的工作环境会让我感到受限。" }, + { id: 13, content: "比起固定流程,我更希望自己决定如何完成任务。" }, + { id: 14, content: "我愿意为了自由度放弃一部分稳定性。" }, + { id: 15, content: "独立完成并拥有成果对我很重要。" }, + { id: 16, content: "稳定收入和明确保障会显著影响我的职业选择。" }, + { id: 17, content: "我偏好规则清楚、风险较低的组织环境。" }, + { id: 18, content: "如果工作前景不稳定,我会很难安心投入。" }, + { id: 19, content: "我希望职业发展有可预期的路径。" }, + { id: 20, content: "福利、保障和长期安全感对我很重要。" }, + { id: 21, content: "我有创造新产品、新业务或新组织的冲动。" }, + { id: 22, content: "看到一个想法从零变成现实会让我兴奋。" }, + { id: 23, content: "我希望自己能主导某个项目或事业的方向。" }, + { id: 24, content: "我愿意为创造自己的东西承担风险。" }, + { id: 25, content: "比起维护现有系统,我更喜欢开创新机会。" }, + { id: 26, content: "我希望工作能服务某种我认同的价值或使命。" }, + { id: 27, content: "如果工作对他人或社会有帮助,我会更有动力。" }, + { id: 28, content: "只追求收入或地位的工作很难长期吸引我。" }, + { id: 29, content: "我愿意为了有意义的目标承受额外压力。" }, + { id: 30, content: "我希望自己的职业能体现某种责任感或贡献。" }, + { id: 31, content: "高难度问题会激发我的斗志。" }, + { id: 32, content: "我喜欢突破限制、战胜竞争或完成不可能任务。" }, + { id: 33, content: "太容易的工作会让我很快失去兴趣。" }, + { id: 34, content: "我愿意主动选择更有挑战的任务。" }, + { id: 35, content: "我享受在压力和复杂局面中证明自己。" }, + { id: 36, content: "职业不能长期牺牲我的生活、健康或家庭。" }, + { id: 37, content: "我希望工作安排能和整体生活目标相协调。" }, + { id: 38, content: "即使机会很好,如果严重破坏生活平衡,我也会犹豫。" }, + { id: 39, content: "自由时间和生活质量是我评价工作的关键指标。" }, + { id: 40, content: "我希望职业成功服务于生活,而不是完全吞掉生活。" }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/crt/zh.ts b/questionairies/crt/zh.ts new file mode 100644 index 0000000..2beab0e --- /dev/null +++ b/questionairies/crt/zh.ts @@ -0,0 +1,92 @@ +import { Questionnaire } from '@/types'; + +const optionsByQuestion: Record = { + 1: [ + { id: 1, content: '0.10元', value: 'A' }, + { id: 2, content: '0.05元', value: 'B' }, + { id: 3, content: '0.01元', value: 'C' }, + { id: 4, content: '0.50元', value: 'D' }, + ], + 2: [ + { id: 1, content: '5分钟', value: 'A' }, + { id: 2, content: '20分钟', value: 'B' }, + { id: 3, content: '100分钟', value: 'C' }, + { id: 4, content: '500分钟', value: 'D' }, + ], + 3: [ + { id: 1, content: '24天', value: 'A' }, + { id: 2, content: '47天', value: 'B' }, + { id: 3, content: '48天', value: 'C' }, + { id: 4, content: '12天', value: 'D' }, + ], + 4: [ + { id: 1, content: '4天', value: 'A' }, + { id: 2, content: '6天', value: 'B' }, + { id: 3, content: '9天', value: 'C' }, + { id: 4, content: '18天', value: 'D' }, + ], + 5: [ + { id: 1, content: '30人', value: 'A' }, + { id: 2, content: '29人', value: 'B' }, + { id: 3, content: '31人', value: 'C' }, + { id: 4, content: '15人', value: 'D' }, + ], + 6: [ + { id: 1, content: '10元', value: 'A' }, + { id: 2, content: '20元', value: 'B' }, + { id: 3, content: '0元', value: 'C' }, + { id: 4, content: '30元', value: 'D' }, + ], + 7: [ + { id: 1, content: '第一名', value: 'A' }, + { id: 2, content: '第二名', value: 'B' }, + { id: 3, content: '第三名', value: 'C' }, + { id: 4, content: '无法判断', value: 'D' }, + ], +}; + +export const crt: Questionnaire = { + id: 'crt', + title: 'CRT 认知反思测试(7题)', + description: '评估抑制直觉答案、进行反思推理的倾向', + category: '认知', + tags: [], + time: '3-6分钟', + details: { + introduction: + 'CRT 用于观察人在面对直觉上很容易答错的问题时,是否会停下来重新检查并进行反思推理。本项目采用7题单选版,方便在当前测评工具中作答和计分。', + questionCount: '7个项目', + evaluationTime: '通常为3-6分钟', + instructions: + '请不要急着选择第一眼看到的答案。每题只有一个正确答案,建议在心里算清楚后再作答。', + scoringMethod: [ + '每题答对1分,答错0分,总分0-7分。', + '分数越高,表示越能抑制直觉错误并进行反思推理。', + 'CRT 会受到题目熟悉度、数学经验和注意力状态影响。', + ], + dimensions: [ + { name: '认知反思', description: '发现直觉答案可能错误,并切换到更慢、更审慎的推理。' }, + ], + notes: [ + 'CRT 不是完整智力测验,也不能代表全部认知能力。', + '如果之前见过题目,结果会明显偏高。', + '建议把结果作为思维风格参考,而不是能力标签。', + ], + references: [ + { + text: 'Frederick, S. (2005). Cognitive Reflection and Decision Making.', + url: 'https://sjdm.org/dmidi/Cognitive_Reflection_Test.html', + }, + ], + }, + questions: [ + { id: 1, content: '球拍和球一共1.10元。球拍比球贵1.00元。球多少钱?' }, + { id: 2, content: '5台机器用5分钟生产5个零件。100台机器生产100个零件需要多久?' }, + { id: 3, content: '湖里有一片睡莲,每天面积翻倍。48天覆盖整个湖,覆盖半个湖需要多少天?' }, + { id: 4, content: '小明6天喝完一桶水,小红12天喝完一桶水。两人一起喝完一桶水需要多久?' }, + { id: 5, content: '小李考试成绩是全班第15高,同时也是第15低。班上一共有多少人?' }, + { id: 6, content: '一个人60元买入一只猪,70元卖出;又80元买回,90元卖出。总共赚了多少钱?' }, + { id: 7, content: '跑步比赛中,你超过了第二名。现在你是第几名?' }, + ], + renderOptions: (id: number) => optionsByQuestion[id] || optionsByQuestion[1], +}; diff --git a/questionairies/dark-triad/zh.ts b/questionairies/dark-triad/zh.ts new file mode 100644 index 0000000..62c234c --- /dev/null +++ b/questionairies/dark-triad/zh.ts @@ -0,0 +1,86 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不同意', value: '1' }, + { id: 2, content: '不同意', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '同意', value: '4' }, + { id: 5, content: '非常同意', value: '5' }, +]; + +export const darkTriad: Questionnaire = { + id: 'dark-triad', + title: 'Short Dark Triad 黑暗三联征(27题)', + description: '评估马基雅维利主义、自恋和冷酷冲动三个非临床人格倾向', + category: '人格', + tags: ['自恋', '自评'], + time: '5-8分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'Short Dark Triad 用于评估非临床人群中的三类社会性较强但可能带来关系风险的人格倾向:策略性操控、自我优越感和冷酷冲动。结果应作为自我观察,不是诊断或道德判决。', + questionCount: '27个项目', + evaluationTime: '通常为5-8分钟', + instructions: + '请根据平时真实倾向作答。某些题目比较直接,回答时不需要追求“看起来正确”。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '分别计算马基雅维利主义、自恋和冷酷冲动三个维度的平均分。', + '分数越高表示该倾向越明显,但不能单独用于临床判断。', + ], + dimensions: [ + { name: '马基雅维利主义', description: '策略性、算计、利用局势和影响他人的倾向。' }, + { name: '自恋', description: '自我重要感、优越感、希望被关注和认可的倾向。' }, + { name: '冷酷冲动', description: '低共情、冒险、冲动和较少顾及后果的倾向。' }, + ], + notes: [ + '该量表测的是普通人群中的人格倾向,不是人格障碍诊断。', + '高分可以提示关系和决策中的风险点,例如操控、过度自我中心或冲动。', + '结果需要结合情境、行为和他人反馈理解。', + ], + references: [ + { + text: 'Jones & Paulhus (2014). Introducing the Short Dark Triad.', + url: 'https://doi.org/10.1177/1073191113514105', + }, + { + text: 'OpenPsychometrics: Dark Triad Personality Test', + url: 'https://openpsychometrics.org/tests/SD3/', + }, + ], + }, + questions: [ + { id: 1, content: '为了达到目标,保留一些真实想法通常是有必要的。' }, + { id: 2, content: '我倾向于用策略影响别人,而不是直接说出全部目的。' }, + { id: 3, content: '在重要竞争中,知道如何利用规则很关键。' }, + { id: 4, content: '我不喜欢操控别人,即使这样能让我受益。' }, + { id: 5, content: '如果别人太容易相信我,那是他们自己的问题。' }, + { id: 6, content: '我会仔细观察别人想要什么,再决定怎么行动。' }, + { id: 7, content: '只要结果值得,有些手段可以灵活处理。' }, + { id: 8, content: '我通常不会把人际关系当作达成目的的工具。' }, + { id: 9, content: '在复杂局面里,我擅长让事情朝对自己有利的方向发展。' }, + { id: 10, content: '我觉得自己有些特别,应该得到更多认可。' }, + { id: 11, content: '我不太在意自己是不是被别人关注。' }, + { id: 12, content: '被人欣赏和称赞会让我很有动力。' }, + { id: 13, content: '我认为自己比大多数人更有潜力。' }, + { id: 14, content: '我喜欢在群体中表现出自己的优势。' }, + { id: 15, content: '我并不觉得自己比别人更重要。' }, + { id: 16, content: '如果有机会成为焦点,我通常不会排斥。' }, + { id: 17, content: '我不需要别人觉得我特别。' }, + { id: 18, content: '我相信自己值得比普通人更好的待遇。' }, + { id: 19, content: '如果别人被我的决定影响,我不一定会太放在心上。' }, + { id: 20, content: '我通常会避免做可能伤害别人的事。' }, + { id: 21, content: '刺激和冒险有时比安全更吸引我。' }, + { id: 22, content: '我做决定时有时不会想太多后果。' }, + { id: 23, content: '当别人受伤害时,我不总是能产生强烈感受。' }, + { id: 24, content: '规则不合理时,我可能会直接无视它。' }, + { id: 25, content: '我很重视不要让别人因为我而受苦。' }, + { id: 26, content: '我有时会为了眼前的刺激做出冒险选择。' }, + { id: 27, content: '在竞争中,我不太容易因为对手的感受而心软。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/dass21/zh.ts b/questionairies/dass21/zh.ts new file mode 100644 index 0000000..080b533 --- /dev/null +++ b/questionairies/dass21/zh.ts @@ -0,0 +1,69 @@ +import { Questionnaire } from "@/types"; + +export const dass21: Questionnaire = { + id: "dass21", + title: "DASS-21抑郁焦虑压力量表", + description: "同时评估抑郁、焦虑、压力三个维度的综合量表", + category: "情绪", + tags: ["抑郁", "焦虑", "压力", "筛查", "自评", "临床常用"], + time: "5-8分钟", + details: { + introduction: "抑郁焦虑压力量表(Depression Anxiety Stress Scales-21, DASS-21)是一个用于测量抑郁、焦虑和压力三个相关负性情绪状态的自评量表。该量表由21个项目组成,每个维度各包含7个项目。DASS-21能够有效区分抑郁、焦虑和压力三种不同的情绪状态,在临床和研究中应用广泛。", + questionCount: "21个项目", + evaluationTime: "通常为5-8分钟", + instructions: "请仔细阅读每个项目,根据您在过去一周的感受,选择最符合您情况的选项。每个问题都有四个选择:0=完全不符合,1=有时符合,2=经常符合,3=总是符合。", + scoringMethod: [ + "分量表计分:抑郁、焦虑、压力各7题,每题0-3分", + "分量表得分乘以2得到最终分数以便与DASS-42比较", + "抑郁分量表:正常0-9,轻度10-13,中度14-20,重度21-27,极重度28+", + "焦虑分量表:正常0-7,轻度8-9,中度10-14,重度15-19,极重度20+", + "压力分量表:正常0-14,轻度15-18,中度19-25,重度26-33,极重度34+" + ], + dimensions: [ + { name: "抑郁", description: "情绪低落、绝望、生活缺乏意义、自我贬低、缺乏兴趣或参与感、快感缺失、无精打采" }, + { name: "焦虑", description: "自主神经系统的唤起、肌肉紧张、情境性焦虑和对焦虑体验的主观感受" }, + { name: "压力", description: "持续的紧张状态、易激惹、过度反应、不耐烦、难以放松、神经质、容易不安或激动" } + ], + notes: [ + "该量表适用于成年人的情绪状态评估", + "如果任一维度得分较高,建议寻求专业心理帮助", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Lovibond, S. H., & Lovibond, P. F. (1995). Manual for the Depression Anxiety Stress Scales. Psychology Foundation.", + url: "https://www.psychology.org.au" + } + ] + }, + questions: [ + // Depression dimension (1, 3, 5, 10, 13, 16, 17) + { id: 1, content: "我发现很难让自己平静下来" }, + { id: 2, content: "我觉得嘴巴发干" }, + { id: 3, content: "我似乎无法体验到任何积极的情感" }, + { id: 4, content: "我遇到了呼吸困难(例如:过度快速的呼吸、喘不过气来)" }, + { id: 5, content: "我发现很难鼓起劲头去做事" }, + { id: 6, content: "我倾向于对事情反应过度" }, + { id: 7, content: "我遇到了颤抖的现象(例如:手颤抖)" }, + { id: 8, content: "我觉得我消耗了很多神经能量" }, + { id: 9, content: "我为一些可能令我恐慌和出丑的情况而担忧" }, + { id: 10, content: "我觉得自己没有什么可期待的" }, + { id: 11, content: "我发现自己变得激动" }, + { id: 12, content: "我发现很难放松" }, + { id: 13, content: "我感到忧愁和沮丧" }, + { id: 14, content: "当我被阻止做我想做的事情时,我无法忍受" }, + { id: 15, content: "我感到好像快要恐慌了" }, + { id: 16, content: "我对任何事情都不能热衷起来" }, + { id: 17, content: "我觉得自己不配做人" }, + { id: 18, content: "我发现我相当敏感" }, + { id: 19, content: "我发现,即使在没有体力活动的时候,我的心跳还是加快了" }, + { id: 20, content: "我无缘无故地感到害怕" }, + { id: 21, content: "我觉得生活没有意义" } + ], + renderOptions: () => [ + { id: 1, content: "完全不符合", value: "0" }, + { id: 2, content: "有时符合", value: "1" }, + { id: 3, content: "经常符合", value: "2" }, + { id: 4, content: "总是符合", value: "3" } + ] +}; diff --git a/questionairies/empathy/zh.ts b/questionairies/empathy/zh.ts new file mode 100644 index 0000000..ca1b81e --- /dev/null +++ b/questionairies/empathy/zh.ts @@ -0,0 +1,88 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '完全不像我', value: '1' }, + { id: 2, content: '不太像我', value: '2' }, + { id: 3, content: '有点像我', value: '3' }, + { id: 4, content: '比较像我', value: '4' }, + { id: 5, content: '非常像我', value: '5' }, +]; + +export const empathy: Questionnaire = { + id: 'empathy', + title: 'IRI 人际反应指数量表(28题)', + description: '从观点采择、共情关怀、个人痛苦和想象代入理解共情模式', + category: '人格', + tags: ['共情', '自评'], + time: '6-10分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'IRI 是常用的多维共情自评量表。它不只看“是否有同理心”,而是区分理解他人视角、关心他人、在人际紧张中自己的不安反应,以及对故事角色的想象代入。', + questionCount: '28个项目', + evaluationTime: '通常为6-10分钟', + instructions: + '请根据这些描述与平时自己的相似程度作答。结果没有单一好坏,四个分项的组合更有意义。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '四个分量表分别计算平均分:观点采择、共情关怀、个人痛苦、想象代入。', + '不建议只看总分,尤其个人痛苦高分代表自我不安反应较强,不等同于更成熟的共情。', + ], + dimensions: [ + { name: '观点采择', description: '是否能自然站到他人视角理解问题。' }, + { name: '共情关怀', description: '是否容易对他人的处境产生关心和怜悯。' }, + { name: '个人痛苦', description: '面对他人痛苦或紧张场景时,自己是否容易不安。' }, + { name: '想象代入', description: '是否容易代入故事、电影或虚构角色的感受。' }, + ], + notes: [ + '共情关怀高不等于必须承担他人的所有情绪。', + '个人痛苦高时,可以关注边界感、情绪调节和助人后的恢复。', + '该量表适合用于自我理解和关系反思,不是临床诊断工具。', + ], + references: [ + { + text: 'Davis (1983). Measuring individual differences in empathy: Evidence for a multidimensional approach.', + url: 'https://doi.org/10.1037/0022-3514.44.1.113', + }, + { + text: 'Interpersonal Reactivity Index overview', + url: 'https://www.eckerd.edu/psychology/iri/', + }, + ], + }, + questions: [ + { id: 1, content: '在做决定前,我通常会试着理解每个人的看法。' }, + { id: 2, content: '我有时很难从别人的角度看问题。' }, + { id: 3, content: '当朋友和我意见不同时,我会努力理解对方为什么这样想。' }, + { id: 4, content: '我不太会花时间想别人为什么会有那样的感受。' }, + { id: 5, content: '在争论中,我能同时看到双方各自合理的部分。' }, + { id: 6, content: '我常常只从自己的立场判断事情。' }, + { id: 7, content: '我能比较自然地把自己放到别人的处境里。' }, + { id: 8, content: '看到别人遇到困难时,我通常会感到关心。' }, + { id: 9, content: '面对陌生人的不幸,我也可能产生同情。' }, + { id: 10, content: '别人难过时,我通常没什么感觉。' }, + { id: 11, content: '我会为处境弱势的人感到心疼。' }, + { id: 12, content: '当身边人受伤或受挫时,我会真心希望能帮上忙。' }, + { id: 13, content: '他人的痛苦很少影响到我。' }, + { id: 14, content: '我容易被真实的人情故事触动。' }, + { id: 15, content: '看到紧张或冲突场面时,我自己也容易慌。' }, + { id: 16, content: '当别人情绪崩溃时,我常常不知道该怎么保持镇定。' }, + { id: 17, content: '在紧急场景里,我通常能保持冷静。' }, + { id: 18, content: '看到别人受苦时,我会感到明显的不舒服。' }, + { id: 19, content: '别人很焦虑时,我也容易被带得焦虑。' }, + { id: 20, content: '面对他人的痛苦,我通常不会太受影响。' }, + { id: 21, content: '当身边气氛很紧张时,我会感到压力。' }, + { id: 22, content: '看电影或读小说时,我很容易代入角色。' }, + { id: 23, content: '我常常想象自己处在故事人物的位置。' }, + { id: 24, content: '虚构角色的经历通常不会让我有太强感受。' }, + { id: 25, content: '看完一个故事后,我有时会继续想着角色的命运。' }, + { id: 26, content: '我很少被小说、影视里的情绪感染。' }, + { id: 27, content: '我能很快进入一个故事营造的情绪世界。' }, + { id: 28, content: '我不太会把自己想象成故事中的人物。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/fisher/zh.ts b/questionairies/fisher/zh.ts new file mode 100644 index 0000000..7e0eb2d --- /dev/null +++ b/questionairies/fisher/zh.ts @@ -0,0 +1,111 @@ +import { Questionnaire } from "@/types"; + +const options = [ + { id: 1, content: "非常不同意", value: "1" }, + { id: 2, content: "不同意", value: "2" }, + { id: 3, content: "同意", value: "3" }, + { id: 4, content: "非常同意", value: "4" }, +]; + +export const fisher: Questionnaire = { + id: "fisher", + title: "Fisher 气质量表", + description: "了解探索者、建设者、指挥者、协商者四种人格气质倾向", + category: "人格", + tags: ["自评"], + time: "6-8分钟", + evaluation: { + academicRecognition: "B", + retestSuitable: true, + recommendedInterval: "1年", + }, + details: { + introduction: + "Fisher气质量表基于Helen Fisher提出的四种气质模型,用来描述个体在探索新事物、追求稳定秩序、分析决策、共情协商方面的自然倾向。它更适合用于自我理解、沟通风格和关系偏好的讨论,不属于临床诊断工具。", + questionCount: "40个项目", + evaluationTime: "通常6-8分钟", + instructions: + "请根据平时大多数情况下的自己作答,而不是某一次特殊经历中的表现。每题选择与自己相符的程度。", + scoringMethod: [ + "每题1-4分,分别计入四个气质维度。", + "每个维度10题,总分范围10-40分,分数越高表示该气质表达越明显。", + "结果重点看最高分和第二高分的组合,而不是把人固定为单一类型。", + ], + dimensions: [ + { + name: "探索者 Explorer", + description: "好奇、灵活、追求新鲜感,喜欢变化、冒险和开放式可能性。", + }, + { + name: "建设者 Builder", + description: "重视秩序、责任、稳定和清晰规则,偏好可靠计划与长期承诺。", + }, + { + name: "指挥者 Director", + description: "直接、理性、目标导向,重视逻辑、效率、系统分析和独立判断。", + }, + { + name: "协商者 Negotiator", + description: "共情、直觉、重视关系与意义,擅长理解情境和协调不同观点。", + }, + ], + notes: [ + "该模型适合做人格风格参考,不应作为职业、关系或心理健康判断的唯一依据。", + "高分并不代表更好,低分也不代表缺陷。不同环境需要不同气质组合。", + "若要做更稳定的人格分析,建议结合Big Five、HEXACO等量表一起看。", + ], + references: [ + { + text: "Fisher, H. E. et al. Four broad temperament dimensions: description, convergent validation correlations, and comparison with the Big Five.", + url: "https://doi.org/10.3389/fpsyg.2015.01098", + }, + { + text: "Open Source Psychometrics Project: Fisher Temperament Inventory", + url: "https://openpsychometrics.org/tests/FTI/", + }, + ], + }, + questions: [ + { id: 1, content: "我喜欢尝试以前没有做过的事情。" }, + { id: 2, content: "比起临时发挥,我更喜欢有清楚的计划。" }, + { id: 3, content: "面对问题时,我会先寻找最有效的解决方案。" }, + { id: 4, content: "我很容易觉察到别人没有说出口的情绪。" }, + { id: 5, content: "重复的日常安排会让我很快感到无聊。" }, + { id: 6, content: "我重视承诺,也希望别人说到做到。" }, + { id: 7, content: "我喜欢把复杂问题拆开分析。" }, + { id: 8, content: "做决定时,我会认真考虑它对人际关系的影响。" }, + { id: 9, content: "我常被新想法、新地点或新体验吸引。" }, + { id: 10, content: "稳定、可靠的生活节奏会让我感到安心。" }, + { id: 11, content: "我不太介意直接指出问题的关键。" }, + { id: 12, content: "我经常从整体氛围和语气中理解一个人。" }, + { id: 13, content: "如果环境太固定,我会想主动制造一些变化。" }, + { id: 14, content: "我倾向于遵守规则,除非有充分理由改变它。" }, + { id: 15, content: "我欣赏思路清晰、判断果断的人。" }, + { id: 16, content: "我对人的动机和内心世界很感兴趣。" }, + { id: 17, content: "我愿意为了有趣的机会承担一定风险。" }, + { id: 18, content: "我习惯把重要资料、安排和流程整理清楚。" }, + { id: 19, content: "在压力下,我通常会优先保持理性和效率。" }, + { id: 20, content: "冲突中,我会试着理解双方真正关心的是什么。" }, + { id: 21, content: "我喜欢开放式任务,因为它们允许我自由发挥。" }, + { id: 22, content: "我相信传统经验中有很多值得保留的东西。" }, + { id: 23, content: "我喜欢和有能力、有想法的人进行高强度讨论。" }, + { id: 24, content: "我常能把分散的信息连成一个有意义的故事。" }, + { id: 25, content: "我通常对旅行、冒险或新领域学习有兴趣。" }, + { id: 26, content: "我愿意承担维护团队秩序和稳定的责任。" }, + { id: 27, content: "我更相信证据和逻辑,而不是单纯的感觉。" }, + { id: 28, content: "别人遇到困难时,我会自然地想提供情感支持。" }, + { id: 29, content: "我喜欢即兴决定,也能适应临时变化。" }, + { id: 30, content: "我做事通常比较有条理,不喜欢混乱。" }, + { id: 31, content: "我在目标明确时行动力很强。" }, + { id: 32, content: "我重视亲密关系中的理解、表达和连接。" }, + { id: 33, content: "我经常会被脑中突然出现的新点子带动。" }, + { id: 34, content: "我对家庭、社群或长期归属感比较看重。" }, + { id: 35, content: "我喜欢用标准、原则或模型来判断事情。" }, + { id: 36, content: "我常能注意到别人情绪变化背后的细节。" }, + { id: 37, content: "我比多数人更需要自由和空间。" }, + { id: 38, content: "我希望自己的生活和工作有可预期的结构。" }, + { id: 39, content: "我能较快判断一个方案是否可行。" }, + { id: 40, content: "我倾向于寻找能让大家都被理解的表达方式。" }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/gad7/zh.ts b/questionairies/gad7/zh.ts new file mode 100644 index 0000000..867a0d3 --- /dev/null +++ b/questionairies/gad7/zh.ts @@ -0,0 +1,56 @@ +import { Questionnaire } from "@/types"; + +export const gad7: Questionnaire = { + id: "gad7", + title: "GAD-7广泛性焦虑量表", + description: "评估广泛性焦虑症状的严重程度", + category: "情绪", + tags: ["焦虑", "筛查", "自评", "临床常用"], + time: "2-5分钟", + details: { + introduction: "广泛性焦虑量表(Generalized Anxiety Disorder 7-item scale, GAD-7)是一个简单、有效的焦虑症筛查工具。该量表由7个项目组成,能够快速评估个体在过去两周内的焦虑症状严重程度。GAD-7被广泛用于临床和研究中,具有良好的信效度。", + questionCount: "7个项目", + evaluationTime: "通常为2-5分钟", + instructions: "请根据您在过去两周内的实际感受,选择最符合您情况的选项。每个问题都有四个选择:完全没有、好几天、一半以上的天数、几乎每天。", + scoringMethod: [ + "总分:将7个项目得分相加,范围0-21分", + "计分方式:完全没有=0分,好几天=1分,一半以上的天数=2分,几乎每天=3分", + "严重程度:0-4分为最低水平,5-9分为轻度,10-14分为中度,15-21分为重度焦虑" + ], + dimensions: [ + { name: "担心程度", description: "过度担心不同事情的频率和强度" }, + { name: "控制能力", description: "控制或停止担心的能力" }, + { name: "身体症状", description: "坐立不安、易疲劳等身体表现" }, + { name: "注意力", description: "注意力集中困难" }, + { name: "易激惹", description: "容易烦躁或易怒" }, + { name: "肌肉紧张", description: "肌肉紧张、疼痛或酸痛" }, + { name: "睡眠问题", description: "入睡困难、睡眠不安或睡眠不足" } + ], + notes: [ + "该量表适用于成年人焦虑症状筛查", + "如果总分≥10分,建议寻求专业心理医生的评估", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Spitzer, R. L., Kroenke, K., Williams, J. B., & Löwe, B. (2006). A brief measure for assessing generalized anxiety disorder: the GAD-7. Archives of internal medicine, 166(10), 1092-1097.", + url: "https://jamanetwork.com/journals/jamainternalmedicine/fullarticle/410326" + } + ] + }, + questions: [ + { id: 1, content: "感到紧张、担心或焦虑" }, + { id: 2, content: "无法停止或控制担心" }, + { id: 3, content: "对各种各样的事情担心过多" }, + { id: 4, content: "很难放松下来" }, + { id: 5, content: "坐立不安,难以静坐" }, + { id: 6, content: "变得容易烦恼或易怒" }, + { id: 7, content: "感到好像有什么可怕的事情会发生" } + ], + renderOptions: () => [ + { id: 1, content: "完全没有", value: "0" }, + { id: 2, content: "好几天", value: "1" }, + { id: 3, content: "一半以上的天数", value: "2" }, + { id: 4, content: "几乎每天", value: "3" } + ] +}; diff --git a/questionairies/gd/zh.ts b/questionairies/gd/zh.ts new file mode 100644 index 0000000..f7e30bc --- /dev/null +++ b/questionairies/gd/zh.ts @@ -0,0 +1,85 @@ +import { Questionnaire } from "@/types"; + +export const gd: Questionnaire = { + id: "gd", + title: "性别认知障碍问卷 (GDQ)", + description: "评估性别认知障碍体验和感受", + category: "心理健康", + tags: ["自评"], + time: "10-15分钟", + details: { + introduction: + "性别认知障碍问卷旨在评估与性别认同相关的体验和感受。性别认知障碍是指可能伴随个人体验或表达的性别与其出生时指定性别不一致而产生的痛苦。此问卷用于自我反思和教育目的。请根据您的个人经历和感受诚实回答。", + questionCount: "27个项目", + evaluationTime: "约10-15分钟", + instructions: + "请仔细阅读每个陈述,选择最能描述您体验或感受的回答。没有对错之分。", + scoringMethod: [ + "每个问题评分为1-7分(不同意=1,略微不同意=2,有些不同意=3,中性=4,有些同意=5,略微同意=6,同意=7)。", + "总分范围:27-189分。", + "分数越高可能表示更强的性别认知障碍体验。", + "这不是诊断工具,不应用于临床诊断。", + ], + dimensions: [ + { name: "性别认同", description: "对个人内在性别感受的体验。" }, + { + name: "社会性别角色", + description: "对基于出生时指定性别的社会期望和角色的舒适度。", + }, + { name: "身体性别焦虑", description: "对身体特征和体型的感受。" }, + { name: "性别表达", description: "以各种方式表达性别的舒适度。" }, + ], + notes: [ + "此问卷仅用于教育和自我反思目的。", + "它不能替代专业评估或诊断。", + "如果您在性别认同方面感到痛苦,请考虑咨询合格的心理健康专业人员。", + "性别认同是一个复杂而个人化的体验,在个体间差异很大。", + ], + references: [ + { + text: "美国心理学会. (2015). 跨性别和性别非顺应者心理实践指南. 美国心理学家, 70(9), 832-864.", + url: "https://www.apa.org/practice/guidelines/transgender.pdf", + }, + ], + }, + questions: [ + { id: 1, content: "我对出生时指定的性别感到舒适。" }, + { id: 2, content: "我希望自己出生时是不同的性别。" }, + { id: 3, content: "我觉得我的身体与我的性别认同相匹配。" }, + { id: 4, content: "我曾经幻想过成为不同的性别。" }, + { id: 5, content: "我对使用为我出生时指定性别设计的洗手间感到舒适。" }, + { id: 6, content: "当人们以我出生时指定的性别称呼我时,我感到痛苦。" }, + { id: 7, content: "我喜欢通常与我出生时指定性别相关的活动。" }, + { id: 8, content: "当我以不同性别的形象出现时,我感到更舒适。" }, + { id: 9, content: "我对目前的外貌感到满意。" }, + { id: 10, content: "我曾考虑过医学性别转换(激素/手术)。" }, + { id: 11, content: "我对穿着符合我出生时指定性别的典型服装感到舒适。" }, + { id: 12, content: "我更希望别人把我看作与出生时指定性别不同的性别。" }, + { id: 13, content: "我在自己的身体里感到自在。" }, + { id: 14, content: "我曾尝试过不同的性别表达方式。" }, + { id: 15, content: "我对自己的声音感到舒适。" }, + { id: 16, content: "我希望我能改变某些身体特征。" }, + { id: 17, content: "我强烈认同我出生时指定的性别。" }, + { id: 18, content: "我曾对不同性别的人感到羡慕。" }, + { id: 19, content: "我对别人如何感知我的性别感到舒适。" }, + { id: 20, content: "从童年开始,我就感到与我出生时指定的性别脱节。" }, + { id: 21, content: "我在为我出生时指定性别设置的单性别空间中感到舒适。" }, + { id: 22, content: "我曾希望醒来时是不同的性别。" }, + { id: 23, content: "我觉得我的性别认同是稳定和一致的。" }, + { id: 24, content: "我对自己的性别认同感到困惑。" }, + { id: 25, content: "我对自己的姓名和代词感到舒适。" }, + { id: 26, content: "我曾因性别期望而感到痛苦。" }, + { id: 27, content: "当我表达真实的性别认同时,我感到真实。" }, + ], + renderOptions: () => { + return [ + { id: 1, content: "不同意", value: "1" }, + { id: 2, content: "略微不同意", value: "2" }, + { id: 3, content: "有些不同意", value: "3" }, + { id: 4, content: "中性", value: "4" }, + { id: 5, content: "有些同意", value: "5" }, + { id: 6, content: "略微同意", value: "6" }, + { id: 7, content: "同意", value: "7" }, + ]; + }, +}; diff --git a/questionairies/grit/zh.ts b/questionairies/grit/zh.ts new file mode 100644 index 0000000..1b6d164 --- /dev/null +++ b/questionairies/grit/zh.ts @@ -0,0 +1,72 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '完全不像我', value: '1' }, + { id: 2, content: '不太像我', value: '2' }, + { id: 3, content: '有点像我', value: '3' }, + { id: 4, content: '比较像我', value: '4' }, + { id: 5, content: '非常像我', value: '5' }, +]; + +export const grit: Questionnaire = { + id: 'grit', + title: 'Grit-S 坚毅量表(8题)', + description: '评估长期目标中的坚持程度和兴趣稳定性', + category: '人格', + tags: ['自评'], + time: '2-3分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'Grit-S 是短版坚毅量表,用来了解一个人在长期目标上是否能持续投入、面对挫折是否能坚持,以及兴趣方向是否相对稳定。它更适合用于自我觉察和长期发展讨论。', + questionCount: '8个项目', + evaluationTime: '通常为2-3分钟', + instructions: + '请根据这些描述与平时自己的相似程度作答,不需要只参考某一次成功或失败经历。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '总分为8题平均分,范围1-5分,分数越高表示坚毅程度越高。', + '结果包含“努力坚持”和“兴趣稳定”两个分项,分项差异比单一总分更值得关注。', + ], + dimensions: [ + { + name: '努力坚持', + description: '遇到困难、挫折和长期任务时,是否能继续投入努力。', + }, + { + name: '兴趣稳定', + description: '长期兴趣和目标方向是否相对稳定,不容易频繁转向。', + }, + ], + notes: [ + '坚毅不等于盲目坚持,长期目标仍需要和价值观、现实资源、身心状态一起判断。', + '兴趣稳定分低不一定是坏事,也可能代表探索期较强或环境变化较多。', + '该量表不是能力测试,也不能直接预测某个具体目标能否成功。', + ], + references: [ + { + text: 'Duckworth & Quinn (2009). Development and Validation of the Short Grit Scale.', + url: 'https://doi.org/10.1080/00223890802634290', + }, + { + text: 'Short Grit Scale scoring guide', + url: 'https://sjdm.org/dmidi/files/Grit-8-item.pdf', + }, + ], + }, + questions: [ + { id: 1, content: '新的想法或计划有时会让我偏离原来的目标。' }, + { id: 2, content: '遇到挫折时,我通常不会轻易放弃。' }, + { id: 3, content: '我常常定下一个目标,后来又转向另一个目标。' }, + { id: 4, content: '我做事比较勤勉,愿意为重要目标持续投入。' }, + { id: 5, content: '我很难长期专注在需要好几年才能完成的事情上。' }, + { id: 6, content: '我完成一件事后,常常很快又被别的兴趣吸引走。' }, + { id: 7, content: '对我来说,完成已经开始的重要事情很有意义。' }, + { id: 8, content: '我认为自己是一个坚持不懈的人。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/hexaco/zh.ts b/questionairies/hexaco/zh.ts new file mode 100644 index 0000000..c3bb869 --- /dev/null +++ b/questionairies/hexaco/zh.ts @@ -0,0 +1,122 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不同意', value: '1' }, + { id: 2, content: '不同意', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '同意', value: '4' }, + { id: 5, content: '非常同意', value: '5' }, +]; + +export const hexaco: Questionnaire = { + id: 'hexaco', + title: 'HEXACO 六因素人格量表(60题)', + description: '从诚实谦逊、情绪性、外向性、宜人性、尽责性和开放性理解人格结构', + category: '人格', + tags: ['自评'], + time: '10-15分钟', + evaluation: { + academicRecognition: 'A+', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'HEXACO 是六因素人格模型,在五大人格基础上突出“诚实谦逊”维度。本量表使用60题短版结构,用于快速了解六个主要人格方向。', + questionCount: '60个项目', + evaluationTime: '通常为10-15分钟', + instructions: + '请根据平时的自己作答,而不是只参考最近一天的状态。每题选择最符合自己的一项。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '六个维度分别计算平均分:诚实谦逊、情绪性、外向性、宜人性、尽责性、开放性。', + '分数适合做人格画像和长期追踪,不建议作为单次定论。', + ], + dimensions: [ + { name: '诚实谦逊', description: '真诚、公平、避免贪婪和谦逊的倾向。' }, + { name: '情绪性', description: '对威胁、依恋、焦虑和情感联系的敏感度。' }, + { name: '外向性', description: '社交自信、活力、表达和人际参与。' }, + { name: '宜人性', description: '宽容、温和、灵活和耐心。' }, + { name: '尽责性', description: '组织性、勤勉、谨慎和自我约束。' }, + { name: '开放性', description: '审美、好奇、创造性和非常规思考。' }, + ], + notes: [ + 'HEXACO 与 Big Five 有重叠,但额外强调诚实谦逊维度。', + '人格分数不是优劣标签,应结合生活目标和具体情境理解。', + '若需要更细分的面向,可之后增加100题或200题版本。', + ], + references: [ + { + text: 'HEXACO-PI-R official materials', + url: 'https://hexaco.org/hexaco-inventory', + }, + { + text: 'HEXACO-60 scoring keys', + url: 'https://hexaco.org/downloads/ScoringKeys_60.pdf', + }, + ], + }, + questions: [ + { id: 1, content: '即使没人知道,我也会尽量诚实做事。' }, + { id: 2, content: '如果能获得好处,我有时会隐瞒关键信息。' }, + { id: 3, content: '我不喜欢用不公平的方式占别人便宜。' }, + { id: 4, content: '只要不被发现,规则可以灵活处理。' }, + { id: 5, content: '我对奢华身份和炫耀性地位兴趣不大。' }, + { id: 6, content: '拥有能让别人羡慕的东西对我很重要。' }, + { id: 7, content: '我通常不会把自己看得比别人更高。' }, + { id: 8, content: '我觉得自己理应获得特殊待遇。' }, + { id: 9, content: '我愿意公平对待不熟悉的人。' }, + { id: 10, content: '为了个人利益,我可能会说一些并不完全真实的话。' }, + { id: 11, content: '遇到危险或不确定情境时,我容易紧张。' }, + { id: 12, content: '我很少为潜在风险担心。' }, + { id: 13, content: '重要的人离开我时,我会受到很大影响。' }, + { id: 14, content: '我通常不太依赖他人的情感支持。' }, + { id: 15, content: '看到别人受苦时,我容易被触动。' }, + { id: 16, content: '我很少因为别人的处境而情绪起伏。' }, + { id: 17, content: '我有时会对未来可能发生的坏事感到焦虑。' }, + { id: 18, content: '面对压力时,我通常非常镇定。' }, + { id: 19, content: '我和亲近的人之间有很强的情感连接需求。' }, + { id: 20, content: '我不太需要别人安慰我。' }, + { id: 21, content: '在社交场合中,我通常比较自在。' }, + { id: 22, content: '在人多的场合,我经常感到不自然。' }, + { id: 23, content: '我容易主动和别人交谈。' }, + { id: 24, content: '我更喜欢安静地待在一边。' }, + { id: 25, content: '我通常对自己在人群中的表现有信心。' }, + { id: 26, content: '我常常怀疑自己是否受欢迎。' }, + { id: 27, content: '我喜欢热闹、有活力的活动。' }, + { id: 28, content: '我很少主动参与社交活动。' }, + { id: 29, content: '我能比较轻松地表达积极情绪。' }, + { id: 30, content: '我在人际中通常比较拘谨。' }, + { id: 31, content: '别人犯错时,我通常愿意给对方机会。' }, + { id: 32, content: '我很难原谅伤害过我的人。' }, + { id: 33, content: '我倾向于温和地处理分歧。' }, + { id: 34, content: '如果别人惹我,我会很快变得尖锐。' }, + { id: 35, content: '我能接受别人和我有不同做法。' }, + { id: 36, content: '我不太愿意在争论中让步。' }, + { id: 37, content: '我通常能耐心听完别人的解释。' }, + { id: 38, content: '我容易对拖拉或犯错的人失去耐心。' }, + { id: 39, content: '我不喜欢把小矛盾升级。' }, + { id: 40, content: '如果我认为自己对,就很难软下来。' }, + { id: 41, content: '我喜欢把事情安排得有条理。' }, + { id: 42, content: '我的生活和工作环境常常比较混乱。' }, + { id: 43, content: '我会努力把重要任务推进到底。' }, + { id: 44, content: '我有时会因为懒散而拖延重要事情。' }, + { id: 45, content: '我做事前通常会认真检查细节。' }, + { id: 46, content: '我常常没有检查就匆忙完成任务。' }, + { id: 47, content: '我会提前规划需要完成的事情。' }, + { id: 48, content: '我经常想到什么就做什么,不太计划。' }, + { id: 49, content: '我能克制短期诱惑来完成长期目标。' }, + { id: 50, content: '我很容易被眼前有趣的事情带偏。' }, + { id: 51, content: '我喜欢接触艺术、音乐或美感体验。' }, + { id: 52, content: '艺术和审美通常不会太吸引我。' }, + { id: 53, content: '我对新知识、新观点有持续好奇心。' }, + { id: 54, content: '我更喜欢熟悉的想法,不太想探索新观点。' }, + { id: 55, content: '我喜欢想象不同的可能性。' }, + { id: 56, content: '我通常更重视现实可行,不太喜欢想象。' }, + { id: 57, content: '我愿意尝试不寻常的解决方法。' }, + { id: 58, content: '我倾向于坚持传统和常规做法。' }, + { id: 59, content: '复杂、有深度的话题会吸引我。' }, + { id: 60, content: '我不太喜欢抽象或理论性的讨论。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/isi/zh.ts b/questionairies/isi/zh.ts new file mode 100644 index 0000000..6fb33bc --- /dev/null +++ b/questionairies/isi/zh.ts @@ -0,0 +1,90 @@ +import { Questionnaire } from "@/types"; + +export const isi: Questionnaire = { + id: "isi", + title: "ISI失眠严重程度指数", + description: "评估失眠问题的严重程度和对生活的影响", + category: "睡眠", + tags: ["失眠", "睡眠", "筛查", "自评", "临床常用"], + time: "2-3分钟", + details: { + introduction: "失眠严重程度指数(Insomnia Severity Index, ISI)是一个简短、有效的自评量表,用于评估失眠的严重程度。该量表由7个项目组成,能够快速评估失眠对个体日常功能的影响程度,是临床和研究中广泛使用的失眠评估工具。", + questionCount: "7个项目", + evaluationTime: "通常为2-3分钟", + instructions: "请根据您最近两周的睡眠情况,选择最符合您实际情况的选项。每个问题都有不同的评分标准。", + scoringMethod: [ + "总分:将7个项目得分相加,范围0-28分", + "严重程度:0-7分为无临床意义失眠,8-14分为亚临床失眠,15-21分为中度失眠,22-28分为重度失眠", + "临床意义:总分≥8分提示存在失眠问题,≥15分建议寻求专业帮助" + ], + dimensions: [ + { name: "入睡困难", description: "从上床到入睡所需时间的延长" }, + { name: "维持睡眠困难", description: "夜间醒来次数和再次入睡的困难" }, + { name: "早醒", description: "比期望时间更早醒来且无法再次入睡" }, + { name: "睡眠满意度", description: "对目前睡眠模式的整体满意程度" }, + { name: "日间功能", description: "失眠对日间生活质量和功能的影响" }, + { name: "他人观察", description: "他人对您睡眠问题的注意程度" }, + { name: "担心程度", description: "对当前睡眠问题的担心和痛苦程度" } + ], + notes: [ + "该量表适用于成年人失眠问题评估", + "如果总分≥15分,建议咨询睡眠专科医生", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Bastien, C. H., Vallières, A., & Morin, C. M. (2001). Validation of the Insomnia Severity Index as an outcome measure for insomnia research. Sleep medicine, 2(4), 297-307.", + url: "https://pubmed.ncbi.nlm.nih.gov/11438246/" + } + ] + }, + questions: [ + { id: 1, content: "入睡困难的严重程度" }, + { id: 2, content: "维持睡眠困难的严重程度" }, + { id: 3, content: "早醒问题的严重程度" }, + { id: 4, content: "您对目前睡眠模式的满意度如何?" }, + { id: 5, content: "您认为您的睡眠问题在多大程度上干扰了您的日常功能?" }, + { id: 6, content: "其他人在多大程度上注意到您的睡眠问题对您生活质量的影响?" }, + { id: 7, content: "您对目前睡眠问题的担心或痛苦程度如何?" } + ], + renderOptions: (id: number) => { + switch (id) { + case 1: + case 2: + case 3: + return [ + { id: 1, content: "无", value: "0" }, + { id: 2, content: "轻度", value: "1" }, + { id: 3, content: "中度", value: "2" }, + { id: 4, content: "重度", value: "3" }, + { id: 5, content: "极重度", value: "4" } + ]; + case 4: + return [ + { id: 1, content: "非常满意", value: "0" }, + { id: 2, content: "满意", value: "1" }, + { id: 3, content: "一般满意", value: "2" }, + { id: 4, content: "不满意", value: "3" }, + { id: 5, content: "非常不满意", value: "4" } + ]; + case 5: + case 6: + case 7: + return [ + { id: 1, content: "完全不", value: "0" }, + { id: 2, content: "轻微", value: "1" }, + { id: 3, content: "有些", value: "2" }, + { id: 4, content: "很多", value: "3" }, + { id: 5, content: "非常多", value: "4" } + ]; + default: + return [ + { id: 1, content: "无", value: "0" }, + { id: 2, content: "轻度", value: "1" }, + { id: 3, content: "中度", value: "2" }, + { id: 4, content: "重度", value: "3" }, + { id: 5, content: "极重度", value: "4" } + ]; + } + } +}; diff --git a/questionairies/maximizer/zh.ts b/questionairies/maximizer/zh.ts new file mode 100644 index 0000000..d5c4248 --- /dev/null +++ b/questionairies/maximizer/zh.ts @@ -0,0 +1,83 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '完全不同意', value: '1' }, + { id: 2, content: '不同意', value: '2' }, + { id: 3, content: '有点不同意', value: '3' }, + { id: 4, content: '不确定', value: '4' }, + { id: 5, content: '有点同意', value: '5' }, + { id: 6, content: '同意', value: '6' }, + { id: 7, content: '完全同意', value: '7' }, +]; + +export const maximizer: Questionnaire = { + id: 'maximizer', + title: 'Maximizer 决策最大化倾向(13题)', + description: '评估做选择时是否追求最优、反复比较并容易决策困难', + category: '认知', + tags: ['自评'], + time: '4-6分钟', + evaluation: { + academicRecognition: 'B', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + '最大化倾向描述的是一个人在做选择时是否倾向于寻找“最好的选项”,以及这种寻找是否伴随大量比较、迟疑或后悔。它适合用于理解消费、职业、关系和日常选择风格。', + questionCount: '13个项目', + evaluationTime: '通常为4-6分钟', + instructions: + '请根据你平时做决定时的真实习惯作答。高分不一定更好,低分也不代表随便;关键在于这种选择方式是否适合你的生活情境。', + scoringMethod: [ + '每题1-7分,计算总平均分。', + '结果分为高标准、选项搜索和决策困难三个方面。', + '平均分越高,表示越倾向于追求最优选择;其中决策困难高分可能提示选择成本较高。', + ], + dimensions: [ + { + name: '高标准', + description: '做决定时是否希望找到非常好的、接近最优的方案。', + }, + { + name: '选项搜索', + description: '是否会花大量时间比较、收集信息、寻找更多备选项。', + }, + { + name: '决策困难', + description: '面对多个选项时是否容易迟疑、后悔或担心错过更好选择。', + }, + ], + notes: [ + '最大化倾向在重要决策中可能有优势,但在低风险选择中会增加时间和情绪成本。', + '如果高标准很高、决策困难也很高,可以尝试预设停止规则和“足够好”标准。', + '该量表描述决策风格,不评价一个人是否理性或优秀。', + ], + references: [ + { + text: 'Schwartz et al. (2002). Maximizing versus satisficing: happiness is a matter of choice.', + url: 'https://doi.org/10.1037/0022-3514.83.5.1178', + }, + { + text: 'Decision Making Individual Differences Inventory: Maximization Scale', + url: 'https://sjdm.org/dmidi/Maximization_Scale.html', + }, + ], + }, + questions: [ + { id: 1, content: '做重要选择时,我希望找到所有可能选项中最好的那个。' }, + { id: 2, content: '即使已经有一个不错的选择,我也会继续看看有没有更好的。' }, + { id: 3, content: '买东西前,我常常会查很多评价和对比信息。' }, + { id: 4, content: '如果没有充分比较,我会担心自己做了不够好的选择。' }, + { id: 5, content: '我对自己最终选中的东西通常有很高要求。' }, + { id: 6, content: '面对很多选项时,我会花不少时间筛选。' }, + { id: 7, content: '做决定后,我有时仍会想另一个选项会不会更好。' }, + { id: 8, content: '我不喜欢太快下结论,因为可能还没看到更好的方案。' }, + { id: 9, content: '哪怕是小选择,我也可能认真比较一阵子。' }, + { id: 10, content: '我会为了得到更好的结果,多投入额外时间和精力。' }, + { id: 11, content: '选择太多时,我反而更难感到确定。' }, + { id: 12, content: '我常常希望自己在选择前能掌握更多信息。' }, + { id: 13, content: '如果最后结果不是最好的,我会比较在意。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/need-for-cognition/zh.ts b/questionairies/need-for-cognition/zh.ts new file mode 100644 index 0000000..7d9e793 --- /dev/null +++ b/questionairies/need-for-cognition/zh.ts @@ -0,0 +1,82 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不像我', value: '1' }, + { id: 2, content: '比较不像我', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '比较像我', value: '4' }, + { id: 5, content: '非常像我', value: '5' }, +]; + +export const needForCognition: Questionnaire = { + id: 'need-for-cognition', + title: 'Need for Cognition 认知需求量表(18题)', + description: '评估一个人是否喜欢投入复杂思考和智力挑战', + category: '认知', + tags: ['自评'], + time: '5-8分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + '认知需求量表用于评估一个人是否倾向于主动投入、享受并寻找需要思考的任务。它测量的是思考动机和偏好,不是智商或知识水平。', + questionCount: '18个项目', + evaluationTime: '通常为5-8分钟', + instructions: + '请判断每句话与平时自己的相似程度。这里没有好坏之分,高分表示更喜欢费力思考,低分表示更偏好简洁、直接或实用的处理方式。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '总分范围18-90分,平均分范围1-5分,分数越高表示认知需求越高。', + '该量表评估的是思考偏好,不等同于认知能力或学习成绩。', + ], + dimensions: [ + { + name: '思考投入', + description: '是否愿意主动投入较复杂、较耗费心力的思考任务。', + }, + { + name: '智力挑战偏好', + description: '是否享受抽象问题、复杂问题和新解法带来的挑战。', + }, + ], + notes: [ + '认知需求高的人通常更愿意深入加工信息,但不代表一定更聪明。', + '认知需求低也不等于能力差,可能只是更偏好效率、直觉或实践导向。', + '建议与CRT一起理解:CRT偏能力表现,认知需求偏思考动机。', + ], + references: [ + { + text: 'Cacioppo, Petty & Kao (1984). The efficient assessment of need for cognition.', + url: 'https://doi.org/10.1207/s15327752jpa4803_13', + }, + { + text: 'Need for Cognition Scale scoring overview', + url: 'https://centerofinquiry.org/uncategorized/need-for-cognition-scale-wabash-national-study/', + }, + ], + }, + questions: [ + { id: 1, content: '相比简单问题,我更喜欢复杂一点的问题。' }, + { id: 2, content: '我愿意负责处理需要大量思考的情况。' }, + { id: 3, content: '对我来说,思考本身并不算有趣。' }, + { id: 4, content: '如果可以选择,我宁愿做不太需要动脑的事。' }, + { id: 5, content: '我会尽量避免需要长时间思考的任务。' }, + { id: 6, content: '长时间认真推敲一个问题会让我感到满足。' }, + { id: 7, content: '我通常只愿意思考到“够用”为止。' }, + { id: 8, content: '相比长期复杂的问题,我更喜欢简单日常的小任务。' }, + { id: 9, content: '一旦学会某件事,我更喜欢它不再需要太多思考。' }, + { id: 10, content: '依靠思考和判断来解决问题这件事很吸引我。' }, + { id: 11, content: '我很享受为问题想出新解法的过程。' }, + { id: 12, content: '学习新的思考方式并不会让我特别兴奋。' }, + { id: 13, content: '我喜欢生活里有一些需要慢慢解开的难题。' }, + { id: 14, content: '抽象思考对我来说是有吸引力的。' }, + { id: 15, content: '我更愿意做重要且有智力挑战的任务,而不是轻松但普通的任务。' }, + { id: 16, content: '完成很费脑的任务后,我更多是松一口气,而不是满足。' }, + { id: 17, content: '只要知道答案就够了,我不太在意背后的原理。' }, + { id: 18, content: '深入思考会让我感到有精神。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/npd/zh.ts b/questionairies/npd/zh.ts new file mode 100644 index 0000000..7ebd461 --- /dev/null +++ b/questionairies/npd/zh.ts @@ -0,0 +1,128 @@ +import { Questionnaire } from "@/types"; + +export const npd: Questionnaire = { + id: "npd", + title: "自恋人格量表 (NPI-16)", + description: "评估自恋人格特征和特质", + category: "人格", + tags: ["自恋", "自评"], + time: "5-10分钟", + details: { + introduction: "自恋人格量表(NPI-16)是一个心理测量工具,旨在测量非临床人群中的自恋特质。这个简化版本包含16个强迫选择项目,评估自恋人格特征的各个方面。请注意,这是一个用于理解人格特质的研究工具,不用于临床诊断。", + questionCount: "16个项目", + evaluationTime: "约5-10分钟", + instructions: "对于下面的每对陈述,选择您认为最能描述您或最接近描述您对自己感受的一个。", + scoringMethod: [ + "每个问题提供两个选择 - 选择最能描述您的一个。", + "自恋反应得1分,非自恋反应得0分。", + "总分范围:0-16分。", + "分数越高表示自恋特质越明显。", + "普通人群的平均分数通常在2-8分之间。" + ], + dimensions: [ + { name: "领导力/权威", description: "渴望领导和对他人拥有权威。" }, + { name: "夸大表现欲", description: "需要他人的关注和赞美。" }, + { name: "特权感", description: "认为自己应当受到特殊待遇和特权。" } + ], + notes: [ + "此量表测量连续体上的自恋特质,而非临床障碍。", + "每个人都在某种程度上具有自恋特质,在某些情况下可能是适应性的。", + "高分并不一定表示人格障碍。", + "此工具仅用于教育和研究目的,不用于临床诊断。" + ], + references: [ + { + text: "Ames, D. R., Rose, P., & Anderson, C. P. (2006). The NPI-16 as a short measure of narcissism. Journal of Research in Personality, 40(4), 440-450.", + url: "https://doi.org/10.1016/j.jrp.2005.03.002" + } + ] + }, + questions: [ + { id: 1, content: "选择最能描述您的陈述:" }, + { id: 2, content: "选择最能描述您的陈述:" }, + { id: 3, content: "选择最能描述您的陈述:" }, + { id: 4, content: "选择最能描述您的陈述:" }, + { id: 5, content: "选择最能描述您的陈述:" }, + { id: 6, content: "选择最能描述您的陈述:" }, + { id: 7, content: "选择最能描述您的陈述:" }, + { id: 8, content: "选择最能描述您的陈述:" }, + { id: 9, content: "选择最能描述您的陈述:" }, + { id: 10, content: "选择最能描述您的陈述:" }, + { id: 11, content: "选择最能描述您的陈述:" }, + { id: 12, content: "选择最能描述您的陈述:" }, + { id: 13, content: "选择最能描述您的陈述:" }, + { id: 14, content: "选择最能描述您的陈述:" }, + { id: 15, content: "选择最能描述您的陈述:" }, + { id: 16, content: "选择最能描述您的陈述:" } + ], + renderOptions: (id: number) => { + const optionPairs = [ + [ + { id: 1, content: "我有影响别人的天赋。", value: "1" }, + { id: 2, content: "我不擅长影响别人。", value: "0" } + ], + [ + { id: 1, content: "谦虚不适合我。", value: "1" }, + { id: 2, content: "谦虚适合我。", value: "0" } + ], + [ + { id: 1, content: "如果有人激将,我几乎什么都敢做。", value: "1" }, + { id: 2, content: "我倾向于是一个相当谨慎的人。", value: "0" } + ], + [ + { id: 1, content: "当人们赞美我时,我有时会感到尴尬。", value: "0" }, + { id: 2, content: "我知道我很好,因为每个人都这样告诉我。", value: "1" } + ], + [ + { id: 1, content: "统治世界的想法让我害怕得要死。", value: "0" }, + { id: 2, content: "如果我统治世界,世界会变得更好。", value: "1" } + ], + [ + { id: 1, content: "我通常能说服别人让我摆脱困境。", value: "1" }, + { id: 2, content: "我努力接受自己行为的后果。", value: "0" } + ], + [ + { id: 1, content: "我更喜欢融入人群。", value: "0" }, + { id: 2, content: "我喜欢成为关注的焦点。", value: "1" } + ], + [ + { id: 1, content: "我将会成功。", value: "1" }, + { id: 2, content: "我不太关心成功。", value: "0" } + ], + [ + { id: 1, content: "我并不比大多数人好或差。", value: "0" }, + { id: 2, content: "我认为我是一个特殊的人。", value: "1" } + ], + [ + { id: 1, content: "我不确定我是否会成为一个好领导。", value: "0" }, + { id: 2, content: "我认为自己是一个好领导。", value: "1" } + ], + [ + { id: 1, content: "我很有主见。", value: "1" }, + { id: 2, content: "我希望我更有主见一些。", value: "0" } + ], + [ + { id: 1, content: "我喜欢对别人有权威。", value: "1" }, + { id: 2, content: "我不介意听从命令。", value: "0" } + ], + [ + { id: 1, content: "我发现操纵别人很容易。", value: "1" }, + { id: 2, content: "当我发现自己在操纵别人时,我不喜欢这样。", value: "0" } + ], + [ + { id: 1, content: "我坚持要得到应得的尊重。", value: "1" }, + { id: 2, content: "我通常得到我应得的尊重。", value: "0" } + ], + [ + { id: 1, content: "我不特别喜欢炫耀我的身体。", value: "0" }, + { id: 2, content: "我喜欢炫耀我的身体。", value: "1" } + ], + [ + { id: 1, content: "我能像读书一样读懂人心。", value: "1" }, + { id: 2, content: "人有时很难理解。", value: "0" } + ] + ]; + + return optionPairs[id - 1] || []; + } +}; diff --git a/questionairies/ocd/zh.ts b/questionairies/ocd/zh.ts new file mode 100644 index 0000000..5247d73 --- /dev/null +++ b/questionairies/ocd/zh.ts @@ -0,0 +1,126 @@ +import { Questionnaire } from "@/types"; + +export const ocd: Questionnaire = { + id: "ocd", + title: "Yale-Brown强迫症状量表", + description: "评估强迫症状及其严重程度", + category: "心理健康", + tags: ["强迫", "筛查", "自评", "临床常用"], + time: "5-10分钟", + details: { + introduction: "耶鲁-布朗强迫症量表(Yale-Brown Obsessive Compulsive Scale, Y-BOCS)是由美国古德曼等人根据DSM-III-R诊断标准制定的专门测定强迫症状严重程度的量表,是临床上使用的评定强迫症的主要量表之一。整个量表共10个项目,用于反映测试者的强迫思维和强迫行为,下面请根据最近一周的情绪进行选择。", + questionCount: "10个核心项目", + evaluationTime: "通常为5-10分钟", + instructions: "Y-BOCS量表包含10个项目,分别评估强迫观念(1-5题)和强迫行为(6-10题)的严重程度。每个项目按0-4分5级评分,0分表示无症状,4分表示极重症状。", + scoringMethod: [ + "总分:将10个核心项目得分相加,范围0-40分。", + "分量表分:强迫观念分量表(1-5题)和强迫行为分量表(6-10题)各0-20分。", + "严重程度判断:总分0-7分为正常;8-15分为轻微强迫症状;16-23分为中度强迫症状;24-31分为严重强迫症状;32-40分为极严重强迫症状。" + ], + dimensions: [ + { name: "强迫观念时间", description: "评估患者每天被强迫观念占据的时间。" }, + { name: "强迫观念干扰", description: "评估强迫观念对患者社会功能和日常活动的干扰程度。" }, + { name: "强迫观念痛苦", description: "评估强迫观念引起的焦虑或痛苦程度。" }, + { name: "抵抗强迫观念", description: "评估患者抵抗强迫观念的努力程度。" }, + { name: "控制强迫观念", description: "评估患者对强迫观念的控制能力。" }, + { name: "强迫行为时间", description: "评估患者每天花在强迫行为上的时间。" }, + { name: "强迫行为干扰", description: "评估强迫行为对患者社会功能和日常活动的干扰程度。" }, + { name: "强迫行为痛苦", description: "评估如果阻止强迫行为会引起的焦虑程度。" }, + { name: "抵抗强迫行为", description: "评估患者抵抗强迫行为的努力程度。" }, + { name: "控制强迫行为", description: "评估患者对强迫行为的控制能力。" } + ], + notes: [ + "评估应在安静、不受干扰的环境中进行,确保测试者舒适和隐私。", + "需要明确区分强迫症状与其他精神障碍症状。", + "评估时应考虑症状的变化和波动,建议定期重复评估以监测变化。" + ], + references: [ + { + text: "Goodman, W. K., Price, L. H., Rasmussen, S. A., Mazure, C., Fleischmann, R. L., Hill, C. L., ... & Charney, D. S. (1989). The Yale-Brown Obsessive Compulsive Scale: I. Development, use, and reliability. Archives of General Psychiatry, 46(11), 1006-1011.", + url: "https://osf.io/tn2vg/download" + } + ] + }, + questions: [ + { + id: 1, + content: "一天之中,强迫性意念占据您多少时间?", + }, + { + id: 2, + content: "强迫性意念,对您日常生活或工作的影响程度?", + }, + { + id: 3, + content: "强迫性意念发生时,对您造成多大的困扰?", + }, + { + id: 4, + content: "强迫性意念发生时,您会在多少方面去抗拒它?", + }, + { + id: 5, + content: "您可以控制您的强迫性意念?", + }, + { + id: 6, + content: "一天之中,强迫行为占据您多少时间?", + }, + { + id: 7, + content: "强迫行为,对您日常生活或工作的影响程度?", + }, + { + id: 8, + content: "强迫行为发生时,对您造成多大的困扰?", + }, + { + id: 9, + content: "当强迫行为发生时,您会在多少方面去抗拒它?", + }, + { + id: 10, + content: "您可以控制您的强迫行为吗?", + } + ], + // Return different options for different questions based on the official Chinese version of the Y-BOCS scale + renderOptions: (id: number) => { + // General severity (time ratio/impact/distress) + const severityOptions = [ + { id: 1, content: '无', value: '0' }, + { id: 2, content: '轻度', value: '1' }, + { id: 3, content: '中度', value: '2' }, + { id: 4, content: '重度', value: '3' }, + { id: 5, content: '极重度', value: '4' }, + ]; + + // Resistance level (questions 4 & 9) + const resistanceOptions = [ + { id: 1, content: '总能抵抗', value: '0' }, + { id: 2, content: '大部分时间能抵抗', value: '1' }, + { id: 3, content: '有时能抵抗', value: '2' }, + { id: 4, content: '很少能抵抗', value: '3' }, + { id: 5, content: '完全无法抵抗', value: '4' }, + ]; + + // Control ability (questions 5 & 10) + const controlOptions = [ + { id: 1, content: '完全可以控制', value: '0' }, + { id: 2, content: '大部分时间可以控制', value: '1' }, + { id: 3, content: '有时可以控制', value: '2' }, + { id: 4, content: '很少可以控制', value: '3' }, + { id: 5, content: '完全不能控制', value: '4' }, + ]; + + switch (id) { + case 4: + case 9: + return resistanceOptions; + case 5: + case 10: + return controlOptions; + default: + return severityOptions; + } + } +} diff --git a/questionairies/oeps/zh.ts b/questionairies/oeps/zh.ts new file mode 100644 index 0000000..aea4a5a --- /dev/null +++ b/questionairies/oeps/zh.ts @@ -0,0 +1,146 @@ +import { Questionnaire } from '@/types'; + +const likertOptions = [ + { id: 1, content: '非常不同意', value: '1' }, + { id: 2, content: '比较不同意', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '比较同意', value: '4' }, + { id: 5, content: '非常同意', value: '5' }, +]; + +const bipolarOptions: Record = { + 37: { left: '自信', right: '不安' }, + 38: { left: '理性', right: '异想天开' }, + 39: { left: '没什么野心', right: '有驱动力' }, + 40: { left: '外向', right: '内向' }, + 41: { left: '有条理', right: '混乱' }, + 42: { left: '感性亲近', right: '疏离' }, + 43: { left: '奔放', right: '温顺' }, + 44: { left: '几乎不哭', right: '容易哭' }, + 45: { left: '强硬', right: '温和' }, + 46: { left: '散乱', right: '自律' }, + 47: { left: '有伤害性', right: '滋养他人' }, + 48: { left: '攀附地位', right: '不随大流' }, + 49: { left: '稳定', right: '不稳定' }, + 50: { left: '人际取向', right: '事物取向' }, + 51: { left: '乐观', right: '恐惧' }, + 52: { left: '自发', right: '审慎' }, + 53: { left: '厚脸皮', right: '敏感' }, + 54: { left: '中立', right: '有主见' }, +}; + +export const oeps: Questionnaire = { + id: 'oeps', + title: '开放式九型人格量表(OEPS)', + description: '基于开放心理测量项目的九型人格自评量表', + category: '人格', + tags: ['九型人格', '自评'], + time: '5-8分钟', + details: { + introduction: + '开放式九型人格量表(Open Enneagram of Personality Scales, OEPS)使用九型人格模型,将作答者与九种常见人格类型描述进行匹配。九型人格更适合作为自我探索工具,不属于主流临床诊断模型。', + questionCount: '54个项目', + evaluationTime: '通常为5-8分钟', + instructions: + '请根据这些描述平时有多符合你来作答。前半部分为同意程度题,后半部分为两极描述题,请选择更接近自己的一侧。', + scoringMethod: [ + '每题按1-5分计分。', + '前36题按九型人格类型分组计分。', + '后18题按两极描述方向为相关类型加分。', + '结果按每一型的平均得分排序,最高分作为主要候选类型。', + ], + dimensions: [ + { name: '1号 改革者', description: '追求正确、负责、自律,害怕自己有缺陷或不够好。' }, + { name: '2号 助人者', description: '重视关系、需要被爱与被需要,倾向照顾他人。' }, + { name: '3号 成就者', description: '重视效率、成就和被认可,害怕失败或平庸。' }, + { name: '4号 自我型', description: '重视独特性、真实感和情绪深度。' }, + { name: '5号 探索者', description: '重视知识、能力和独立空间,倾向观察和理解。' }, + { name: '6号 忠诚者', description: '重视安全、支持和可靠性,容易预想风险。' }, + { name: '7号 享乐者', description: '追求自由、新体验、快乐和可能性。' }, + { name: '8号 挑战者', description: '重视力量、直接和掌控,害怕被控制或受伤。' }, + { name: '9号 调停者', description: '追求和谐、稳定和避免冲突。' }, + ], + notes: [ + 'OEPS 适合自我探索,不构成心理诊断。', + '九型人格不是主流心理学研究模型,结果应作为参考材料。', + '如果多个类型分数接近,可以结合文字描述继续自我核对。', + ], + references: [ + { + text: 'Open Psychometrics: Open Enneagram of Personality Scales', + url: 'https://openpsychometrics.org/tests/OEPS/', + }, + { + text: 'Development of the Open Enneagram of Personality Scales', + url: 'https://openpsychometrics.org/tests/OEPS/development/', + }, + ], + }, + questions: [ + { id: 1, content: '我追求高效率。' }, + { id: 2, content: '我生命中最重要的是与他人的关系。' }, + { id: 3, content: '我把工作放在优先位置。' }, + { id: 4, content: '我会幻想自己坠入爱河。' }, + { id: 5, content: '我不太容易表现情绪。' }, + { id: 6, content: '害怕被利用让我不太容易信任别人。' }, + { id: 7, content: '我总是需要新的体验。' }, + { id: 8, content: '需要领导者时,我会自然站出来。' }, + { id: 9, content: '别人争吵时,我会离开现场。' }, + { id: 10, content: '我是完美主义者。' }, + { id: 11, content: '我很难拒绝别人。' }, + { id: 12, content: '我喜欢显得突出。' }, + { id: 13, content: '我很享受苦乐参半的感觉。' }, + { id: 14, content: '我会花很多时间独自投入自己的兴趣。' }, + { id: 15, content: '做决定前,我会先征求别人的意见。' }, + { id: 16, content: '我能和任何人持续聊任何话题。' }, + { id: 17, content: '我喜欢没有人意见一致的谈话。' }, + { id: 18, content: '为了避免麻烦,我会把想法藏在心里。' }, + { id: 19, content: '我经常不得不重做别人做过的工作。' }, + { id: 20, content: '帮助别人实现目标会让我很满足。' }, + { id: 21, content: '醒来后有一整天安排好的活动会让我感觉不错。' }, + { id: 22, content: '我会哭。' }, + { id: 23, content: '我大部分时间都在试图理解事情。' }, + { id: 24, content: '我会遵从规范。' }, + { id: 25, content: '我比较不受拘束。' }, + { id: 26, content: '我希望别人告诉我真相,而不是顾及我的感受。' }, + { id: 27, content: '我很接纳,也很灵活。' }, + { id: 28, content: '我会把自己的物品整理好。' }, + { id: 29, content: '我把家庭放在优先位置。' }, + { id: 30, content: '金钱对我的幸福很重要。' }, + { id: 31, content: '我更愿意站在反叛者一边,而不是既有秩序一边。' }, + { id: 32, content: '我喜欢智力挑战。' }, + { id: 33, content: '我很忠诚。' }, + { id: 34, content: '我总会试着用好笑的玩笑打破紧张气氛。' }, + { id: 35, content: '我喜欢果断的领导者。' }, + { id: 36, content: '我会避免正面冲突。' }, + { id: 37, content: '请选择更接近你的描述:自信 / 不安' }, + { id: 38, content: '请选择更接近你的描述:理性 / 异想天开' }, + { id: 39, content: '请选择更接近你的描述:没什么野心 / 有驱动力' }, + { id: 40, content: '请选择更接近你的描述:外向 / 内向' }, + { id: 41, content: '请选择更接近你的描述:有条理 / 混乱' }, + { id: 42, content: '请选择更接近你的描述:感性亲近 / 疏离' }, + { id: 43, content: '请选择更接近你的描述:奔放 / 温顺' }, + { id: 44, content: '请选择更接近你的描述:几乎不哭 / 容易哭' }, + { id: 45, content: '请选择更接近你的描述:强硬 / 温和' }, + { id: 46, content: '请选择更接近你的描述:散乱 / 自律' }, + { id: 47, content: '请选择更接近你的描述:有伤害性 / 滋养他人' }, + { id: 48, content: '请选择更接近你的描述:攀附地位 / 不随大流' }, + { id: 49, content: '请选择更接近你的描述:稳定 / 不稳定' }, + { id: 50, content: '请选择更接近你的描述:人际取向 / 事物取向' }, + { id: 51, content: '请选择更接近你的描述:乐观 / 恐惧' }, + { id: 52, content: '请选择更接近你的描述:自发 / 审慎' }, + { id: 53, content: '请选择更接近你的描述:厚脸皮 / 敏感' }, + { id: 54, content: '请选择更接近你的描述:中立 / 有主见' }, + ], + renderOptions: (id: number) => { + const pair = bipolarOptions[id]; + if (!pair) return likertOptions; + return [ + { id: 1, content: `非常偏向:${pair.left}`, value: '1' }, + { id: 2, content: `比较偏向:${pair.left}`, value: '2' }, + { id: 3, content: '两者差不多', value: '3' }, + { id: 4, content: `比较偏向:${pair.right}`, value: '4' }, + { id: 5, content: `非常偏向:${pair.right}`, value: '5' }, + ]; + }, +}; diff --git a/questionairies/phq9/zh.ts b/questionairies/phq9/zh.ts new file mode 100644 index 0000000..0bb3854 --- /dev/null +++ b/questionairies/phq9/zh.ts @@ -0,0 +1,61 @@ +import { Questionnaire } from "@/types"; + +export const phq9: Questionnaire = { + id: "phq9", + title: "PHQ-9患者健康问卷", + description: "评估抑郁症状的严重程度和频率", + category: "情绪", + tags: ["抑郁", "筛查", "自评", "临床常用"], + time: "3-5分钟", + details: { + introduction: "患者健康问卷-9(Patient Health Questionnaire-9, PHQ-9)是一个广泛使用的抑郁症筛查和评估工具。该问卷由9个项目组成,基于DSM-IV诊断标准中的抑郁症症状标准设计。PHQ-9不仅可以用于筛查抑郁症,还可以评估抑郁症状的严重程度和治疗效果。", + questionCount: "9个项目", + evaluationTime: "通常为3-5分钟", + instructions: "请根据您在过去两周内的实际感受,选择最符合您情况的选项。每个问题都有四个选择:完全没有、好几天、一半以上的天数、几乎每天。", + scoringMethod: [ + "总分:将9个项目得分相加,范围0-27分", + "计分方式:完全没有=0分,好几天=1分,一半以上的天数=2分,几乎每天=3分", + "严重程度:0-4分为无/最低程度,5-9分为轻度,10-14分为中度,15-19分为中重度,20-27分为重度抑郁" + ], + dimensions: [ + { name: "兴趣丧失", description: "对活动失去兴趣或快感" }, + { name: "情绪低落", description: "感到沮丧、抑郁或绝望" }, + { name: "睡眠问题", description: "入睡困难、睡眠不安或睡眠过多" }, + { name: "疲劳感", description: "感到疲倦或没有活力" }, + { name: "食欲改变", description: "食欲不振或吃得过多" }, + { name: "自我评价", description: "对自己感到不满或认为自己让家人失望" }, + { name: "注意力", description: "注意力集中困难" }, + { name: "精神运动", description: "动作或说话缓慢,或坐立不安" }, + { name: "自伤想法", description: "有伤害自己或自杀的想法" } + ], + notes: [ + "该量表适用于成年人抑郁症状筛查和严重程度评估", + "如果总分≥10分,建议寻求专业心理医生的评估", + "如果第9题(自伤想法)得分≥1分,需要立即寻求专业帮助", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Kroenke, K., Spitzer, R. L., & Williams, J. B. (2001). The PHQ‐9: validity of a brief depression severity measure. Journal of general internal medicine, 16(9), 606-613.", + url: "https://link.springer.com/article/10.1046/j.1525-1497.2001.016009606.x" + } + ] + }, + questions: [ + { id: 1, content: "做事时提不起劲或没有兴趣" }, + { id: 2, content: "感到心情低落、沮丧或绝望" }, + { id: 3, content: "入睡困难、睡不安稳或睡眠过多" }, + { id: 4, content: "感觉疲倦或没有活力" }, + { id: 5, content: "食欲不振或吃太多" }, + { id: 6, content: "觉得自己很糟糕,或觉得自己很失败,或让自己或家人失望" }, + { id: 7, content: "对事物专注有困难,例如阅读报纸或看电视时" }, + { id: 8, content: "动作或说话速度缓慢到别人已经察觉?或正好相反——比平时更加烦躁或坐立不安,动来动去" }, + { id: 9, content: "有不如死掉或用某种方式伤害自己的念头" } + ], + renderOptions: () => [ + { id: 1, content: "完全没有", value: "0" }, + { id: 2, content: "好几天", value: "1" }, + { id: 3, content: "一半以上的天数", value: "2" }, + { id: 4, content: "几乎每天", value: "3" } + ] +}; diff --git a/questionairies/pss10/zh.ts b/questionairies/pss10/zh.ts new file mode 100644 index 0000000..b2b6d9c --- /dev/null +++ b/questionairies/pss10/zh.ts @@ -0,0 +1,58 @@ +import { Questionnaire } from "@/types"; + +export const pss10: Questionnaire = { + id: "pss10", + title: "PSS-10感知压力量表", + description: "评估个体对生活中压力的主观感受", + category: "情绪", + tags: ["压力", "自评"], + time: "3-5分钟", + details: { + introduction: "感知压力量表(Perceived Stress Scale-10, PSS-10)是一个广泛使用的心理压力评估工具。该量表由10个项目组成,主要评估个体在过去一个月中对生活事件的压力感知程度。PSS-10不测量具体的压力源,而是测量个体对压力的主观体验和应对能力的感知。", + questionCount: "10个项目", + evaluationTime: "通常为3-5分钟", + instructions: "请根据您在过去一个月中的实际感受,选择最符合您情况的选项。每个问题都有五个选择:从不、几乎从不、有时、相当频繁、非常频繁。", + scoringMethod: [ + "总分:将10个项目得分相加,范围0-40分", + "正向计分项目:1、2、3、6、9、10题", + "反向计分项目:4、5、7、8题(倒序计分)", + "严重程度:得分越高表示感知到的压力越大" + ], + dimensions: [ + { name: "压力感知", description: "对生活中不可预测性和不可控制性的感知" }, + { name: "应对能力", description: "对自己应对能力和控制能力的信心" }, + { name: "压力超载", description: "感到生活中的要求超过了应对能力" }, + { name: "情绪反应", description: "对压力情境的情绪反应" } + ], + notes: [ + "该量表适用于评估成年人的压力感知水平", + "PSS-10得分没有绝对的临界值,需要结合个体情况综合分析", + "持续的高压力感知可能影响身心健康,建议寻求专业指导" + ], + references: [ + { + text: "Cohen, S., Kamarck, T., & Mermelstein, R. (1983). A global measure of perceived stress. Journal of health and social behavior, 385-396.", + url: "https://www.jstor.org/stable/2136404" + } + ] + }, + questions: [ + { id: 1, content: "在过去的一个月中,您有多经常因为发生了意想不到的事情而感到心烦意乱?" }, + { id: 2, content: "在过去的一个月中,您有多经常感到无法控制您生活中的重要事情?" }, + { id: 3, content: "在过去的一个月中,您有多经常感到紧张和压力?" }, + { id: 4, content: "在过去的一个月中,您有多经常自信地处理您的个人问题?" }, + { id: 5, content: "在过去的一个月中,您有多经常感到事情正朝着您希望的方向发展?" }, + { id: 6, content: "在过去的一个月中,您有多经常发现您无法应付所有您必须做的事情?" }, + { id: 7, content: "在过去的一个月中,您有多经常能够控制生活中令人烦恼的事情?" }, + { id: 8, content: "在过去的一个月中,您有多经常感到您掌控着整个局面?" }, + { id: 9, content: "在过去的一个月中,您有多经常因为您无法控制的事情而感到生气?" }, + { id: 10, content: "在过去的一个月中,您有多经常感到困难重重,以至于您无法克服它们?" } + ], + renderOptions: () => [ + { id: 1, content: "从不", value: "0" }, + { id: 2, content: "几乎从不", value: "1" }, + { id: 3, content: "有时", value: "2" }, + { id: 4, content: "相当频繁", value: "3" }, + { id: 5, content: "非常频繁", value: "4" } + ] +}; diff --git a/questionairies/riasec/zh.ts b/questionairies/riasec/zh.ts new file mode 100644 index 0000000..646603c --- /dev/null +++ b/questionairies/riasec/zh.ts @@ -0,0 +1,110 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不喜欢', value: '1' }, + { id: 2, content: '比较不喜欢', value: '2' }, + { id: 3, content: '无所谓', value: '3' }, + { id: 4, content: '比较喜欢', value: '4' }, + { id: 5, content: '非常喜欢', value: '5' }, +]; + +export const riasec: Questionnaire = { + id: 'riasec', + title: 'RIASEC 霍兰德职业兴趣量表(48题)', + description: '了解你更喜欢哪类工作活动,并形成三位职业兴趣代码', + category: '职业', + tags: ['自评'], + time: '5-10分钟', + evaluation: { + academicRecognition: 'A+', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'RIASEC 将职业兴趣分为现实型、研究型、艺术型、社会型、企业型和常规型六类。本量表采用 Liao、Armstrong 与 Rounds 开发的48题 IIP RIASEC Markers,每类包含8项职业活动。得分最高的三个类型组成 Holland Code,用于探索更可能令人感兴趣的工作环境。', + questionCount: '48个项目', + evaluationTime: '通常为5-10分钟', + instructions: + '请判断自己会不会喜欢从事每项活动。回答兴趣本身,不要根据目前是否具备相应技能、收入高低或他人的期待作答。', + scoringMethod: [ + '每题1-5分,从“非常不喜欢”到“非常喜欢”。', + '六个类型各包含8题,分别计算总分和平均分。', + '得分最高的三个类型按顺序组成 Holland Code;分数接近时,类型顺序不必作绝对解释。', + ], + dimensions: [ + { name: 'R 现实型', description: '偏好动手、机械、设备、户外和具体操作任务。' }, + { name: 'I 研究型', description: '偏好观察、分析、研究、科学和解决复杂问题。' }, + { name: 'A 艺术型', description: '偏好创作、表达、设计、表演和开放式任务。' }, + { name: 'S 社会型', description: '偏好帮助、教学、照护、辅导和与人合作。' }, + { name: 'E 企业型', description: '偏好影响、说服、领导、销售和组织资源。' }, + { name: 'C 常规型', description: '偏好数据、记录、流程、秩序和明确规范。' }, + ], + notes: [ + '职业兴趣不等同于能力,也不直接决定职业选择。', + '教育、工作经验和生活阶段可能使兴趣结构发生变化。', + '结果适合用于拓展探索方向,不应作为排除职业的唯一依据。', + ], + references: [ + { + text: 'OpenPsychometrics:Holland Code (RIASEC) Test', + url: 'https://openpsychometrics.org/tests/RIASEC/', + }, + { + text: 'Liao, Armstrong & Rounds (2008)', + url: 'https://doi.org/10.1016/j.jvb.2007.12.002', + }, + ], + }, + questions: [ + { id: 1, content: '测试零件在出货前的质量。' }, + { id: 2, content: '铺砖或瓷砖。' }, + { id: 3, content: '在海上石油钻井平台工作。' }, + { id: 4, content: '组装电子零件。' }, + { id: 5, content: '在工厂操作磨床。' }, + { id: 6, content: '修理坏掉的水龙头。' }, + { id: 7, content: '在工厂组装产品。' }, + { id: 8, content: '为房屋铺设地板。' }, + { id: 9, content: '研究人体结构。' }, + { id: 10, content: '研究动物行为。' }, + { id: 11, content: '对植物或动物进行研究。' }, + { id: 12, content: '开发新的医疗治疗方法或操作流程。' }, + { id: 13, content: '开展生物学研究。' }, + { id: 14, content: '研究鲸鱼及其他海洋生物。' }, + { id: 15, content: '在生物实验室工作。' }, + { id: 16, content: '绘制海底地图。' }, + { id: 17, content: '指挥合唱团。' }, + { id: 18, content: '执导戏剧。' }, + { id: 19, content: '为杂志设计艺术作品。' }, + { id: 20, content: '创作歌曲。' }, + { id: 21, content: '撰写书籍或剧本。' }, + { id: 22, content: '演奏乐器。' }, + { id: 23, content: '为电影或电视节目表演特技。' }, + { id: 24, content: '设计舞台布景。' }, + { id: 25, content: '为他人提供职业指导。' }, + { id: 26, content: '在非营利组织做志愿工作。' }, + { id: 27, content: '帮助有药物或酒精依赖问题的人。' }, + { id: 28, content: '教别人一套锻炼方法。' }, + { id: 29, content: '帮助有家庭问题的人。' }, + { id: 30, content: '监督夏令营儿童的活动。' }, + { id: 31, content: '教儿童阅读。' }, + { id: 32, content: '帮助老年人完成日常活动。' }, + { id: 33, content: '向个人销售餐厅特许经营权。' }, + { id: 34, content: '在百货商店销售商品。' }, + { id: 35, content: '管理酒店运营。' }, + { id: 36, content: '经营美容院或理发店。' }, + { id: 37, content: '管理大型公司中的一个部门。' }, + { id: 38, content: '管理服装店。' }, + { id: 39, content: '销售房屋。' }, + { id: 40, content: '经营玩具店。' }, + { id: 41, content: '为办公室制作每月工资单。' }, + { id: 42, content: '使用手持设备盘点物资。' }, + { id: 43, content: '使用计算机程序生成客户账单。' }, + { id: 44, content: '维护员工档案。' }, + { id: 45, content: '计算并记录统计及其他数值数据。' }, + { id: 46, content: '操作计算器。' }, + { id: 47, content: '办理客户的银行交易。' }, + { id: 48, content: '保存收发货记录。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/schwartz/zh.ts b/questionairies/schwartz/zh.ts new file mode 100644 index 0000000..5c327d2 --- /dev/null +++ b/questionairies/schwartz/zh.ts @@ -0,0 +1,96 @@ +import { Questionnaire } from "@/types"; + +const options = [ + { id: 1, content: "完全不像我", value: "1" }, + { id: 2, content: "不太像我", value: "2" }, + { id: 3, content: "有点像我", value: "3" }, + { id: 4, content: "比较像我", value: "4" }, + { id: 5, content: "非常像我", value: "5" }, +]; + +export const schwartz: Questionnaire = { + id: "schwartz", + title: "Schwartz 价值观量表", + description: "评估十种基础价值取向和四类高阶价值倾向", + category: "人格", + tags: ["自评"], + time: "5-7分钟", + evaluation: { + academicRecognition: "A+", + retestSuitable: true, + recommendedInterval: "1年", + }, + details: { + introduction: + "Schwartz价值观理论用于描述个体在生活目标和行为选择中重视什么。本量表围绕十种基础价值观进行自评,包括自主、刺激、享乐、成就、权力、安全、遵从、传统、仁慈和普世主义,并进一步归纳为开放变化、保守稳定、自我提升和自我超越四类高阶价值。", + questionCount: "30个项目", + evaluationTime: "通常5-7分钟", + instructions: + "请根据这些描述与自己的相似程度作答。这里没有好坏对错,重点是了解你在不同价值目标上的相对优先级。", + scoringMethod: [ + "每题1-5分,分别计入十个基础价值观。", + "每个基础价值观由3题组成,取平均分,范围1-5分。", + "四个高阶价值由相关基础价值观平均得到,用来观察价值冲突和价值平衡。", + ], + dimensions: [ + { name: "自主", description: "独立思考、自由选择、创造和探索自己的道路。" }, + { name: "刺激", description: "追求新奇、挑战、变化和兴奋体验。" }, + { name: "享乐", description: "重视愉悦、享受和生活中的积极体验。" }, + { name: "成就", description: "通过能力、努力和结果获得认可。" }, + { name: "权力", description: "重视影响力、资源、地位和掌控感。" }, + { name: "安全", description: "重视稳定、秩序、健康和生活保障。" }, + { name: "遵从", description: "遵守规则,避免伤害他人或破坏秩序。" }, + { name: "传统", description: "尊重文化、家庭、宗教或群体传承。" }, + { name: "仁慈", description: "关心亲近的人,重视忠诚、照顾和支持。" }, + { name: "普世主义", description: "关心公平、自然、社会整体和更广泛的人群。" }, + ], + notes: [ + "价值观不是能力或人格优劣判断,而是行为选择中的优先级。", + "相邻价值通常更容易兼容,相对价值之间可能产生张力,例如开放变化与保守稳定。", + "价值观会随人生阶段、文化环境和重要经历发生变化,适合长期追踪。", + ], + references: [ + { + text: "Schwartz, S. H. (2012). An Overview of the Schwartz Theory of Basic Values.", + url: "https://doi.org/10.9707/2307-0919.1116", + }, + { + text: "European Social Survey Human Values Scale", + url: "https://zis.gesis.org/skala/Schwartz-Breyer-Danner-Human-Values-Scale-%28ESS%29?lang=en", + }, + ], + }, + questions: [ + { id: 1, content: "我重视独立思考,不喜欢别人替我决定该怎么想。" }, + { id: 2, content: "我喜欢尝试新鲜、刺激或有挑战的事情。" }, + { id: 3, content: "我希望生活中有足够多让自己愉快和享受的时刻。" }, + { id: 4, content: "对我来说,展现能力并获得认可很重要。" }, + { id: 5, content: "我希望自己拥有更多影响力和资源。" }, + { id: 6, content: "稳定、安全、可预期的生活对我很重要。" }, + { id: 7, content: "我认为遵守规则、避免给别人添麻烦很重要。" }, + { id: 8, content: "我重视传统、家庭或文化中延续下来的东西。" }, + { id: 9, content: "我愿意照顾和支持身边亲近的人。" }, + { id: 10, content: "我关心社会公平,也关心不同群体是否被尊重。" }, + { id: 11, content: "我希望有自由选择自己生活方式的空间。" }, + { id: 12, content: "如果生活总是一成不变,我会觉得缺少活力。" }, + { id: 13, content: "我认为享受生活本身也是重要的人生目标。" }, + { id: 14, content: "我会为了做出优秀成果而投入很多努力。" }, + { id: 15, content: "我希望在群体中拥有一定的话语权。" }, + { id: 16, content: "我会优先考虑风险是否可控。" }, + { id: 17, content: "即使没人监督,我也倾向于按约定和规范行事。" }, + { id: 18, content: "我觉得尊重长辈、仪式或习俗有其意义。" }, + { id: 19, content: "朋友或家人需要帮助时,我通常会认真回应。" }, + { id: 20, content: "我希望自己的选择不要伤害自然环境或弱势群体。" }, + { id: 21, content: "我喜欢自己寻找答案,而不是照搬现成说法。" }, + { id: 22, content: "我会被冒险、旅行或未知领域吸引。" }, + { id: 23, content: "我会主动安排让自己放松、开心或满足的活动。" }, + { id: 24, content: "我在意别人是否认可我的能力和成绩。" }, + { id: 25, content: "我希望自己能掌握关键资源,而不是完全依赖他人。" }, + { id: 26, content: "我希望生活环境有秩序,少一些不确定和混乱。" }, + { id: 27, content: "我会控制自己的行为,避免冒犯或影响别人。" }, + { id: 28, content: "我认为延续重要传统能让人更有归属感。" }, + { id: 29, content: "我很看重对亲近的人保持可靠和忠诚。" }, + { id: 30, content: "我认为理解不同观点、尊重差异很重要。" }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/scl90/zh.ts b/questionairies/scl90/zh.ts new file mode 100644 index 0000000..fe81190 --- /dev/null +++ b/questionairies/scl90/zh.ts @@ -0,0 +1,145 @@ +import { Questionnaire } from "@/types"; + +export const scl90: Questionnaire = { + id: "scl90", + title: "SCL-90症状自评量表", + description: "综合评估心理健康状况和精神症状", + category: "心理健康", + tags: ["筛查", "自评", "临床常用"], + time: "10-15分钟", + details: { + introduction: "症状自评量表SCL-90是世界上最著名的心理健康测试量表之一,是当前使用最为广泛的精神障碍和心理疾病门诊检查量表,适用于评估一个人的心理健康状况。该量表包含90个项目,从感觉、情绪、思维、意识、行为直至生活习惯、人际关系、饮食睡眠等多个方面评估心理健康程度。", + questionCount: "90个项目", + evaluationTime: "通常为10-15分钟", + instructions: "请仔细阅读每一个项目,根据您最近一周的实际情况进行评分。选择最符合您情况的选项。", + scoringMethod: [ + "总分:将所有项目得分相加", + "阳性项目数:得分≥2分的项目数", + "阳性症状均分:阳性项目总分/阳性项目数", + "严重程度:总分>160分或阳性项目数>43个提示存在心理问题" + ], + dimensions: [ + { name: "躯体化", description: "身体不适感,包括心血管、胃肠道、呼吸系统等症状" }, + { name: "强迫症状", description: "强迫思维和强迫行为等症状" }, + { name: "人际关系敏感", description: "人际交往中的自卑感、心神不安等" }, + { name: "抑郁", description: "情绪低落、兴趣丧失、绝望感等" }, + { name: "焦虑", description: "紧张、心烦、恐惧等情绪体验" }, + { name: "敌对", description: "愤怒、攻击性、烦躁等情绪" }, + { name: "恐怖", description: "对特定场所、人群、情境的恐惧" }, + { name: "偏执", description: "投射性思维、猜疑、敌意等" }, + { name: "精神病性", description: "幻觉、妄想、思维障碍等" }, + { name: "其他", description: "睡眠、饮食、性方面的问题" } + ], + notes: [ + "评估应在安静、不受干扰的环境中进行", + "需要真实反映自己的感受,不要过分思考", + "如果得分较高,建议咨询专业心理医生" + ], + references: [ + { + text: "王征宇, 张明园, 等. 症状自评量表SCL-90的信度和效度研究. 上海精神医学, 1984, 2(2): 68-70.", + url: "https://www.cnki.net" + } + ] + }, + questions: [ + // Somatization (1-12) + { id: 1, content: "头痛" }, + { id: 2, content: "神经过敏,心中不踏实" }, + { id: 3, content: "头脑中有不必要的想法或字句盘旋" }, + { id: 4, content: "头昏或昏倒" }, + { id: 5, content: "对异性的兴趣减退" }, + { id: 6, content: "对旁人责备求全" }, + { id: 7, content: "感到别人能控制您的思想" }, + { id: 8, content: "责怪别人制造麻烦" }, + { id: 9, content: "忘记性大" }, + { id: 10, content: "担心自己的衣着整齐和仪表端正" }, + { id: 11, content: "容易烦恼和激动" }, + { id: 12, content: "胸痛" }, + { id: 13, content: "害怕空旷的场所或街道" }, + { id: 14, content: "感到自己的精力下降,活动减慢" }, + { id: 15, content: "想结束自己的生命" }, + { id: 16, content: "听到旁人听不到的声音" }, + { id: 17, content: "发抖" }, + { id: 18, content: "感到大多数人都不可信任" }, + { id: 19, content: "胃口不好" }, + { id: 20, content: "容易哭泣" }, + { id: 21, content: "同异性相处时感到害羞不自在" }, + { id: 22, content: "感到受骗,中了圈套或有人想抓住您" }, + { id: 23, content: "无缘无故地突然感到害怕" }, + { id: 24, content: "自己不能控制地大发脾气" }, + { id: 25, content: "怕单独出门" }, + { id: 26, content: "经常责怪自己" }, + { id: 27, content: "腰痛" }, + { id: 28, content: "感到难以完成任务" }, + { id: 29, content: "感到孤独" }, + { id: 30, content: "感到苦闷" }, + { id: 31, content: "过分担忧" }, + { id: 32, content: "对事物不感兴趣" }, + { id: 33, content: "感到害怕" }, + { id: 34, content: "您的感情容易受到伤害" }, + { id: 35, content: "旁人能知道您的私下想法" }, + { id: 36, content: "感到别人不理解您或不同情您" }, + { id: 37, content: "感到人们对您不友好,不喜欢您" }, + { id: 38, content: "做事必须做得很慢以保证做得正确" }, + { id: 39, content: "心跳得很厉害" }, + { id: 40, content: "恶心或胃部不舒服" }, + { id: 41, content: "感到比不上他人" }, + { id: 42, content: "肌肉酸痛" }, + { id: 43, content: "感到有人在监视您、谈论您" }, + { id: 44, content: "难以入睡" }, + { id: 45, content: "做事必须反复检查" }, + { id: 46, content: "难以作出决定" }, + { id: 47, content: "怕乘电车、公共汽车、地铁或火车" }, + { id: 48, content: "呼吸有困难" }, + { id: 49, content: "一阵阵发冷或发热" }, + { id: 50, content: "因为感到害怕而避开某些东西、场所或活动" }, + { id: 51, content: "脑子变空了" }, + { id: 52, content: "身体的某一部分麻木或刺痛" }, + { id: 53, content: "喉咙有梗塞感" }, + { id: 54, content: "感到前途没有希望" }, + { id: 55, content: "不能集中注意力" }, + { id: 56, content: "感到身体的某一部分软弱无力" }, + { id: 57, content: "感到紧张或心神不定" }, + { id: 58, content: "感到手或脚发重" }, + { id: 59, content: "想到有关死亡的事" }, + { id: 60, content: "吃得太多" }, + { id: 61, content: "当别人看着您或谈论您时感到不自在" }, + { id: 62, content: "有一些不属于您自己的想法" }, + { id: 63, content: "有想打人或伤害他人的冲动" }, + { id: 64, content: "醒得太早" }, + { id: 65, content: "必须反复洗手、点数或触摸某些东西" }, + { id: 66, content: "睡得不稳不深" }, + { id: 67, content: "有想摔坏或破坏东西的冲动" }, + { id: 68, content: "有一些别人没有的想法" }, + { id: 69, content: "感到对别人神经过敏" }, + { id: 70, content: "在商店或电影院等人多的地方感到不自在" }, + { id: 71, content: "感到任何事情都很困难" }, + { id: 72, content: "一阵阵恐惧或惊恐" }, + { id: 73, content: "感到在公共场所吃东西很不舒服" }, + { id: 74, content: "经常与人争论" }, + { id: 75, content: "单独一人时神经很紧张" }, + { id: 76, content: "别人对您的成绩没有做出恰当的评价" }, + { id: 77, content: "即使和别人在一起也感到孤独" }, + { id: 78, content: "感到坐立不安心神不定" }, + { id: 79, content: "感到自己没有什么价值" }, + { id: 80, content: "感到熟悉的东西变成陌生或不象是真的" }, + { id: 81, content: "大叫或摔东西" }, + { id: 82, content: "害怕会在公共场所昏倒" }, + { id: 83, content: "感到别人想占您的便宜" }, + { id: 84, content: "为一些有关性的想法而苦恼" }, + { id: 85, content: "您认为应该因为自己的过错而受到惩罚" }, + { id: 86, content: "感到要很快把事情做完" }, + { id: 87, content: "感到自己的身体有严重问题" }, + { id: 88, content: "从未感到和其他人很亲近" }, + { id: 89, content: "感到有罪恶感" }, + { id: 90, content: "感到自己的脑子有毛病" } + ], + renderOptions: () => [ + { id: 1, content: "没有", value: "1" }, + { id: 2, content: "很轻", value: "2" }, + { id: 3, content: "中等", value: "3" }, + { id: 4, content: "偏重", value: "4" }, + { id: 5, content: "严重", value: "5" } + ] +}; diff --git a/questionairies/sds/zh.ts b/questionairies/sds/zh.ts new file mode 100644 index 0000000..1277a77 --- /dev/null +++ b/questionairies/sds/zh.ts @@ -0,0 +1,67 @@ +import { Questionnaire } from "@/types"; + +export const sds: Questionnaire = { + id: "sds", + title: "SDS抑郁自评量表", + description: "评估抑郁症状的严重程度", + category: "情绪", + tags: ["抑郁", "筛查", "自评", "临床常用"], + time: "5-10分钟", + details: { + introduction: "抑郁自评量表(Self-Rating Depression Scale, SDS)是由Zung于1965年编制的,用于评估抑郁症状的严重程度。该量表共20个项目,涵盖情感、躯体、精神运动和心理方面的症状。量表简单易用,适合作为抑郁症筛查工具。", + questionCount: "20个项目", + evaluationTime: "通常为5-10分钟", + instructions: "请根据您最近一周的实际感受,选择最符合您情况的选项。每个问题都有四个选择:很少、有些时间、相当多时间、绝大部分时间。", + scoringMethod: [ + "总分:将20个项目得分相加,再乘以1.25得到标准分", + "正向计分项目:1、3、4、7、8、9、10、13、15、19题", + "反向计分项目:2、5、6、11、12、14、16、17、18、20题(倒序计分)", + "严重程度:标准分53-62分为轻度抑郁,63-72分为中度抑郁,73分以上为重度抑郁" + ], + dimensions: [ + { name: "情感症状", description: "情绪低落、悲伤、绝望等情感体验" }, + { name: "躯体症状", description: "食欲不振、睡眠障碍、疲劳等身体症状" }, + { name: "精神运动症状", description: "行动迟缓、思维迟钝、注意力不集中等" }, + { name: "心理症状", description: "自我评价低、内疚感、无助感等心理体验" } + ], + notes: [ + "该量表适用于具有抑郁症状的成年人", + "如果标准分超过53分,建议寻求专业心理医生的帮助", + "该量表仅供筛查使用,不能替代专业诊断" + ], + references: [ + { + text: "Zung, W. W. (1965). A self-rating depression scale. Archives of general psychiatry, 12(1), 63-70.", + url: "https://jamanetwork.com/journals/jamapsychiatry/article-abstract/487993" + } + ] + }, + questions: [ + { id: 1, content: "我感到情绪沮丧,郁闷" }, + { id: 2, content: "我感到早晨心情最好" }, + { id: 3, content: "我要哭或想哭" }, + { id: 4, content: "我夜间睡眠不好" }, + { id: 5, content: "我吃饭像平时一样多" }, + { id: 6, content: "我的性功能正常" }, + { id: 7, content: "我感到体重减轻" }, + { id: 8, content: "我为便秘烦恼" }, + { id: 9, content: "我的心跳比平时快" }, + { id: 10, content: "我无故感到疲劳" }, + { id: 11, content: "我的头脑像往常一样清楚" }, + { id: 12, content: "我做事情像平时一样不感到困难" }, + { id: 13, content: "我坐卧不安,难以保持平静" }, + { id: 14, content: "我对未来感到有希望" }, + { id: 15, content: "我比平时更容易激怒" }, + { id: 16, content: "我觉得决定什么事很容易" }, + { id: 17, content: "我感到自己是有用的和不可缺少的人" }, + { id: 18, content: "我的生活很有意义" }, + { id: 19, content: "假若我死了别人会过得更好" }, + { id: 20, content: "我仍旧喜爱自己平时喜爱的东西" } + ], + renderOptions: () => [ + { id: 1, content: "很少", value: "1" }, + { id: 2, content: "有些时间", value: "2" }, + { id: 3, content: "相当多时间", value: "3" }, + { id: 4, content: "绝大部分时间", value: "4" } + ] +}; diff --git a/questionairies/self-control/zh.ts b/questionairies/self-control/zh.ts new file mode 100644 index 0000000..a2edd14 --- /dev/null +++ b/questionairies/self-control/zh.ts @@ -0,0 +1,77 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不同意', value: '1' }, + { id: 2, content: '不同意', value: '2' }, + { id: 3, content: '不确定', value: '3' }, + { id: 4, content: '同意', value: '4' }, + { id: 5, content: '非常同意', value: '5' }, +]; + +export const selfControl: Questionnaire = { + id: 'self-control', + title: 'Brief Self-Control 自控力量表(13题)', + description: '评估抵抗诱惑、维持习惯和管理冲动的能力倾向', + category: '人格', + tags: ['自评'], + time: '3-5分钟', + evaluation: { + academicRecognition: 'A', + retestSuitable: true, + recommendedInterval: '1年', + }, + details: { + introduction: + 'Brief Self-Control Scale 是常用的短版自控力量表,用于了解个体在抵抗诱惑、坚持计划、管理冲动和维持有益习惯方面的倾向。', + questionCount: '13个项目', + evaluationTime: '通常为3-5分钟', + instructions: + '请根据自己平时的表现作答,不需要因为某一次特别自律或特别失控的经历而改变判断。', + scoringMethod: [ + '每题1-5分,部分题目反向计分。', + '总分范围13-65分,分数越高表示自控力倾向越强。', + '结果可用于自我观察,不应被理解为能力诊断或道德评价。', + ], + dimensions: [ + { + name: '冲动管理', + description: '面对诱惑、即时满足和不合适行为时的抑制能力。', + }, + { + name: '计划执行', + description: '维持习惯、集中注意力、把事情推进到底的倾向。', + }, + ], + notes: [ + '自控力受睡眠、压力、环境诱因和任务设计影响,不是固定不变的标签。', + '低分可以提示从环境改造、习惯设计和减少诱惑暴露开始,而不是单纯责备自己。', + '该量表适合与拖延、注意力、压力和幸福感结果一起理解。', + ], + references: [ + { + text: 'Tangney, Baumeister & Boone (2004). High self-control predicts good adjustment, less pathology, better grades, and interpersonal success.', + url: 'https://doi.org/10.1111/j.0022-3506.2004.00263.x', + }, + { + text: 'Brief Self-Control Scale psychometric review', + url: 'https://pmc.ncbi.nlm.nih.gov/articles/PMC7261631/', + }, + ], + }, + questions: [ + { id: 1, content: '我通常能抵抗诱惑。' }, + { id: 2, content: '我很难改掉不好的习惯。' }, + { id: 3, content: '我有时会显得懒散。' }, + { id: 4, content: '我能避免那些对自己不利的事情。' }, + { id: 5, content: '如果一件事很有趣,即使对我不好,我也可能会去做。' }, + { id: 6, content: '我希望自己能更有自律。' }, + { id: 7, content: '娱乐和享受有时会妨碍我完成该做的事。' }, + { id: 8, content: '我有时难以集中注意力。' }, + { id: 9, content: '别人可能会说我有很强的自律性。' }, + { id: 10, content: '我有时会不经过充分考虑就行动。' }, + { id: 11, content: '我有时会说出不太合适的话。' }, + { id: 12, content: '我经常难以坚持自己的计划。' }, + { id: 13, content: '我擅长朝着长期目标稳步推进。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/self-esteem/zh.ts b/questionairies/self-esteem/zh.ts new file mode 100644 index 0000000..1815209 --- /dev/null +++ b/questionairies/self-esteem/zh.ts @@ -0,0 +1,73 @@ +import { Questionnaire } from '@/types'; + +const options = [ + { id: 1, content: '非常不同意', value: '0' }, + { id: 2, content: '不同意', value: '1' }, + { id: 3, content: '同意', value: '2' }, + { id: 4, content: '非常同意', value: '3' }, +]; + +export const selfEsteem: Questionnaire = { + id: 'self-esteem', + title: 'Rosenberg 自尊量表(10题)', + description: '评估一个人对自身价值和自我接纳的整体感受', + category: '人格', + tags: ['自评'], + time: '2-3分钟', + evaluation: { + academicRecognition: 'A+', + retestSuitable: true, + recommendedInterval: '3个月', + }, + details: { + introduction: + 'Rosenberg 自尊量表是最常用的整体自尊自评工具之一,用于了解一个人对自身价值、自我接纳和自我满意度的总体感受。它题量很短,适合做个人觉察和阶段性追踪。', + questionCount: '10个项目', + evaluationTime: '通常为2-3分钟', + instructions: + '请根据你通常对自己的看法作答,不需要只参考今天的情绪。每题选择最符合自己的一项。', + scoringMethod: [ + '每题0-3分,正向题从“非常不同意”到“非常同意”依次计0-3分。', + '负向题反向计分,使总分越高表示整体自尊水平越高。', + '总分范围0-30分,结果适合连续观察,不应单独用于诊断。', + ], + dimensions: [ + { + name: '自我价值感', + description: '对自己是否有价值、是否值得尊重的总体感受。', + }, + { + name: '自我接纳', + description: '能否接纳自己的优点、限制和不完美。', + }, + ], + notes: [ + '自尊会受到近期压力、人际关系、成就体验和生活阶段影响。', + '低分不等同于心理疾病,但提示可以进一步关注自我评价、情绪状态和支持系统。', + '如果结果伴随长期低落、无价值感或自我伤害想法,请及时寻求专业支持。', + ], + references: [ + { + text: 'University of Maryland: Using the Rosenberg Self-Esteem Scale', + url: 'https://socy.umd.edu/about-us/using-rosenberg-self-esteem-scale', + }, + { + text: 'Rosenberg, M. (1965). Society and the Adolescent Self-Image.', + url: 'https://openlibrary.org/works/OL5983267W/Society_and_the_adolescent_self-image', + }, + ], + }, + questions: [ + { id: 1, content: '总体来说,我对自己感到满意。' }, + { id: 2, content: '有时候我觉得自己一无是处。' }, + { id: 3, content: '我觉得自己有不少优点。' }, + { id: 4, content: '我能够像大多数人一样把事情做好。' }, + { id: 5, content: '我觉得自己没有太多值得骄傲的地方。' }, + { id: 6, content: '有时候我确实觉得自己很没用。' }, + { id: 7, content: '我觉得自己是一个有价值的人,至少不比别人差。' }, + { id: 8, content: '我希望自己能更尊重自己。' }, + { id: 9, content: '总体来说,我倾向于认为自己是失败的。' }, + { id: 10, content: '我对自己持积极态度。' }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/via/zh.ts b/questionairies/via/zh.ts new file mode 100644 index 0000000..88dd6c4 --- /dev/null +++ b/questionairies/via/zh.ts @@ -0,0 +1,113 @@ +import { Questionnaire } from "@/types"; + +const options = [ + { id: 1, content: "完全不像我", value: "1" }, + { id: 2, content: "不太像我", value: "2" }, + { id: 3, content: "有点像我", value: "3" }, + { id: 4, content: "比较像我", value: "4" }, + { id: 5, content: "非常像我", value: "5" }, +]; + +const strengths = [ + "创造力", "好奇心", "判断力", "热爱学习", "洞察力", "勇敢", + "坚持", "真诚", "热情", "爱", "善良", "社交智慧", + "团队精神", "公平", "领导力", "宽恕", "谦逊", "审慎", + "自我调节", "审美", "感恩", "希望", "幽默", "灵性", +]; + +export const via: Questionnaire = { + id: "via", + title: "VIA 性格优势量表", + description: "评估24项性格优势和六类核心美德倾向", + category: "人格", + tags: ["自评"], + time: "8-10分钟", + evaluation: { + academicRecognition: "A", + retestSuitable: true, + recommendedInterval: "1年", + }, + details: { + introduction: + "VIA性格优势框架来自积极心理学,用24项可培养的优势描述个体在思考、情感和行为中的积极特质。本版本用于了解优势排序和优势组合,不等同于官方VIA-IS题库。", + questionCount: "48个项目", + evaluationTime: "通常8-10分钟", + instructions: + "请根据平时的自己作答。结果强调你的相对优势,不把低分项目解释为缺陷。", + scoringMethod: [ + "每题1-5分,每项优势由2题组成,取平均分。", + "24项优势归入六类美德:智慧、勇气、仁爱、正义、节制、超越。", + "结果重点看前5项标志性优势,以及六类美德之间的整体分布。", + ], + dimensions: strengths.map((name) => ({ + name, + description: `评估${name}在日常选择、关系和行动中的表达程度。`, + })), + notes: [ + "优势不是固定标签,而是可以被练习和调用的倾向。", + "高分优势适合用于学习、职业和关系中的策略设计。", + "低分优势不代表弱点,只表示它们不是当前最自然调用的方式。", + ], + references: [ + { + text: "VIA Institute: 24 Character Strengths", + url: "https://www.viacharacter.org/character-strengths", + }, + { + text: "Peterson, C., & Seligman, M. E. P. Character Strengths and Virtues.", + url: "https://www.viacharacter.org/character-strengths-and-virtues", + }, + ], + }, + questions: [ + { id: 1, content: "我经常能想出不同寻常但有用的点子。" }, + { id: 2, content: "遇到问题时,我会尝试用新的方式解决。" }, + { id: 3, content: "我对未知事物通常有探索兴趣。" }, + { id: 4, content: "我喜欢追问事情背后的原因。" }, + { id: 5, content: "做判断前,我会尽量听取不同观点。" }, + { id: 6, content: "我愿意根据证据修正自己的看法。" }, + { id: 7, content: "学习新知识本身会让我感到满足。" }, + { id: 8, content: "即使没有外部要求,我也会主动学习。" }, + { id: 9, content: "我常能从复杂经历中总结出有用的经验。" }, + { id: 10, content: "别人遇到困惑时,我能提供比较有帮助的视角。" }, + { id: 11, content: "即使害怕,我也会在重要时刻站出来。" }, + { id: 12, content: "我愿意为重要原则承担一定压力。" }, + { id: 13, content: "遇到挫折时,我通常会继续推进。" }, + { id: 14, content: "我能把重要事情坚持完成。" }, + { id: 15, content: "我倾向于真实表达自己的想法和感受。" }, + { id: 16, content: "我不喜欢用伪装或夸大来获得认可。" }, + { id: 17, content: "我做事时常带着能量和投入感。" }, + { id: 18, content: "我容易被有意义的目标激发热情。" }, + { id: 19, content: "我很珍惜亲密关系中的连接和信任。" }, + { id: 20, content: "我愿意主动表达对重要他人的关心。" }, + { id: 21, content: "看到别人需要帮助时,我通常愿意伸出援手。" }, + { id: 22, content: "我会留意身边人是否被善待。" }, + { id: 23, content: "我能比较准确地读懂他人的情绪和处境。" }, + { id: 24, content: "我知道在不同人际场合中如何调整表达方式。" }, + { id: 25, content: "在团队中,我愿意承担自己的责任。" }, + { id: 26, content: "我重视合作,不喜欢只顾自己表现。" }, + { id: 27, content: "我希望规则对每个人都尽量公平。" }, + { id: 28, content: "做决定时,我会考虑是否偏袒了某一方。" }, + { id: 29, content: "我能组织大家朝共同目标推进。" }, + { id: 30, content: "需要协调分工时,我愿意承担领导责任。" }, + { id: 31, content: "别人犯错后,我愿意在合适时机放下怨气。" }, + { id: 32, content: "我能区分一个人的错误和这个人的整体价值。" }, + { id: 33, content: "取得成绩时,我不会过度夸大自己的作用。" }, + { id: 34, content: "我愿意承认自己仍有很多需要学习的地方。" }, + { id: 35, content: "重大决定前,我会认真考虑风险和后果。" }, + { id: 36, content: "我通常不会因为一时冲动就做出关键选择。" }, + { id: 37, content: "我能管理自己的情绪或欲望,不让它们完全控制行动。" }, + { id: 38, content: "我可以为了长期目标延迟眼前满足。" }, + { id: 39, content: "我容易被自然、艺术或卓越表现打动。" }, + { id: 40, content: "我会主动留意生活中美好或精彩的细节。" }, + { id: 41, content: "我经常意识到自己拥有值得感谢的东西。" }, + { id: 42, content: "别人给予帮助时,我会真诚地感激。" }, + { id: 43, content: "即使处境困难,我也能看到改善的可能。" }, + { id: 44, content: "我倾向于相信努力和时间能带来转机。" }, + { id: 45, content: "我能用幽默缓和压力或尴尬。" }, + { id: 46, content: "我喜欢让身边人的气氛轻松一些。" }, + { id: 47, content: "我会思考人生意义或更大的价值连接。" }, + { id: 48, content: "我觉得自己和某种更大的目标、信念或整体有关联。" }, + ], + renderOptions: () => options, +}; diff --git a/questionairies/who5/zh.ts b/questionairies/who5/zh.ts new file mode 100644 index 0000000..3b46999 --- /dev/null +++ b/questionairies/who5/zh.ts @@ -0,0 +1,58 @@ +import { Questionnaire } from "@/types"; + +export const who5: Questionnaire = { + id: "who5", + title: "WHO-5 幸福感指数", + description: "评估最近两周的主观幸福感和积极心理状态", + category: "心理健康", + tags: ["筛查", "自评", "临床常用"], + time: "1-2分钟", + evaluation: { + academicRecognition: "A+", + retestSuitable: true, + recommendedInterval: "3个月", + }, + details: { + introduction: "WHO-5幸福感指数是一份简短的主观幸福感筛查量表,由5个正向表述项目组成,常用于评估最近两周内的积极情绪、活力和日常兴趣。它适合作为幸福感与情绪状态的快速追踪工具。", + questionCount: "5个项目", + evaluationTime: "通常1-2分钟", + instructions: "请根据您在过去两周内的真实感受,选择最符合情况的选项。每题从0到5分,分数越高表示幸福感越好。", + scoringMethod: [ + "原始总分:5个项目相加,范围0-25分。", + "百分制得分:原始总分乘以4,范围0-100分。", + "通常原始总分低于13分,或任一题得分为0或1,提示幸福感偏低,建议结合PHQ-9、GAD-7或专业评估进一步了解。" + ], + dimensions: [ + { name: "积极情绪", description: "愉快、心情良好、精神状态积极" }, + { name: "放松与平静", description: "感到平静、放松和不紧绷" }, + { name: "活力", description: "醒来时感觉清新、有精力" }, + { name: "兴趣", description: "日常生活中有感兴趣的事情" } + ], + notes: [ + "WHO-5适合快速筛查和长期追踪幸福感变化。", + "该量表不是诊断工具,低分只提示需要进一步了解情绪和生活状态。", + "如果近期持续低落、绝望或出现伤害自己的想法,请及时寻求专业帮助。" + ], + references: [ + { + text: "Topp, C. W., Ostergaard, S. D., Sondergaard, S., & Bech, P. (2015). The WHO-5 Well-Being Index: a systematic review of the literature. Psychotherapy and Psychosomatics, 84(3), 167-176.", + url: "https://doi.org/10.1159/000376585" + } + ] + }, + questions: [ + { id: 1, content: "我感到快乐,心情愉快" }, + { id: 2, content: "我感到平静和放松" }, + { id: 3, content: "我感到精力充沛、充满活力" }, + { id: 4, content: "我醒来时感到清新、休息充分" }, + { id: 5, content: "我的日常生活中充满了让我感兴趣的事情" } + ], + renderOptions: () => [ + { id: 1, content: "从没有", value: "0" }, + { id: 2, content: "偶尔", value: "1" }, + { id: 3, content: "少于一半时间", value: "2" }, + { id: 4, content: "超过一半时间", value: "3" }, + { id: 5, content: "大部分时间", value: "4" }, + { id: 6, content: "所有时间", value: "5" } + ] +}; diff --git a/questionairies/zh.ts b/questionairies/zh.ts new file mode 100644 index 0000000..4cae0e4 --- /dev/null +++ b/questionairies/zh.ts @@ -0,0 +1,68 @@ +import { Questionnaire } from "@/types"; +import { bigfive } from "./bigfive/zh"; +import { bigfive120 } from "./bigfive/neo120-zh"; +import { bigfive300 } from "./bigfive/neo300-zh"; +import { crt } from "./crt/zh"; +import { oeps } from "./oeps/zh"; +import { ocd } from "./ocd/zh"; +import { scl90 } from "./scl90/zh"; +import { sds } from "./sds/zh"; +import { gad7 } from "./gad7/zh"; +import { phq9 } from "./phq9/zh"; +import { pss10 } from "./pss10/zh"; +import { dass21 } from "./dass21/zh"; +import { who5 } from "./who5/zh"; +import { bdi2 } from "./bdi2/zh"; +import { isi } from "./isi/zh"; +import { adhd } from "./adhd/zh"; +import { gd } from "./gd/zh"; +import { npd } from "./npd/zh"; +import { riasec } from "./riasec/zh"; +import { selfEsteem } from "./self-esteem/zh"; +import { grit } from "./grit/zh"; +import { selfControl } from "./self-control/zh"; +import { needForCognition } from "./need-for-cognition/zh"; +import { maximizer } from "./maximizer/zh"; +import { attachment } from "./attachment/zh"; +import { empathy } from "./empathy/zh"; +import { darkTriad } from "./dark-triad/zh"; +import { hexaco } from "./hexaco/zh"; +import { fisher } from "./fisher/zh"; +import { schwartz } from "./schwartz/zh"; +import { via } from "./via/zh"; +import { careerAnchors } from "./career-anchors/zh"; + +export const questionnairesZh: Questionnaire[] = [ + bigfive, + bigfive120, + bigfive300, + selfEsteem, + grit, + selfControl, + needForCognition, + maximizer, + attachment, + empathy, + darkTriad, + hexaco, + fisher, + schwartz, + via, + oeps, + crt, + riasec, + careerAnchors, + dass21, + who5, + ocd, + scl90, + sds, + gad7, + phq9, + pss10, + bdi2, + isi, + adhd, + gd, + npd +]; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d8b9323 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..36c0ff0 --- /dev/null +++ b/types/index.ts @@ -0,0 +1,72 @@ +export interface QuestionType { + id: number; + content: string; + options: Option[]; + factors?: string[]; +} + + +export interface Option { + id: number; + content: string; + value: string; +} + +export type QuestionnaireCategory = + | '人格' + | '认知' + | '情绪' + | '睡眠' + | '职业' + | '心理健康'; + +export type QuestionnaireTag = + | '抑郁' + | '焦虑' + | '压力' + | 'ADHD' + | '强迫' + | '失眠' + | '睡眠' + | '注意力' + | '执行功能' + | '依恋' + | '共情' + | '自恋' + | '九型人格' + | '300题' + | '120题' + | '筛查' + | '自评' + | '临床常用'; + +export interface QuestionnaireEvaluation { + academicRecognition: 'A+' | 'A' | 'B'; + retestSuitable: boolean; + recommendedInterval: '1年' | '3个月' | '一次即可'; +} + +export interface Questionnaire { + id: string; + title: string; + description: string; + category: QuestionnaireCategory; + tags: QuestionnaireTag[]; + time: string; + evaluation?: QuestionnaireEvaluation; + details: { + introduction: string; + questionCount: string; + evaluationTime: string; + instructions: string; + scoringMethod: string[]; + dimensions: Array<{ name: string; description: string }>; + notes: string[]; + references: Array<{ text: string; url: string }>; + }; + questions: { + id: number; + content: string; + }[]; + renderOptions: (id: number) => Option[]; +}