Spaces:
Running
Running
| <html lang="en" dir="ltr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AssessmentArchitect Pro - Intelligent Assessment Strategy Platform</title> | |
| <style> | |
| :root { | |
| --primary: #ea580c; | |
| --secondary: #f59e0b; | |
| --accent: #fb923c; | |
| --background: #f8fafc; | |
| --surface: #ffffff; | |
| --text: #1e293b; | |
| --text-secondary: #64748b; | |
| --border: #e2e8f0; | |
| --success: #10b981; | |
| --warning: #f59e0b; | |
| --error: #dc2626; | |
| --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); | |
| color: var(--text); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| padding: 15px; | |
| transition: all 0.3s ease; | |
| } | |
| /* RTL Support */ | |
| [dir="rtl"] { | |
| text-align: right; | |
| } | |
| [dir="rtl"] .container { | |
| direction: rtl; | |
| } | |
| [dir="rtl"] .api-key-section { | |
| margin: 15px 15px 15px auto; | |
| } | |
| [dir="rtl"] .language-switcher { | |
| right: auto; | |
| left: 15px; | |
| } | |
| /* Language Selection Landing */ | |
| .language-landing { | |
| display: block; | |
| max-width: 800px; | |
| margin: 50px auto; | |
| background: var(--surface); | |
| border-radius: 16px; | |
| box-shadow: var(--shadow-lg); | |
| padding: 40px; | |
| text-align: center; | |
| } | |
| .language-landing h1 { | |
| font-size: 2.5rem; | |
| font-weight: 800; | |
| background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 15px; | |
| } | |
| .language-landing p { | |
| font-size: 1.1rem; | |
| color: var(--text-secondary); | |
| margin-bottom: 30px; | |
| } | |
| .language-selector { | |
| margin-bottom: 25px; | |
| } | |
| .language-selector label { | |
| display: block; | |
| margin-bottom: 10px; | |
| font-weight: 600; | |
| color: var(--text); | |
| font-size: 1.1rem; | |
| } | |
| .language-selector select { | |
| width: 100%; | |
| padding: 15px 20px; | |
| border: 2px solid var(--border); | |
| border-radius: 12px; | |
| font-size: 1.1rem; | |
| font-family: inherit; | |
| background: var(--surface); | |
| transition: all 0.2s ease; | |
| margin-bottom: 20px; | |
| } | |
| .language-selector select:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| .api-key-landing { | |
| margin-bottom: 25px; | |
| } | |
| .api-key-landing label { | |
| display: block; | |
| margin-bottom: 10px; | |
| font-weight: 600; | |
| color: var(--text); | |
| font-size: 1.1rem; | |
| } | |
| .api-key-landing input { | |
| width: 100%; | |
| padding: 15px 20px; | |
| border: 2px solid var(--border); | |
| border-radius: 12px; | |
| font-size: 1.1rem; | |
| background: var(--surface); | |
| transition: all 0.2s ease; | |
| font-family: 'Courier New', monospace; | |
| } | |
| .api-key-landing input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| .start-btn { | |
| background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); | |
| color: white; | |
| padding: 18px 40px; | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 1.2rem; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| width: 100%; | |
| margin-bottom: 20px; | |
| } | |
| .start-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 12px 24px -8px rgba(234, 88, 12, 0.4); | |
| } | |
| .start-btn:disabled { | |
| opacity: 0.7; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| /* Cache Management Section */ | |
| .cache-management { | |
| background: rgba(234, 88, 12, 0.05); | |
| border: 1px solid rgba(234, 88, 12, 0.1); | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin-top: 20px; | |
| } | |
| .cache-management h3 { | |
| color: var(--primary); | |
| font-size: 1.1rem; | |
| font-weight: 700; | |
| margin-bottom: 15px; | |
| text-align: center; | |
| } | |
| .cache-status { | |
| background: rgba(16, 185, 129, 0.1); | |
| border: 1px solid rgba(16, 185, 129, 0.2); | |
| color: var(--success); | |
| padding: 12px 16px; | |
| border-radius: 8px; | |
| margin-bottom: 15px; | |
| font-size: 0.9rem; | |
| text-align: center; | |
| font-weight: 500; | |
| } | |
| .cache-status.no-cache { | |
| background: rgba(156, 163, 175, 0.1); | |
| border: 1px solid rgba(156, 163, 175, 0.2); | |
| color: var(--text-secondary); | |
| } | |
| .clear-cache-btn { | |
| background: linear-gradient(135deg, var(--warning) 0%, #f59e0b 100%); | |
| color: white; | |
| padding: 12px 24px; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| width: 100%; | |
| } | |
| .clear-cache-btn:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 8px 16px -4px rgba(245, 158, 11, 0.4); | |
| } | |
| .clear-cache-btn:active { | |
| transform: translateY(0); | |
| } | |
| /* Translation Loading Overlay */ | |
| .translation-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(234, 88, 12, 0.9); | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 10000; | |
| color: white; | |
| text-align: center; | |
| } | |
| .translation-content { | |
| background: rgba(255, 255, 255, 0.1); | |
| padding: 40px; | |
| border-radius: 16px; | |
| backdrop-filter: blur(10px); | |
| max-width: 400px; | |
| width: 90%; | |
| } | |
| .translation-spinner { | |
| width: 50px; | |
| height: 50px; | |
| border: 4px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top-color: white; | |
| animation: spin 1s ease-in-out infinite; | |
| margin: 0 auto 20px; | |
| } | |
| .translation-progress { | |
| background: rgba(255, 255, 255, 0.2); | |
| border-radius: 10px; | |
| height: 8px; | |
| margin: 20px 0; | |
| overflow: hidden; | |
| } | |
| .translation-progress-bar { | |
| background: white; | |
| height: 100%; | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| } | |
| /* Main Application (Hidden Initially) */ | |
| .main-app { | |
| display: none; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: var(--surface); | |
| border-radius: 16px; | |
| box-shadow: var(--shadow-lg); | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* API Key Section - Static Position Top Left */ | |
| .api-key-section { | |
| background: rgba(234, 88, 12, 0.05); | |
| border: 1px solid rgba(234, 88, 12, 0.1); | |
| border-radius: 8px; | |
| padding: 12px; | |
| margin: 15px; | |
| max-width: 280px; | |
| } | |
| .api-key-section label { | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--primary); | |
| margin-bottom: 6px; | |
| display: block; | |
| } | |
| .api-key-section input { | |
| width: 100%; | |
| padding: 8px 12px; | |
| border: 1px solid var(--border); | |
| border-radius: 6px; | |
| font-size: 14px; | |
| background: var(--surface); | |
| transition: all 0.2s ease; | |
| font-family: 'Courier New', monospace; | |
| } | |
| .api-key-section input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| /* Language Switcher in Main App */ | |
| .language-switcher { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| z-index: 100; | |
| } | |
| .language-switcher select { | |
| background: rgba(234, 88, 12, 0.05); | |
| border: 1px solid rgba(234, 88, 12, 0.1); | |
| border-radius: 8px; | |
| padding: 8px 12px; | |
| font-size: 12px; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .language-switcher select:hover { | |
| background: rgba(234, 88, 12, 0.1); | |
| } | |
| .clear-cache-mini { | |
| background: var(--warning); | |
| color: white; | |
| border: none; | |
| padding: 6px 12px; | |
| border-radius: 4px; | |
| font-size: 0.8rem; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .clear-cache-mini:hover { | |
| background: #d97706; | |
| } | |
| /* Main Content */ | |
| .main-content { | |
| padding: 20px 30px 30px; | |
| } | |
| /* Header */ | |
| .header { | |
| text-align: center; | |
| margin-bottom: 25px; | |
| padding-bottom: 20px; | |
| border-bottom: 2px solid var(--border); | |
| } | |
| .header h1 { | |
| font-size: 2.5rem; | |
| font-weight: 800; | |
| background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 8px; | |
| } | |
| .header .subtitle { | |
| font-size: 1.1rem; | |
| color: var(--text-secondary); | |
| font-weight: 500; | |
| } | |
| .header .description { | |
| font-size: 1rem; | |
| color: var(--text-secondary); | |
| margin-top: 12px; | |
| max-width: 700px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| } | |
| /* Configuration Section */ | |
| .config-section { | |
| background: var(--surface); | |
| border-radius: 16px; | |
| padding: 25px; | |
| box-shadow: var(--shadow); | |
| margin-bottom: 25px; | |
| transition: all 0.3s ease; | |
| } | |
| .config-section:hover { | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .config-item { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .config-label { | |
| font-size: 0.875rem; | |
| font-weight: 600; | |
| color: var(--text); | |
| margin-bottom: 8px; | |
| } | |
| .config-select, | |
| .config-textarea { | |
| padding: 12px; | |
| border: 2px solid var(--border); | |
| border-radius: 8px; | |
| font-size: 0.875rem; | |
| font-family: inherit; | |
| background: var(--background); | |
| color: var(--text); | |
| transition: all 0.2s ease; | |
| } | |
| .config-select:focus, | |
| .config-textarea:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| .config-textarea { | |
| resize: vertical; | |
| min-height: 80px; | |
| } | |
| /* Questions Section */ | |
| .questions-section { | |
| background: var(--surface); | |
| border-radius: 16px; | |
| padding: 30px; | |
| box-shadow: var(--shadow); | |
| margin-bottom: 25px; | |
| transition: all 0.3s ease; | |
| } | |
| .questions-section:hover { | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .section-title { | |
| font-size: 1.5rem; | |
| font-weight: 600; | |
| color: var(--text); | |
| margin-bottom: 25px; | |
| text-align: center; | |
| position: relative; | |
| } | |
| .section-title::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -8px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 60px; | |
| height: 3px; | |
| background: linear-gradient(90deg, var(--primary), var(--secondary)); | |
| border-radius: 2px; | |
| } | |
| .question-item { | |
| margin-bottom: 25px; | |
| padding: 20px; | |
| background: var(--background); | |
| border-radius: 12px; | |
| border: 1px solid var(--border); | |
| transition: all 0.3s ease; | |
| } | |
| .question-item:hover { | |
| border-color: var(--accent); | |
| box-shadow: var(--shadow); | |
| } | |
| .question-number { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 32px; | |
| height: 32px; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| color: white; | |
| border-radius: 50%; | |
| font-weight: 600; | |
| font-size: 0.875rem; | |
| margin-right: 12px; | |
| flex-shrink: 0; | |
| } | |
| [dir="rtl"] .question-number { | |
| margin-right: 0; | |
| margin-left: 12px; | |
| } | |
| .question-label { | |
| display: flex; | |
| align-items: flex-start; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| color: var(--text); | |
| margin-bottom: 15px; | |
| line-height: 1.4; | |
| } | |
| .question-textarea { | |
| width: 100%; | |
| min-height: 80px; | |
| padding: 15px; | |
| border: 2px solid var(--border); | |
| border-radius: 12px; | |
| font-size: 1rem; | |
| font-family: inherit; | |
| background: var(--surface); | |
| color: var(--text); | |
| transition: all 0.2s ease; | |
| resize: vertical; | |
| } | |
| .question-textarea:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| .question-textarea::placeholder { | |
| color: var(--text-secondary); | |
| } | |
| /* Generate Button */ | |
| .generate-section { | |
| text-align: center; | |
| margin-bottom: 25px; | |
| } | |
| .generate-button { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 18px 40px; | |
| font-size: 1.125rem; | |
| font-weight: 600; | |
| color: white; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| border: none; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| box-shadow: var(--shadow); | |
| position: relative; | |
| overflow: hidden; | |
| min-width: 320px; | |
| } | |
| .generate-button:hover:not(:disabled) { | |
| transform: translateY(-2px); | |
| box-shadow: 0 12px 24px -8px rgba(234, 88, 12, 0.4); | |
| } | |
| .generate-button:active { | |
| transform: translateY(0); | |
| } | |
| .generate-button:disabled { | |
| opacity: 0.7; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .button-text { | |
| transition: opacity 0.2s ease; | |
| } | |
| .loading-spinner { | |
| display: none; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid rgba(255, 255, 255, 0.3); | |
| border-top: 2px solid white; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| [dir="rtl"] .loading-spinner { | |
| margin-right: 0; | |
| margin-left: 8px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* Progress Bar */ | |
| .progress-container { | |
| width: 100%; | |
| height: 8px; | |
| background: var(--border); | |
| border-radius: 4px; | |
| overflow: hidden; | |
| margin-top: 15px; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--primary), var(--secondary)); | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| border-radius: 4px; | |
| } | |
| /* Output Section */ | |
| .output-section { | |
| background: var(--surface); | |
| border-radius: 16px; | |
| padding: 30px; | |
| box-shadow: var(--shadow); | |
| transition: all 0.3s ease; | |
| } | |
| .output-section:hover { | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .output-content { | |
| background: var(--background); | |
| border: 2px solid var(--border); | |
| border-radius: 12px; | |
| padding: 20px; | |
| min-height: 200px; | |
| font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; | |
| font-size: 0.875rem; | |
| line-height: 1.6; | |
| color: var(--text); | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| max-height: 600px; | |
| overflow-y: auto; | |
| transition: all 0.2s ease; | |
| } | |
| .output-content:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(234, 88, 12, 0.1); | |
| } | |
| .output-content:empty::before { | |
| content: attr(data-placeholder); | |
| color: var(--text-secondary); | |
| font-style: italic; | |
| font-family: inherit; | |
| } | |
| /* Error Messages */ | |
| .error-message { | |
| background: rgba(220, 38, 38, 0.1); | |
| border: 1px solid rgba(220, 38, 38, 0.2); | |
| color: var(--error); | |
| padding: 12px 16px; | |
| border-radius: 8px; | |
| margin: 10px 0; | |
| font-size: 0.9rem; | |
| display: none; | |
| } | |
| /* Status Messages */ | |
| .status-message { | |
| background: rgba(234, 88, 12, 0.1); | |
| border: 1px solid rgba(234, 88, 12, 0.2); | |
| color: var(--primary); | |
| padding: 12px 16px; | |
| border-radius: 8px; | |
| margin: 10px 0; | |
| font-size: 0.9rem; | |
| text-align: center; | |
| font-weight: 500; | |
| display: none; | |
| } | |
| /* Hidden sections */ | |
| .hidden { | |
| display: none ; | |
| } | |
| /* Footer */ | |
| .footer { | |
| text-align: center; | |
| padding: 20px; | |
| background: rgba(234, 88, 12, 0.05); | |
| border-top: 1px solid var(--border); | |
| color: var(--text-secondary); | |
| font-size: 0.9rem; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 768px) { | |
| body { | |
| padding: 10px; | |
| } | |
| .language-landing { | |
| margin: 20px auto; | |
| padding: 30px 20px; | |
| } | |
| .api-key-section { | |
| margin: 10px; | |
| max-width: none; | |
| } | |
| .main-content { | |
| padding: 15px 20px 20px; | |
| } | |
| .header h1 { | |
| font-size: 2rem; | |
| } | |
| .config-section, | |
| .questions-section, | |
| .output-section { | |
| padding: 20px 15px; | |
| } | |
| .question-item { | |
| padding: 15px; | |
| } | |
| .question-label { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 8px; | |
| } | |
| .question-number { | |
| margin-right: 0; | |
| margin-bottom: 8px; | |
| } | |
| .generate-button { | |
| width: 100%; | |
| margin: 25px 0; | |
| padding: 16px 32px; | |
| font-size: 1.1rem; | |
| } | |
| .language-switcher { | |
| position: static; | |
| text-align: center; | |
| margin-bottom: 20px; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .language-landing h1 { | |
| font-size: 2rem; | |
| } | |
| .header h1 { | |
| font-size: 1.8rem; | |
| } | |
| } | |
| /* Dark mode support */ | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --background: #0f172a; | |
| --surface: #1e293b; | |
| --text: #f1f5f9; | |
| --text-secondary: #94a3b8; | |
| --border: #334155; | |
| } | |
| body { | |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Translation Loading Overlay --> | |
| <div class="translation-overlay" id="translationOverlay"> | |
| <div class="translation-content"> | |
| <div class="translation-spinner"></div> | |
| <h2 id="translationTitle">Translating Interface...</h2> | |
| <p id="translationMessage">Please wait while we translate the interface to your selected language.</p> | |
| <div class="translation-progress"> | |
| <div class="translation-progress-bar" id="translationProgressBar"></div> | |
| </div> | |
| <p id="translationProgress">0%</p> | |
| </div> | |
| </div> | |
| <!-- Language Selection Landing Page --> | |
| <div class="language-landing" id="languageLanding"> | |
| <h1 data-translate="app_title">AssessmentArchitect Pro</h1> | |
| <p data-translate="welcome_message">Welcome! Please select your preferred language and enter your API key to get started with intelligent assessment strategy building.</p> | |
| <div class="language-selector"> | |
| <label for="languageSelect" data-translate="select_language">🌐 Select Language</label> | |
| <select id="languageSelect"> | |
| <option value="en">🇺🇸 English</option> | |
| <option value="es">🇪🇸 Español (Spanish)</option> | |
| <option value="fr">🇫🇷 Français (French)</option> | |
| <option value="de">🇩🇪 Deutsch (German)</option> | |
| <option value="it">🇮🇹 Italiano (Italian)</option> | |
| <option value="pt">🇵🇹 Português (Portuguese)</option> | |
| <option value="ru">🇷🇺 Русский (Russian)</option> | |
| <option value="ja">🇯🇵 日本語 (Japanese)</option> | |
| <option value="ko">🇰🇷 한국어 (Korean)</option> | |
| <option value="zh">🇨🇳 中文 (Chinese)</option> | |
| <option value="ar">🇸🇦 العربية (Arabic)</option> | |
| <option value="hi">🇮🇳 हिन्दी (Hindi)</option> | |
| <option value="bn">🇧🇩 বাংলা (Bengali)</option> | |
| <option value="ur">🇵🇰 اردو (Urdu)</option> | |
| <option value="tr">🇹🇷 Türkçe (Turkish)</option> | |
| <option value="pl">🇵🇱 Polski (Polish)</option> | |
| <option value="nl">🇳🇱 Nederlands (Dutch)</option> | |
| <option value="sv">🇸🇪 Svenska (Swedish)</option> | |
| <option value="da">🇩🇰 Dansk (Danish)</option> | |
| <option value="no">🇳🇴 Norsk (Norwegian)</option> | |
| <option value="fi">🇫🇮 Suomi (Finnish)</option> | |
| <option value="cs">🇨🇿 Čeština (Czech)</option> | |
| <option value="hu">🇭🇺 Magyar (Hungarian)</option> | |
| <option value="ro">🇷🇴 Română (Romanian)</option> | |
| <option value="bg">🇧🇬 Български (Bulgarian)</option> | |
| <option value="hr">🇭🇷 Hrvatski (Croatian)</option> | |
| <option value="sr">🇷🇸 Српски (Serbian)</option> | |
| <option value="sk">🇸🇰 Slovenčina (Slovak)</option> | |
| <option value="sl">🇸🇮 Slovenščina (Slovenian)</option> | |
| <option value="et">🇪🇪 Eesti (Estonian)</option> | |
| <option value="lv">🇱🇻 Latviešu (Latvian)</option> | |
| <option value="lt">🇱🇹 Lietuvių (Lithuanian)</option> | |
| <option value="el">🇬🇷 Ελληνικά (Greek)</option> | |
| <option value="he">🇮🇱 עברית (Hebrew)</option> | |
| <option value="th">🇹🇭 ไทย (Thai)</option> | |
| <option value="vi">🇻🇳 Tiếng Việt (Vietnamese)</option> | |
| <option value="km">🇰🇭 ខ្មែរ (Khmer)</option> | |
| <option value="my">🇲🇲 မြန်မာ (Myanmar)</option> | |
| <option value="ta">🇮🇳 தமிழ் (Tamil)</option> | |
| <option value="te">🇮🇳 తెలుగు (Telugu)</option> | |
| <option value="kn">🇮🇳 ಕನ್ನಡ (Kannada)</option> | |
| <option value="ml">🇮🇳 മലയാളം (Malayalam)</option> | |
| <option value="gu">🇮🇳 ગુજરાતી (Gujarati)</option> | |
| <option value="pa">🇮🇳 ਪੰਜਾਬੀ (Punjabi)</option> | |
| <option value="ne">🇳🇵 नेपाली (Nepali)</option> | |
| <option value="si">🇱🇰 සිංහල (Sinhala)</option> | |
| <option value="id">🇮🇩 Bahasa Indonesia</option> | |
| <option value="ms">🇲🇾 Bahasa Melayu (Malay)</option> | |
| <option value="tl">🇵🇭 Filipino</option> | |
| <option value="sw">🇰🇪 Kiswahili (Swahili)</option> | |
| <option value="zu">🇿🇦 isiZulu (Zulu)</option> | |
| <option value="xh">🇿🇦 isiXhosa (Xhosa)</option> | |
| <option value="af">🇿🇦 Afrikaans</option> | |
| <option value="am">🇪🇹 አማርኛ (Amharic)</option> | |
| <option value="ha">🇳🇬 Hausa</option> | |
| <option value="yo">🇳🇬 Yorùbá (Yoruba)</option> | |
| <option value="ig">🇳🇬 Igbo</option> | |
| <option value="so">🇸🇴 Soomaali (Somali)</option> | |
| <option value="rw">🇷🇼 Kinyarwanda</option> | |
| <option value="ny">🇲🇼 Chichewa</option> | |
| <option value="mg">🇲🇬 Malagasy</option> | |
| <option value="sn">🇿🇼 Shona</option> | |
| <option value="st">🇱🇸 Sesotho</option> | |
| <option value="tn">🇧🇼 Setswana</option> | |
| <option value="ts">🇿🇦 Xitsonga</option> | |
| <option value="ve">🇿🇦 Tshivenda</option> | |
| <option value="ss">🇸🇿 SiSwati</option> | |
| <option value="nr">🇿🇦 isiNdebele</option> | |
| <option value="fa">🇮🇷 فارسی (Persian)</option> | |
| <option value="ps">🇦🇫 پښتو (Pashto)</option> | |
| <option value="ku">🇮🇶 کوردی (Kurdish)</option> | |
| <option value="sd">🇵🇰 سنڌي (Sindhi)</option> | |
| <option value="ug">🇨🇳 ئۇيغۇرچە (Uyghur)</option> | |
| <option value="kk">🇰🇿 Қазақша (Kazakh)</option> | |
| <option value="ky">🇰🇬 Кыргызча (Kyrgyz)</option> | |
| <option value="uz">🇺🇿 O'zbekcha (Uzbek)</option> | |
| <option value="tg">🇹🇯 Тоҷикӣ (Tajik)</option> | |
| <option value="tk">🇹🇲 Türkmençe (Turkmen)</option> | |
| <option value="mn">🇲🇳 Монгол (Mongolian)</option> | |
| <option value="bo">🇨🇳 བོད་ཡིག (Tibetan)</option> | |
| <option value="dz">🇧🇹 རྫོང་ཁ (Dzongkha)</option> | |
| <option value="lo">🇱🇦 ລາວ (Lao)</option> | |
| <option value="ka">🇬🇪 ქართული (Georgian)</option> | |
| <option value="hy">🇦🇲 Հայերեն (Armenian)</option> | |
| <option value="az">🇦🇿 Azərbaycan (Azerbaijani)</option> | |
| <option value="be">🇧🇾 Беларуская (Belarusian)</option> | |
| <option value="uk">🇺🇦 Українська (Ukrainian)</option> | |
| <option value="mk">🇲🇰 Македонски (Macedonian)</option> | |
| <option value="sq">🇦🇱 Shqip (Albanian)</option> | |
| <option value="mt">🇲🇹 Malti (Maltese)</option> | |
| <option value="is">🇮🇸 Íslenska (Icelandic)</option> | |
| <option value="fo">🇫🇴 Føroyskt (Faroese)</option> | |
| <option value="ga">🇮🇪 Gaeilge (Irish)</option> | |
| <option value="gd">🏴 Gàidhlig (Scottish Gaelic)</option> | |
| <option value="cy">🏴 Cymraeg (Welsh)</option> | |
| <option value="br">🇫🇷 Brezhoneg (Breton)</option> | |
| <option value="eu">🇪🇸 Euskera (Basque)</option> | |
| <option value="ca">🇪🇸 Català (Catalan)</option> | |
| <option value="gl">🇪🇸 Galego (Galician)</option> | |
| <option value="oc">🇫🇷 Occitan</option> | |
| <option value="co">🇫🇷 Corsu (Corsican)</option> | |
| <option value="sc">🇮🇹 Sardu (Sardinian)</option> | |
| <option value="rm">🇨🇭 Rumantsch (Romansh)</option> | |
| <option value="lb">🇱🇺 Lëtzebuergesch (Luxembourgish)</option> | |
| <option value="fy">🇳🇱 Frysk (Frisian)</option> | |
| </select> | |
| </div> | |
| <div class="api-key-landing"> | |
| <label for="apiKeyLanding" data-translate="api_key_label">🔑 OpenAI API Key</label> | |
| <input type="password" id="apiKeyLanding" placeholder="Enter your OpenAI API key" data-translate-placeholder="api_key_placeholder"> | |
| </div> | |
| <button class="start-btn" id="startBtn" data-translate="start_button">🚀 Start Assessment Builder</button> | |
| <!-- Cache Management Section --> | |
| <div class="cache-management"> | |
| <h3>🗄️ Translation Cache Management</h3> | |
| <div class="cache-status" id="cacheStatusDisplay"> | |
| 💾 No cached translations found | |
| </div> | |
| <button class="clear-cache-btn" id="clearCacheBtn" onclick="clearAllTranslationCache()"> | |
| 🗑️ Clear Translation Cache | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Main Application --> | |
| <div class="main-app" id="mainApp"> | |
| <div class="container"> | |
| <!-- Language Switcher --> | |
| <div class="language-switcher"> | |
| <select id="languageSwitcher" onchange="switchLanguage()"> | |
| <option value="en">🇺🇸 English</option> | |
| </select> | |
| <button class="clear-cache-mini" onclick="clearAllTranslationCache()" title="Clear Translation Cache"> | |
| 🗑️ Clear Cache | |
| </button> | |
| </div> | |
| <!-- API Key Section - Static Position Top Left --> | |
| <div class="api-key-section"> | |
| <label for="apiKey" data-translate="api_key_short">🔑 API Key</label> | |
| <input type="password" id="apiKey" data-translate-placeholder="api_key_placeholder" autocomplete="off"> | |
| </div> | |
| <div class="main-content"> | |
| <!-- Header --> | |
| <div class="header"> | |
| <h1 data-translate="app_title">AssessmentArchitect Pro</h1> | |
| <p class="subtitle" data-translate="app_subtitle">Intelligent Assessment Strategy Platform</p> | |
| <p class="description" data-translate="app_description">Design comprehensive, effective assessment strategies through guided questions and AI-powered insights. Create assessments that measure learning objectives, accommodate diverse needs, and drive educational excellence.</p> | |
| </div> | |
| <!-- Configuration Section --> | |
| <section class="config-section"> | |
| <div class="config-item"> | |
| <label for="additionalContext" class="config-label" data-translate="additional_context_label">Additional Context (Optional)</label> | |
| <textarea | |
| id="additionalContext" | |
| class="config-textarea" | |
| data-translate-placeholder="additional_context_placeholder" | |
| ></textarea> | |
| </div> | |
| </section> | |
| <!-- Questions Section --> | |
| <section class="questions-section"> | |
| <h2 class="section-title" data-translate="questions_section_title">Assessment Strategy Questions</h2> | |
| <div class="question-item"> | |
| <label for="q1" class="question-label"> | |
| <span class="question-number">1</span> | |
| <span data-translate="question_1">What are the clear learning objectives for this assessment?</span> | |
| </label> | |
| <textarea | |
| id="q1" | |
| class="question-textarea" | |
| data-translate-placeholder="question_1_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q2" class="question-label"> | |
| <span class="question-number">2</span> | |
| <span data-translate="question_2">How does this assessment align with real-world scenarios and challenges?</span> | |
| </label> | |
| <textarea | |
| id="q2" | |
| class="question-textarea" | |
| data-translate-placeholder="question_2_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q3" class="question-label"> | |
| <span class="question-number">3</span> | |
| <span data-translate="question_3">Does the assessment measure both breadth and depth of student knowledge?</span> | |
| </label> | |
| <textarea | |
| id="q3" | |
| class="question-textarea" | |
| data-translate-placeholder="question_3_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q4" class="question-label"> | |
| <span class="question-number">4</span> | |
| <span data-translate="question_4">What mix of question types will best evaluate student understanding?</span> | |
| </label> | |
| <textarea | |
| id="q4" | |
| class="question-textarea" | |
| data-translate-placeholder="question_4_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q5" class="question-label"> | |
| <span class="question-number">5</span> | |
| <span data-translate="question_5">How will this assessment provide opportunities for student feedback and reflection?</span> | |
| </label> | |
| <textarea | |
| id="q5" | |
| class="question-textarea" | |
| data-translate-placeholder="question_5_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q6" class="question-label"> | |
| <span class="question-number">6</span> | |
| <span data-translate="question_6">Is the assessment designed to accommodate different learning needs and preferences?</span> | |
| </label> | |
| <textarea | |
| id="q6" | |
| class="question-textarea" | |
| data-translate-placeholder="question_6_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q7" class="question-label"> | |
| <span class="question-number">7</span> | |
| <span data-translate="question_7">How will the assessment results be used to inform and improve teaching practices?</span> | |
| </label> | |
| <textarea | |
| id="q7" | |
| class="question-textarea" | |
| data-translate-placeholder="question_7_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q8" class="question-label"> | |
| <span class="question-number">8</span> | |
| <span data-translate="question_8">Does the assessment strategy include both formative and summative components?</span> | |
| </label> | |
| <textarea | |
| id="q8" | |
| class="question-textarea" | |
| data-translate-placeholder="question_8_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q9" class="question-label"> | |
| <span class="question-number">9</span> | |
| <span data-translate="question_9">How will the assessment measure higher-order thinking skills and critical reasoning?</span> | |
| </label> | |
| <textarea | |
| id="q9" | |
| class="question-textarea" | |
| data-translate-placeholder="question_9_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="question-item"> | |
| <label for="q10" class="question-label"> | |
| <span class="question-number">10</span> | |
| <span data-translate="question_10">What methods will be used to ensure the assessment is fair, valid, and reliable?</span> | |
| </label> | |
| <textarea | |
| id="q10" | |
| class="question-textarea" | |
| data-translate-placeholder="question_10_placeholder" | |
| required | |
| ></textarea> | |
| </div> | |
| </section> | |
| <!-- Generate Button --> | |
| <section class="generate-section"> | |
| <button id="generateStrategyBtn" class="generate-button"> | |
| <span class="button-text" data-translate="generate_button">🎯 Generate Assessment Strategy</span> | |
| <div class="loading-spinner" id="loadingSpinner"></div> | |
| </button> | |
| <div class="progress-container"> | |
| <div class="progress-bar" id="strategyProgress"></div> | |
| </div> | |
| </section> | |
| <!-- Output Section --> | |
| <section class="output-section"> | |
| <h2 class="section-title" data-translate="output_section_title">Generated Assessment Strategy</h2> | |
| <div class="output-content" id="strategyOutput" contenteditable="true" data-translate="output_placeholder" data-placeholder="Your comprehensive assessment strategy will appear here..."></div> | |
| </section> | |
| <!-- Error and Status Messages --> | |
| <div class="error-message" id="errorMessage"></div> | |
| <div class="status-message" id="statusMessage"></div> | |
| </div> | |
| <!-- Footer --> | |
| <div class="footer"> | |
| Created by Shift Mind AI Labs | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| let currentLanguage = 'en'; | |
| let translationCache = {}; | |
| let isTranslating = false; | |
| // RTL languages list | |
| const rtlLanguages = ['ar', 'he', 'fa', 'ur', 'ps', 'ku', 'sd', 'ug']; | |
| // Language names mapping | |
| const languageNames = { | |
| 'en': 'English', 'es': 'Spanish', 'fr': 'French', 'de': 'German', 'it': 'Italian', | |
| 'pt': 'Portuguese', 'ru': 'Russian', 'ja': 'Japanese', 'ko': 'Korean', 'zh': 'Chinese', | |
| 'ar': 'Arabic', 'hi': 'Hindi', 'bn': 'Bengali', 'ur': 'Urdu', 'tr': 'Turkish', | |
| 'pl': 'Polish', 'nl': 'Dutch', 'sv': 'Swedish', 'da': 'Danish', 'no': 'Norwegian', | |
| 'fi': 'Finnish', 'cs': 'Czech', 'hu': 'Hungarian', 'ro': 'Romanian', 'bg': 'Bulgarian', | |
| 'hr': 'Croatian', 'sr': 'Serbian', 'sk': 'Slovak', 'sl': 'Slovenian', 'et': 'Estonian', | |
| 'lv': 'Latvian', 'lt': 'Lithuanian', 'el': 'Greek', 'he': 'Hebrew', 'th': 'Thai', | |
| 'vi': 'Vietnamese', 'km': 'Khmer', 'my': 'Myanmar', 'ta': 'Tamil', 'te': 'Telugu', | |
| 'kn': 'Kannada', 'ml': 'Malayalam', 'gu': 'Gujarati', 'pa': 'Punjabi', 'ne': 'Nepali', | |
| 'si': 'Sinhala', 'id': 'Indonesian', 'ms': 'Malay', 'tl': 'Filipino', 'sw': 'Swahili', | |
| 'zu': 'Zulu', 'xh': 'Xhosa', 'af': 'Afrikaans', 'am': 'Amharic', 'ha': 'Hausa', | |
| 'yo': 'Yoruba', 'ig': 'Igbo', 'so': 'Somali', 'rw': 'Kinyarwanda', 'ny': 'Chichewa', | |
| 'mg': 'Malagasy', 'sn': 'Shona', 'st': 'Sesotho', 'tn': 'Setswana', 'ts': 'Xitsonga', | |
| 've': 'Tshivenda', 'ss': 'SiSwati', 'nr': 'isiNdebele', 'fa': 'Persian', 'ps': 'Pashto', | |
| 'ku': 'Kurdish', 'sd': 'Sindhi', 'ug': 'Uyghur', 'kk': 'Kazakh', 'ky': 'Kyrgyz', | |
| 'uz': 'Uzbek', 'tg': 'Tajik', 'tk': 'Turkmen', 'mn': 'Mongolian', 'bo': 'Tibetan', | |
| 'dz': 'Dzongkha', 'lo': 'Lao', 'ka': 'Georgian', 'hy': 'Armenian', 'az': 'Azerbaijani', | |
| 'be': 'Belarusian', 'uk': 'Ukrainian', 'mk': 'Macedonian', 'sq': 'Albanian', 'mt': 'Maltese', | |
| 'is': 'Icelandic', 'fo': 'Faroese', 'ga': 'Irish', 'gd': 'Scottish Gaelic', 'cy': 'Welsh', | |
| 'br': 'Breton', 'eu': 'Basque', 'ca': 'Catalan', 'gl': 'Galician', 'oc': 'Occitan', | |
| 'co': 'Corsican', 'sc': 'Sardinian', 'rm': 'Romansh', 'lb': 'Luxembourgish', 'fy': 'Frisian' | |
| }; | |
| // Current API key | |
| let currentApiKey = ''; | |
| // App Configuration | |
| const AppConfig = { | |
| API_BASE_URL: 'https://api.openai.com/v1/chat/completions', | |
| MODEL: 'gpt-4o-mini', | |
| MAX_TOKENS: 3000, | |
| TEMPERATURE: 0.7 | |
| }; | |
| // App State Management | |
| const AppState = { | |
| apiKey: '', | |
| isGenerating: false, | |
| currentStrategy: '', | |
| setApiKey(key) { | |
| this.apiKey = key; | |
| this.saveToLocalStorage(); | |
| }, | |
| setGenerating(generating) { | |
| this.isGenerating = generating; | |
| UIController.updateGeneratingState(generating); | |
| }, | |
| saveToLocalStorage() { | |
| try { | |
| localStorage.setItem('assessmentarchitect_apiKey', this.apiKey); | |
| } catch (e) { | |
| console.warn('Could not save to localStorage:', e); | |
| } | |
| }, | |
| loadFromLocalStorage() { | |
| try { | |
| this.apiKey = localStorage.getItem('assessmentarchitect_apiKey') || ''; | |
| } catch (e) { | |
| console.warn('Could not load from localStorage:', e); | |
| } | |
| } | |
| }; | |
| // Initialize the application | |
| function initializeApp() { | |
| // Load saved language and API key | |
| const savedLanguage = localStorage.getItem('assessmentarchitect_language') || 'en'; | |
| const savedApiKey = localStorage.getItem('assessmentarchitect_api_key') || ''; | |
| currentLanguage = savedLanguage; | |
| currentApiKey = savedApiKey; | |
| // Set language selector | |
| document.getElementById('languageSelect').value = currentLanguage; | |
| document.getElementById('apiKeyLanding').value = currentApiKey; | |
| // Load translation cache | |
| const cached = localStorage.getItem('assessmentarchitect_translations'); | |
| if (cached) { | |
| translationCache = JSON.parse(cached); | |
| } | |
| // Apply direction for current language | |
| applyDirection(currentLanguage); | |
| // Update cache status | |
| updateCacheStatus(); | |
| // Show appropriate screen | |
| if (currentApiKey && currentLanguage) { | |
| showMainApp(); | |
| } else { | |
| showLanguageLanding(); | |
| } | |
| } | |
| // Update cache status display | |
| function updateCacheStatus() { | |
| const cacheStatusDisplay = document.getElementById('cacheStatusDisplay'); | |
| const cachedLanguages = Object.keys(translationCache).map(key => key.replace('assessmentarchitect_', '')); | |
| if (cachedLanguages.length > 0) { | |
| cacheStatusDisplay.classList.remove('no-cache'); | |
| cacheStatusDisplay.textContent = `💾 Cached translations for ${cachedLanguages.length} language(s): ${cachedLanguages.join(', ')}`; | |
| } else { | |
| cacheStatusDisplay.classList.add('no-cache'); | |
| cacheStatusDisplay.textContent = '💾 No cached translations found'; | |
| } | |
| } | |
| // Clear all translation cache | |
| function clearAllTranslationCache() { | |
| if (confirm('Are you sure you want to clear all cached translations? This will require re-downloading translations for all languages.')) { | |
| translationCache = {}; | |
| localStorage.removeItem('assessmentarchitect_translations'); | |
| updateCacheStatus(); | |
| // Show confirmation message | |
| const originalStatus = document.getElementById('cacheStatusDisplay').textContent; | |
| document.getElementById('cacheStatusDisplay').textContent = '🗑️ Translation cache cleared successfully!'; | |
| setTimeout(() => { | |
| updateCacheStatus(); | |
| }, 2000); | |
| // If currently using a non-English language, offer to reload | |
| if (currentLanguage !== 'en') { | |
| if (confirm('The current language translations have been cleared. Would you like to reload the translations now?')) { | |
| translateInterface(currentLanguage); | |
| } | |
| } | |
| } | |
| } | |
| // Apply language direction | |
| function applyDirection(language) { | |
| currentLanguage = language; | |
| // Set document language and direction | |
| document.documentElement.lang = language; | |
| document.documentElement.dir = rtlLanguages.includes(language) ? 'rtl' : 'ltr'; | |
| // Save language preference | |
| localStorage.setItem('assessmentarchitect_language', language); | |
| } | |
| // Switch language | |
| async function switchLanguage() { | |
| const newLanguage = document.getElementById('languageSwitcher').value; | |
| if (newLanguage === currentLanguage) return; | |
| currentLanguage = newLanguage; | |
| localStorage.setItem('assessmentarchitect_language', newLanguage); | |
| applyDirection(newLanguage); | |
| if (newLanguage !== 'en') { | |
| await translateInterface(newLanguage); | |
| } else { | |
| // Reset to English | |
| restoreOriginalText(); | |
| } | |
| } | |
| // Translate interface | |
| async function translateInterface(targetLanguage) { | |
| if (isTranslating) return; | |
| const cacheKey = `assessmentarchitect_${targetLanguage}`; | |
| // Check if translations are cached | |
| if (translationCache[cacheKey]) { | |
| showTranslationStatus('💾 Translations cached - instant loading!', false); | |
| applyTranslations(translationCache[cacheKey]); | |
| return; | |
| } | |
| // Show first-time translation message | |
| showTranslationStatus('🔄 First time translation - will be cached for future use', true); | |
| isTranslating = true; | |
| try { | |
| const apiKey = document.getElementById('apiKey').value || document.getElementById('apiKeyLanding').value; | |
| if (!apiKey) { | |
| throw new Error('API key required for translation'); | |
| } | |
| // Get all translatable elements | |
| const elements = document.querySelectorAll('[data-translate]'); | |
| const textsToTranslate = []; | |
| const elementMap = {}; | |
| elements.forEach((element, index) => { | |
| const key = element.getAttribute('data-translate'); | |
| let text = ''; | |
| if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { | |
| text = element.placeholder; | |
| } else { | |
| text = element.textContent.trim(); | |
| } | |
| if (text && !elementMap[key]) { | |
| elementMap[key] = text; | |
| textsToTranslate.push({ key, text }); | |
| } | |
| }); | |
| // Translate in batches | |
| const batchSize = 10; | |
| const translations = {}; | |
| for (let i = 0; i < textsToTranslate.length; i += batchSize) { | |
| const batch = textsToTranslate.slice(i, i + batchSize); | |
| const progress = Math.round(((i + batch.length) / textsToTranslate.length) * 100); | |
| updateTranslationProgress(progress); | |
| const batchTexts = batch.map(item => item.text).join('\n---\n'); | |
| const prompt = `Translate the following text to ${languageNames[targetLanguage]}. Provide ONLY the exact translation without any explanations, additional information, or formatting. Separate each translation with ---:\n\n${batchTexts}`; | |
| const response = await fetch('https://api.openai.com/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${apiKey}` | |
| }, | |
| body: JSON.stringify({ | |
| model: 'gpt-4o-mini', | |
| messages: [{ role: 'user', content: prompt }], | |
| max_tokens: 2000, | |
| temperature: 0.3 | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Translation API request failed'); | |
| } | |
| const data = await response.json(); | |
| const translatedTexts = data.choices[0].message.content.split('---').map(t => t.trim()); | |
| batch.forEach((item, index) => { | |
| if (translatedTexts[index]) { | |
| translations[item.key] = translatedTexts[index]; | |
| } | |
| }); | |
| } | |
| // Cache translations | |
| translationCache[cacheKey] = translations; | |
| localStorage.setItem('assessmentarchitect_translations', JSON.stringify(translationCache)); | |
| // Apply translations | |
| applyTranslations(translations); | |
| updateTranslationProgress(100); | |
| setTimeout(() => { | |
| hideTranslationOverlay(); | |
| showTranslationStatus('💾 Translations cached - instant loading!', false); | |
| updateCacheStatus(); | |
| }, 500); | |
| } catch (error) { | |
| console.error('Translation error:', error); | |
| hideTranslationOverlay(); | |
| alert('Translation failed. Please check your API key and try again.'); | |
| } finally { | |
| isTranslating = false; | |
| } | |
| } | |
| // Apply translations to elements | |
| function applyTranslations(translations) { | |
| const elements = document.querySelectorAll('[data-translate]'); | |
| elements.forEach(element => { | |
| const key = element.getAttribute('data-translate'); | |
| if (translations[key]) { | |
| if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { | |
| element.placeholder = translations[key]; | |
| } else { | |
| element.textContent = translations[key]; | |
| } | |
| // Update data-placeholder for output area | |
| if (element.hasAttribute('data-placeholder')) { | |
| element.setAttribute('data-placeholder', translations[key]); | |
| } | |
| } | |
| }); | |
| } | |
| // Restore original English text | |
| function restoreOriginalText() { | |
| const originalTexts = { | |
| 'app_title': 'AssessmentArchitect Pro', | |
| 'welcome_message': 'Welcome! Please select your preferred language and enter your API key to get started with intelligent assessment strategy building.', | |
| 'select_language': '🌐 Select Language', | |
| 'api_key_label': '🔑 OpenAI API Key', | |
| 'api_key_placeholder': 'Enter your OpenAI API key', | |
| 'start_button': '🚀 Start Assessment Builder', | |
| 'api_key_short': '🔑 API Key', | |
| 'app_subtitle': 'Intelligent Assessment Strategy Platform', | |
| 'app_description': 'Design comprehensive, effective assessment strategies through guided questions and AI-powered insights. Create assessments that measure learning objectives, accommodate diverse needs, and drive educational excellence.', | |
| 'additional_context_label': 'Additional Context (Optional)', | |
| 'additional_context_placeholder': 'Provide any additional context or specific requirements for your assessment strategy...', | |
| 'questions_section_title': 'Assessment Strategy Questions', | |
| 'question_1': 'What are the clear learning objectives for this assessment?', | |
| 'question_1_placeholder': 'Describe the specific learning objectives and outcomes this assessment should measure...', | |
| 'question_2': 'How does this assessment align with real-world scenarios and challenges?', | |
| 'question_2_placeholder': 'Explain how this assessment connects to practical applications and real-world contexts...', | |
| 'question_3': 'Does the assessment measure both breadth and depth of student knowledge?', | |
| 'question_3_placeholder': 'Describe how the assessment will evaluate comprehensive understanding and detailed knowledge...', | |
| 'question_4': 'What mix of question types will best evaluate student understanding?', | |
| 'question_4_placeholder': 'Outline the variety of question formats and assessment methods you plan to use...', | |
| 'question_5': 'How will this assessment provide opportunities for student feedback and reflection?', | |
| 'question_5_placeholder': 'Describe mechanisms for student self-assessment and reflective learning...', | |
| 'question_6': 'Is the assessment designed to accommodate different learning needs and preferences?', | |
| 'question_6_placeholder': 'Explain how the assessment will be inclusive and accessible to diverse learners...', | |
| 'question_7': 'How will the assessment results be used to inform and improve teaching practices?', | |
| 'question_7_placeholder': 'Describe how assessment data will guide instructional improvements and curriculum development...', | |
| 'question_8': 'Does the assessment strategy include both formative and summative components?', | |
| 'question_8_placeholder': 'Outline the balance between ongoing assessment and final evaluation methods...', | |
| 'question_9': 'How will the assessment measure higher-order thinking skills and critical reasoning?', | |
| 'question_9_placeholder': 'Describe how the assessment will evaluate analysis, synthesis, evaluation, and creative thinking...', | |
| 'question_10': 'What methods will be used to ensure the assessment is fair, valid, and reliable?', | |
| 'question_10_placeholder': 'Explain quality assurance measures and validation approaches for the assessment...', | |
| 'generate_button': '🎯 Generate Assessment Strategy', | |
| 'output_section_title': 'Generated Assessment Strategy', | |
| 'output_placeholder': 'Your comprehensive assessment strategy will appear here...' | |
| }; | |
| applyTranslations(originalTexts); | |
| } | |
| // Show translation status | |
| function showTranslationStatus(message, showProgress) { | |
| const overlay = document.getElementById('translationOverlay'); | |
| const messageEl = document.getElementById('translationMessage'); | |
| const progressContainer = overlay.querySelector('.translation-progress'); | |
| messageEl.textContent = message; | |
| progressContainer.style.display = showProgress ? 'block' : 'none'; | |
| overlay.style.display = 'flex'; | |
| if (!showProgress) { | |
| setTimeout(hideTranslationOverlay, 1500); | |
| } | |
| } | |
| // Update translation progress | |
| function updateTranslationProgress(percentage) { | |
| const progressBar = document.getElementById('translationProgressBar'); | |
| const progressText = document.getElementById('translationProgress'); | |
| progressBar.style.width = percentage + '%'; | |
| progressText.textContent = percentage + '%'; | |
| } | |
| // Hide translation overlay | |
| function hideTranslationOverlay() { | |
| document.getElementById('translationOverlay').style.display = 'none'; | |
| } | |
| // Show language landing page | |
| function showLanguageLanding() { | |
| document.getElementById('languageLanding').style.display = 'block'; | |
| document.getElementById('mainApp').style.display = 'none'; | |
| } | |
| // Show main application | |
| function showMainApp() { | |
| document.getElementById('languageLanding').style.display = 'none'; | |
| document.getElementById('mainApp').style.display = 'block'; | |
| // Set API key in main app | |
| document.getElementById('apiKey').value = currentApiKey; | |
| // Update language switcher | |
| const switcher = document.getElementById('languageSwitcher'); | |
| switcher.innerHTML = ''; | |
| const allOptions = document.getElementById('languageSelect').innerHTML; | |
| switcher.innerHTML = allOptions; | |
| switcher.value = currentLanguage; | |
| } | |
| // Show error message | |
| function showError(message) { | |
| const errorDiv = document.getElementById('errorMessage'); | |
| errorDiv.textContent = message; | |
| errorDiv.style.display = 'block'; | |
| setTimeout(() => { | |
| errorDiv.style.display = 'none'; | |
| }, 5000); | |
| } | |
| // API Service Module | |
| const APIService = { | |
| async callAPI(prompt) { | |
| const languageName = languageNames[currentLanguage] || 'English'; | |
| // Modify prompt to include language instruction | |
| const languagePrompt = currentLanguage !== 'en' | |
| ? `\n\nIMPORTANT: Generate all content in ${languageName} language.` | |
| : ''; | |
| const payload = { | |
| model: AppConfig.MODEL, | |
| messages: [{ role: 'user', content: prompt + languagePrompt }], | |
| max_tokens: AppConfig.MAX_TOKENS, | |
| temperature: AppConfig.TEMPERATURE | |
| }; | |
| const response = await fetch(AppConfig.API_BASE_URL, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${currentApiKey}` | |
| }, | |
| body: JSON.stringify(payload) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| return data.choices[0].message.content; | |
| } | |
| }; | |
| // UI Controller Module | |
| const UIController = { | |
| elements: {}, | |
| init() { | |
| this.elements = { | |
| apiKeyInput: document.getElementById('apiKey'), | |
| additionalContext: document.getElementById('additionalContext'), | |
| generateButton: document.getElementById('generateStrategyBtn'), | |
| loadingSpinner: document.getElementById('loadingSpinner'), | |
| buttonText: document.querySelector('.button-text'), | |
| progressBar: document.getElementById('strategyProgress'), | |
| strategyOutput: document.getElementById('strategyOutput'), | |
| questionInputs: {} | |
| }; | |
| // Get all question inputs | |
| for (let i = 1; i <= 10; i++) { | |
| this.elements.questionInputs[`q${i}`] = document.getElementById(`q${i}`); | |
| } | |
| this.setupEventListeners(); | |
| }, | |
| setupEventListeners() { | |
| this.elements.apiKeyInput.addEventListener('input', (e) => { | |
| AppState.setApiKey(e.target.value.trim()); | |
| }); | |
| this.elements.generateButton.addEventListener('click', () => { | |
| App.generateStrategy(); | |
| }); | |
| }, | |
| updateGeneratingState(isGenerating) { | |
| if (isGenerating) { | |
| this.elements.generateButton.disabled = true; | |
| this.elements.buttonText.style.opacity = '0'; | |
| this.elements.loadingSpinner.style.display = 'block'; | |
| this.elements.progressBar.style.width = '20%'; | |
| } else { | |
| this.elements.generateButton.disabled = false; | |
| this.elements.buttonText.style.opacity = '1'; | |
| this.elements.loadingSpinner.style.display = 'none'; | |
| this.elements.progressBar.style.width = '100%'; | |
| } | |
| }, | |
| setStrategyOutput(content) { | |
| this.elements.strategyOutput.textContent = content; | |
| }, | |
| showError(message) { | |
| showError(message); | |
| this.setStrategyOutput(`Error: ${message}`); | |
| } | |
| }; | |
| // Error Handler Module | |
| const ErrorHandler = { | |
| handleError(error) { | |
| console.error('Application error:', error); | |
| let userMessage = 'An unexpected error occurred. Please try again.'; | |
| if (error.message.includes('API key')) { | |
| userMessage = 'Invalid API key. Please check your OpenAI API key and try again.'; | |
| } else if (error.message.includes('network') || error.message.includes('fetch')) { | |
| userMessage = 'Network error. Please check your internet connection and try again.'; | |
| } else if (error.message.includes('rate limit')) { | |
| userMessage = 'Rate limit exceeded. Please wait a moment before trying again.'; | |
| } else if (error.message.includes('quota')) { | |
| userMessage = 'API quota exceeded. Please check your OpenAI account usage.'; | |
| } | |
| UIController.showError(userMessage); | |
| } | |
| }; | |
| // Input Validator Module | |
| const InputValidator = { | |
| validateInputs() { | |
| const apiKey = UIController.elements.apiKeyInput.value.trim(); | |
| if (!apiKey) { | |
| throw new Error('Please enter your API key.'); | |
| } | |
| // Check if all required questions are answered | |
| for (let i = 1; i <= 10; i++) { | |
| const answer = UIController.elements.questionInputs[`q${i}`].value.trim(); | |
| if (!answer) { | |
| throw new Error(`Please answer question ${i}.`); | |
| } | |
| } | |
| return true; | |
| } | |
| }; | |
| // Prompt Builder Module | |
| const PromptBuilder = { | |
| buildFinalStrategyPrompt() { | |
| const language = languageNames[currentLanguage] || 'English'; | |
| const additionalContext = UIController.elements.additionalContext.value.trim(); | |
| const responses = { | |
| "Learning Objectives": UIController.elements.questionInputs.q1.value.trim(), | |
| "Real-World Alignment": UIController.elements.questionInputs.q2.value.trim(), | |
| "Breadth and Depth": UIController.elements.questionInputs.q3.value.trim(), | |
| "Question Type Mix": UIController.elements.questionInputs.q4.value.trim(), | |
| "Feedback & Reflection": UIController.elements.questionInputs.q5.value.trim(), | |
| "Accommodations": UIController.elements.questionInputs.q6.value.trim(), | |
| "Teaching Improvement": UIController.elements.questionInputs.q7.value.trim(), | |
| "Formative and Summative": UIController.elements.questionInputs.q8.value.trim(), | |
| "Higher-Order Skills": UIController.elements.questionInputs.q9.value.trim(), | |
| "Fairness & Reliability": UIController.elements.questionInputs.q10.value.trim() | |
| }; | |
| let responsesText = ""; | |
| let i = 1; | |
| for (const [question, answer] of Object.entries(responses)) { | |
| responsesText += `${i}. ${question}: ${answer}\n`; | |
| i++; | |
| } | |
| return ` | |
| Develop an effective assessment strategy that leads to the best outcomes based on the user responses below. Consider the following key questions and their answers: ${responsesText} ${ additionalContext ? "\nAdditional Context: " + additionalContext : "" } Produce a comprehensive, professional, and actionable assessment strategy that: | |
| - Clearly defines learning objectives. | |
| - Aligns with real-world scenarios and challenges. | |
| - Measures both breadth and depth of student knowledge. | |
| - Utilizes an appropriate mix of question types. | |
| - Provides opportunities for student feedback and reflection. | |
| - Accommodates different learning needs and preferences. | |
| - Uses assessment results to improve teaching practices. | |
| - Balances formative and summative assessments. | |
| - Measures higher-order thinking and critical reasoning. | |
| - Ensures fairness, validity, and reliability. | |
| Output the final strategy in ${language} as a detailed written plan. | |
| 1. **Opening**: Acknowledge the request and provide an overview of your approach. | |
| 2. **Main Content**: Address each component of the instruction systematically: | |
| - Use clear section headers for multi-part requests. | |
| - Provide specific examples where helpful. | |
| - Include relevant context or background information. | |
| - Address potential misconceptions or common confusion points. | |
| 3. **Supporting Details**: Add relevant supporting information, sources, or methodology explanations. | |
| 4. **Conclusion**: Summarize key points and offer additional resources or next steps if applicable. | |
| Ensure that all aspects of the original instruction are addressed, the response is appropriately detailed for the intended audience, the information is accurate and up-to-date, the format matches any specified requirements, and the language is clear, professional, and accessible. Complete this instruction with precision, ensuring no element is overlooked and the response fully satisfies the stated requirements. | |
| `; | |
| } | |
| }; | |
| // Main App Module | |
| const App = { | |
| init() { | |
| AppState.loadFromLocalStorage(); | |
| UIController.init(); | |
| // Load saved API key | |
| if (AppState.apiKey) { | |
| UIController.elements.apiKeyInput.value = AppState.apiKey; | |
| } | |
| console.log('AssessmentArchitect Pro initialized'); | |
| }, | |
| async generateStrategy() { | |
| if (AppState.isGenerating) return; | |
| try { | |
| // Validate inputs | |
| InputValidator.validateInputs(); | |
| // Update state | |
| AppState.setGenerating(true); | |
| // Update API key in state | |
| const apiKey = UIController.elements.apiKeyInput.value.trim(); | |
| AppState.setApiKey(apiKey); | |
| // Clear previous output | |
| UIController.setStrategyOutput('Generating assessment strategy...'); | |
| // Build prompt and call API | |
| const prompt = PromptBuilder.buildFinalStrategyPrompt(); | |
| const result = await APIService.callAPI(prompt); | |
| // Update output | |
| UIController.setStrategyOutput(result); | |
| AppState.currentStrategy = result; | |
| } catch (error) { | |
| ErrorHandler.handleError(error); | |
| } finally { | |
| AppState.setGenerating(false); | |
| } | |
| } | |
| }; | |
| // Start button click handler | |
| document.getElementById('startBtn').addEventListener('click', async function() { | |
| const selectedLanguage = document.getElementById('languageSelect').value; | |
| const apiKey = document.getElementById('apiKeyLanding').value.trim(); | |
| if (!apiKey) { | |
| alert('Please enter your OpenAI API key'); | |
| return; | |
| } | |
| currentLanguage = selectedLanguage; | |
| currentApiKey = apiKey; | |
| // Save preferences | |
| localStorage.setItem('assessmentarchitect_language', selectedLanguage); | |
| localStorage.setItem('assessmentarchitect_api_key', apiKey); | |
| // Apply direction | |
| applyDirection(selectedLanguage); | |
| if (selectedLanguage !== 'en') { | |
| await translateInterface(selectedLanguage); | |
| } | |
| // Show main app | |
| showMainApp(); | |
| }); | |
| // Language selector change handler | |
| document.getElementById('languageSelect').addEventListener('change', async function() { | |
| const selectedLanguage = this.value; | |
| applyDirection(selectedLanguage); | |
| }); | |
| // API key sync between landing and main app | |
| document.getElementById('apiKeyLanding').addEventListener('input', function() { | |
| currentApiKey = this.value; | |
| localStorage.setItem('assessmentarchitect_api_key', this.value); | |
| document.getElementById('apiKey').value = this.value; | |
| }); | |
| document.getElementById('apiKey').addEventListener('input', function() { | |
| currentApiKey = this.value; | |
| localStorage.setItem('assessmentarchitect_api_key', this.value); | |
| document.getElementById('apiKeyLanding').value = this.value; | |
| }); | |
| // Initialize the application when page loads | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeApp(); | |
| App.init(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |