Spaces:
Running
Running
| <html lang="de"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Lernplan - Dein persönlicher Lernassistent</title> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| --secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); | |
| --success-gradient: linear-gradient(135deg, #13f1fc 0%, #0470dc 100%); | |
| --warning-gradient: linear-gradient(135deg, #f9d423 0%, #ff4e50 100%); | |
| --cloud-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); | |
| --bg-pattern: radial-gradient(circle at 20% 80%, rgba(102, 126, 234, 0.1) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(118, 75, 162, 0.1) 0%, transparent 50%), | |
| radial-gradient(circle at 40% 40%, rgba(240, 147, 251, 0.05) 0%, transparent 50%); | |
| --glass-bg: rgba(255, 255, 255, 0.95); | |
| --glass-border: rgba(255, 255, 255, 0.2); | |
| --shadow-glow: 0 0 40px rgba(102, 126, 234, 0.3); | |
| --text-primary: #2d3748; | |
| --text-secondary: #718096; | |
| --border-light: #e2e8f0; | |
| --spacing-xs: 0.5rem; | |
| --spacing-sm: 1rem; | |
| --spacing-md: 1.5rem; | |
| --spacing-lg: 2rem; | |
| --spacing-xl: 3rem; | |
| --radius-sm: 0.5rem; | |
| --radius-md: 1rem; | |
| --radius-lg: 1.5rem; | |
| --transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| --transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| color: var(--text-primary); | |
| line-height: 1.6; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: var(--bg-pattern); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| .cloud-bg { | |
| position: fixed; | |
| width: 100%; | |
| height: 100%; | |
| top: 0; | |
| left: 0; | |
| z-index: -1; | |
| opacity: 0.3; | |
| background-image: | |
| radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.3) 0%, transparent 50%), | |
| radial-gradient(circle at 70% 60%, rgba(255, 255, 255, 0.2) 0%, transparent 40%), | |
| radial-gradient(circle at 40% 80%, rgba(255, 255, 255, 0.25) 0%, transparent 45%); | |
| animation: floatClouds 20s ease-in-out infinite; | |
| } | |
| @keyframes floatClouds { | |
| 0%, | |
| 100% { | |
| transform: translateY(0) translateX(0); | |
| } | |
| 33% { | |
| transform: translateY(-20px) translateX(10px); | |
| } | |
| 66% { | |
| transform: translateY(10px) translateX(-10px); | |
| } | |
| } | |
| .animated-bg { | |
| position: fixed; | |
| width: 200%; | |
| height: 200%; | |
| top: -50%; | |
| left: -50%; | |
| z-index: -1; | |
| animation: rotateBG 30s linear infinite; | |
| background: radial-gradient(circle at 20% 50%, rgba(102, 126, 234, 0.2) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 80%, rgba(118, 75, 162, 0.2) 0%, transparent 50%), | |
| radial-gradient(circle at 40% 20%, rgba(240, 147, 251, 0.1) 0%, transparent 50%); | |
| } | |
| @keyframes rotateBG { | |
| 0% { | |
| transform: rotate(0deg); | |
| } | |
| 100% { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: var(--spacing-lg); | |
| position: relative; | |
| z-index: 1; | |
| } | |
| header { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: var(--radius-lg); | |
| padding: var(--spacing-xl); | |
| margin-bottom: var(--spacing-xl); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| animation: slideDown 0.8s cubic-bezier(0.34, 1.56, 0.64, 1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| header::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 4px; | |
| background: var(--primary-gradient); | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| h1 { | |
| font-size: 3rem; | |
| font-weight: 800; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: var(--spacing-md); | |
| display: flex; | |
| align-items: center; | |
| gap: var(--spacing-md); | |
| animation: glow 3s ease-in-out infinite; | |
| } | |
| @keyframes glow { | |
| 0%, | |
| 100% { | |
| filter: brightness(1); | |
| } | |
| 50% { | |
| filter: brightness(1.2); | |
| } | |
| } | |
| .header-icon { | |
| width: 60px; | |
| height: 60px; | |
| background: var(--primary-gradient); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-size: 28px; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, | |
| 100% { | |
| transform: scale(1); | |
| } | |
| 50% { | |
| transform: scale(1.05); | |
| } | |
| } | |
| .description { | |
| color: var(--text-secondary); | |
| font-size: 1.25rem; | |
| margin-bottom: var(--spacing-md); | |
| line-height: 1.8; | |
| } | |
| .personal-info { | |
| background: linear-gradient(135deg, rgba(79, 172, 254, 0.1), rgba(0, 242, 254, 0.1)); | |
| border-left: 4px solid #4facfe; | |
| padding: var(--spacing-md); | |
| border-radius: var(--radius-md); | |
| margin: var(--spacing-lg) 0; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .personal-info::before { | |
| content: '☁️'; | |
| position: absolute; | |
| right: 20px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| font-size: 2rem; | |
| opacity: 0.3; | |
| animation: cloudFloat 3s ease-in-out infinite; | |
| } | |
| @keyframes cloudFloat { | |
| 0%, | |
| 100% { | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| 50% { | |
| transform: translateY(-50%) translateX(10px); | |
| } | |
| } | |
| .personal-info h3 { | |
| color: #4facfe; | |
| font-size: 1.5rem; | |
| margin-bottom: var(--spacing-sm); | |
| } | |
| .personal-info .name { | |
| font-size: 1.8rem; | |
| font-weight: 800; | |
| background: var(--cloud-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: var(--spacing-xs); | |
| margin-top: var(--spacing-sm); | |
| display: inline-block; | |
| position: relative; | |
| } | |
| .personal-info .name::after { | |
| content: '✨'; | |
| position: absolute; | |
| right: -30px; | |
| top: 0; | |
| animation: sparkle 2s ease-in-out infinite; | |
| } | |
| @keyframes sparkle { | |
| 0%, | |
| 100% { | |
| opacity: 0; | |
| transform: scale(0); | |
| } | |
| 50% { | |
| opacity: 1; | |
| transform: scale(1); | |
| } | |
| } | |
| .attribution { | |
| font-size: 0.95rem; | |
| color: var(--text-secondary); | |
| padding-top: var(--spacing-md); | |
| border-top: 1px solid var(--border-light); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .attribution a { | |
| color: #667eea; | |
| text-decoration: none; | |
| font-weight: 600; | |
| transition: var(--transition-base); | |
| position: relative; | |
| } | |
| .attribution a::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -2px; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background: #667eea; | |
| transform: scaleX(0); | |
| transition: transform 0.3s ease; | |
| } | |
| .attribution a:hover::after { | |
| transform: scaleX(1); | |
| } | |
| .main-content { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--spacing-xl); | |
| animation: fadeInUp 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s both; | |
| } | |
| @keyframes fadeInUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .card { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: var(--radius-lg); | |
| padding: var(--spacing-xl); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| transition: var(--transition-base); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 3px; | |
| background: var(--primary-gradient); | |
| transform: scaleX(0); | |
| transition: transform 0.3s ease; | |
| } | |
| .card:hover::before { | |
| transform: scaleX(1); | |
| } | |
| .card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 30px 60px rgba(0, 0, 0, 0.15); | |
| } | |
| .card-title { | |
| font-size: 1.75rem; | |
| font-weight: 700; | |
| margin-bottom: var(--spacing-lg); | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: var(--spacing-sm); | |
| } | |
| .card-title i { | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| font-size: 1.5rem; | |
| } | |
| .form-group { | |
| margin-bottom: var(--spacing-md); | |
| position: relative; | |
| } | |
| label { | |
| display: block; | |
| font-weight: 600; | |
| margin-bottom: var(--spacing-xs); | |
| color: var(--text-primary); | |
| font-size: 0.95rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| input[type="text"], | |
| input[type="number"], | |
| input[type="date"] { | |
| width: 100%; | |
| padding: 14px 18px; | |
| border: 2px solid var(--border-light); | |
| border-radius: var(--radius-md); | |
| font-size: 1rem; | |
| transition: var(--transition-base); | |
| background: rgba(255, 255, 255, 0.8); | |
| backdrop-filter: blur(10px); | |
| } | |
| input[type="text"]:focus, | |
| input[type="number"]:focus, | |
| input[type="date"]:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| background: white; | |
| box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1); | |
| transform: translateY(-2px); | |
| } | |
| .checkbox-group { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--spacing-sm); | |
| padding: 18px; | |
| background: rgba(102, 126, 234, 0.05); | |
| border: 2px solid var(--border-light); | |
| border-radius: var(--radius-md); | |
| cursor: pointer; | |
| transition: var(--transition-base); | |
| } | |
| .checkbox-group:hover { | |
| background: rgba(102, 126, 234, 0.1); | |
| border-color: #667eea; | |
| transform: translateX(5px); | |
| } | |
| input[type="checkbox"] { | |
| width: 24px; | |
| height: 24px; | |
| cursor: pointer; | |
| accent-color: #667eea; | |
| } | |
| .checkbox-label { | |
| cursor: pointer; | |
| user-select: none; | |
| font-weight: 500; | |
| } | |
| .help-text { | |
| font-size: 0.875rem; | |
| color: var(--text-secondary); | |
| margin-top: var(--spacing-xs); | |
| font-style: italic; | |
| } | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: var(--spacing-sm); | |
| padding: 14px 32px; | |
| font-size: 1rem; | |
| font-weight: 700; | |
| border: none; | |
| border-radius: var(--radius-md); | |
| cursor: pointer; | |
| transition: var(--transition-base); | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.3); | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| .btn:hover::before { | |
| width: 300px; | |
| height: 300px; | |
| } | |
| .btn-primary { | |
| background: var(--primary-gradient); | |
| color: white; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4); | |
| } | |
| .btn-primary:hover:not(:disabled) { | |
| transform: translateY(-3px); | |
| box-shadow: 0 15px 40px rgba(102, 126, 234, 0.5); | |
| } | |
| .btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| } | |
| .spinner { | |
| width: 20px; | |
| height: 20px; | |
| border: 3px solid rgba(255, 255, 255, 0.3); | |
| border-top-color: white; | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .kpi-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: var(--spacing-md); | |
| margin-bottom: var(--spacing-xl); | |
| } | |
| .kpi-card { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.7)); | |
| padding: var(--spacing-lg); | |
| border-radius: var(--radius-md); | |
| border: 1px solid var(--border-light); | |
| transition: var(--transition-base); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .kpi-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| right: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: radial-gradient(circle, rgba(102, 126, 234, 0.1) 0%, transparent 70%); | |
| animation: rotate 10s linear infinite; | |
| } | |
| @keyframes rotate { | |
| 0% { | |
| transform: rotate(0deg); | |
| } | |
| 100% { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .kpi-card:hover { | |
| transform: translateY(-5px) scale(1.02); | |
| box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1); | |
| } | |
| .kpi-label { | |
| font-size: 0.875rem; | |
| color: var(--text-secondary); | |
| margin-bottom: var(--spacing-xs); | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| font-weight: 600; | |
| } | |
| .kpi-value { | |
| font-size: 2.5rem; | |
| font-weight: 800; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .table-container { | |
| overflow-x: auto; | |
| border-radius: var(--radius-md); | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); | |
| } | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| background: white; | |
| } | |
| thead { | |
| background: var(--primary-gradient); | |
| color: white; | |
| } | |
| th { | |
| padding: 18px; | |
| text-align: left; | |
| font-weight: 700; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| font-size: 0.875rem; | |
| } | |
| td { | |
| padding: 18px; | |
| border-bottom: 1px solid var(--border-light); | |
| } | |
| tbody tr { | |
| transition: var(--transition-fast); | |
| } | |
| tbody tr:hover { | |
| background: rgba(102, 126, 234, 0.05); | |
| transform: scale(1.01); | |
| } | |
| tbody tr:last-child td { | |
| border-bottom: none; | |
| } | |
| .empty-state { | |
| text-align: center; | |
| padding: 80px 20px; | |
| color: var(--text-secondary); | |
| } | |
| .empty-icon { | |
| font-size: 4rem; | |
| margin-bottom: var(--spacing-lg); | |
| opacity: 0.5; | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%, | |
| 100% { | |
| transform: translateY(0); | |
| } | |
| 50% { | |
| transform: translateY(-10px); | |
| } | |
| } | |
| .toast { | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| padding: 16px 24px; | |
| border-radius: var(--radius-md); | |
| color: white; | |
| font-weight: 600; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); | |
| display: flex; | |
| align-items: center; | |
| gap: var(--spacing-sm); | |
| z-index: 1000; | |
| animation: slideIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); | |
| backdrop-filter: blur(10px); | |
| } | |
| @keyframes slideIn { | |
| from { | |
| opacity: 0; | |
| transform: translateX(100%); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| } | |
| .toast.success { | |
| background: var(--success-gradient); | |
| } | |
| .toast.error { | |
| background: var(--secondary-gradient); | |
| } | |
| .hidden { | |
| display: none ; | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| } | |
| to { | |
| opacity: 1; | |
| } | |
| } | |
| #calendar { | |
| background: white; | |
| border-radius: var(--radius-md); | |
| padding: var(--spacing-lg); | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); | |
| } | |
| #calendarHeader { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: var(--spacing-md); | |
| padding: 0 var(--spacing-sm); | |
| } | |
| #calendarHeader button { | |
| background: var(--primary-gradient); | |
| color: white; | |
| border: none; | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: var(--transition-base); | |
| font-size: 1.2rem; | |
| } | |
| #calendarHeader button:hover { | |
| transform: scale(1.1); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); | |
| } | |
| #calendarTitle { | |
| margin: 0; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| background: var(--primary-gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .calendar-day { | |
| border: 1px solid var(--border-light); | |
| border-radius: var(--radius-sm); | |
| padding: 8px; | |
| min-height: 90px; | |
| transition: var(--transition-fast); | |
| background: white; | |
| } | |
| .calendar-day:hover { | |
| background: rgba(102, 126, 234, 0.05); | |
| transform: scale(1.05); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.2); | |
| } | |
| .calendar-day-header { | |
| font-weight: 700; | |
| text-align: center; | |
| padding: 10px; | |
| background: rgba(102, 126, 234, 0.1); | |
| color: #667eea; | |
| text-transform: uppercase; | |
| font-size: 0.875rem; | |
| letter-spacing: 1px; | |
| } | |
| .calendar-today { | |
| background: rgba(102, 126, 234, 0.1) ; | |
| border: 2px solid #667eea ; | |
| } | |
| .calendar-exam { | |
| background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 193, 7, 0.2)) ; | |
| border: 2px solid gold ; | |
| box-shadow: 0 0 20px rgba(255, 215, 0, 0.3) ; | |
| } | |
| .calendar-learning { | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)) ; | |
| } | |
| #plansList { | |
| list-style: none; | |
| padding: 0; | |
| } | |
| #plansList li { | |
| background: white; | |
| padding: var(--spacing-md); | |
| border-radius: var(--radius-md); | |
| margin-bottom: var(--spacing-sm); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| transition: var(--transition-base); | |
| border: 2px solid transparent; | |
| } | |
| #plansList li:hover { | |
| border-color: #667eea; | |
| transform: translateX(5px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.2); | |
| } | |
| #plansList button { | |
| background: none; | |
| border: none; | |
| font-size: 1.2rem; | |
| cursor: pointer; | |
| transition: var(--transition-fast); | |
| padding: var(--spacing-xs); | |
| } | |
| #plansList button:hover { | |
| transform: scale(1.2); | |
| } | |
| @media (max-width: 768px) { | |
| h1 { | |
| font-size: 2rem; | |
| } | |
| .container { | |
| padding: var(--spacing-md); | |
| } | |
| .card { | |
| padding: var(--spacing-lg); | |
| } | |
| .kpi-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| table { | |
| font-size: 0.875rem; | |
| } | |
| th, | |
| td { | |
| padding: 12px; | |
| } | |
| .header-icon { | |
| width: 40px; | |
| height: 40px; | |
| font-size: 20px; | |
| } | |
| .personal-info .name { | |
| font-size: 1.4rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="cloud-bg"></div> | |
| <div class="animated-bg"></div> | |
| <div class="container"> | |
| <header> | |
| <h1> | |
| <div class="header-icon"> | |
| <i class="fas fa-cloud"></i> | |
| </div> | |
| Lernplan | |
| </h1> | |
| <div class="personal-info"> | |
| <h3>👋 Willkommen bei deinem persönlichen Lernassistenten!</h3> | |
| <div class="name">Max Mustermann</div> | |
| </div> | |
| <p class="description"> | |
| Erstelle automatisch einen personalisierten Lernplan für deine Prüfungsvorbereitung. | |
| Gib einfach dein Fachthema, das Prüfungsdatum und deine tägliche Lernzeit ein. | |
| </p> | |
| <p class="attribution"> | |
| <span>📚 Optimiert für Studenten</span> | |
| <span>Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder">anycoder</a></span> | |
| </p> | |
| </header> | |
| <main class="main-content"> | |
| <section class="card"> | |
| <h2 class="card-title"> | |
| <i class="fas fa-cloud-upload-alt"></i> Lernplandetails | |
| </h2> | |
| <form id="planForm"> | |
| <div class="form-group"> | |
| <label for="subject">Fachthema</label> | |
| <input type="text" id="subject" name="subject" required placeholder="z.B. AWS Services, Docker, Kubernetes"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="examDate">Prüfungsdatum</label> | |
| <input type="date" id="examDate" name="examDate" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="hoursPerDay">Stunden pro Tag</label> | |
| <input type="number" id="hoursPerDay" name="hoursPerDay" | |
| min="0.5" max="24" step="0.5" required placeholder="z.B. 2"> | |
| <div class="help-text">Gib an, wie viele Stunden du täglich lernen möchtest.</div> | |
| </div> | |
| <div class="form-group"> | |
| <div class="checkbox-group"> | |
| <input type="checkbox" id="skipWeekends" name="skipWeekends" checked> | |
| <label for="skipWeekends" class="checkbox-label">Wochenenden überspringen</label> | |
| </div> | |
| <div class="help-text">Keine Lerneinheiten an Samstagen und Sonntagen planen.</div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="blockedDates">Blockierte Daten</label> | |
| <input type="text" id="blockedDates" name="blockedDates" | |
| placeholder="YYYY-MM-DD,YYYY-MM-DD"> | |
| <div class="help-text"> | |
| Kommaseparierte Liste von Daten, an denen nicht gelernt werden soll. | |
| Format: YYYY-MM-DD | |
| </div> | |
| </div> | |
| <button type="submit" class="btn btn-primary" id="submitBtn"> | |
| <i class="fas fa-cloud-upload-alt"></i> | |
| <span>Lernplan erstellen</span> | |
| </button> | |
| </form> | |
| </section> | |
| <section class="card"> | |
| <h2 class="card-title"> | |
| <i class="fas fa-server"></i> Gespeicherte Lernpläne | |
| </h2> | |
| <ul id="plansList"></ul> | |
| </section> | |
| <section class="card"> | |
| <h2 class="card-title"> | |
| <i class="fas fa-calendar-alt"></i> Learning Kalender | |
| </h2> | |
| <div id="calendarHeader"> | |
| <button onclick="changeMonth(-1)"> | |
| <i class="fas fa-chevron-left"></i> | |
| </button> | |
| <h3 id="calendarTitle"></h3> | |
| <button onclick="changeMonth(1)"> | |
| <i class="fas fa-chevron-right"></i> | |
| </button> | |
| </div> | |
| <div id="calendar"></div> | |
| </section> | |
| <section id="resultsSection" class="card hidden"> | |
| <h2 class="card-title"> | |
| <i class="fas fa-chart-line"></i> Dein Lernplan | |
| </h2> | |
| <div class="kpi-grid"> | |
| <div class="kpi-card"> | |
| <div class="kpi-label">Lerntage</div> | |
| <div class="kpi-value" id="totalDays">0</div> | |
| </div> | |
| <div class="kpi-card"> | |
| <div class="kpi-label">Gesamtstunden</div> | |
| <div class="kpi-value" id="totalHours">0</div> | |
| </div> | |
| <div class="kpi-card"> | |
| <div class="kpi-label">Startdatum</div> | |
| <div class="kpi-value" id="startDate">-</div> | |
| </div> | |
| <div class="kpi-card"> | |
| <div class="kpi-label">Enddatum</div> | |
| <div class="kpi-value" id="endDate">-</div> | |
| </div> | |
| </div> | |
| <div id="tableContainer"> | |
| <div class="table-container"> | |
| <table id="planTable"> | |
| <thead> | |
| <tr> | |
| <th>Datum</th> | |
| <th>Thema</th> | |
| <th>Stunden</th> | |
| </tr> | |
| </thead> | |
| <tbody id="planTableBody"> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div id="emptyState" class="empty-state hidden"> | |
| <div class="empty-icon"> | |
| <i class="fas fa-cloud-slash"></i> | |
| </div> | |
| <h3>Keine Lernplaneinträge</h3> | |
| <p>Alle ausgewählten Daten sind blockiert. Bitte passe deine Einstellungen an.</p> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <script> | |
| // DOM Elemente | |
| const planForm = document.getElementById('planForm'); | |
| const submitBtn = document.getElementById('submitBtn'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const planTableBody = document.getElementById('planTableBody'); | |
| const tableContainer = document.getElementById('tableContainer'); | |
| const emptyState = document.getElementById('emptyState'); | |
| // KPI Elemente | |
| const totalDaysEl = document.getElementById('totalDays'); | |
| const totalHoursEl = document.getElementById('totalHours'); | |
| const startDateEl = document.getElementById('startDate'); | |
| const endDateEl = document.getElementById('endDate'); | |
| // JSON speichern | |
| async function safeJson(res) { | |
| const text = await res.text(); | |
| return text ? JSON.parse(text) : null; | |
| } | |
| // User-ID | |
| function getUserId() { | |
| let userId = localStorage.getItem("userId"); | |
| if (!userId) { | |
| userId = crypto.randomUUID(); | |
| localStorage.setItem("userId", userId); | |
| } | |
| return userId; | |
| } | |
| let currentYear = new Date().getFullYear(); | |
| let currentMonth = new Date().getMonth(); | |
| let cachedLearningDays = []; | |
| function toDateKey(date) { | |
| if (typeof date === "string") return date; | |
| return date.getFullYear() + "-" + | |
| String(date.getMonth() + 1).padStart(2, "0") + "-" + | |
| String(date.getDate()).padStart(2, "0"); | |
| } | |
| function formatDate(dateString) { | |
| const date = new Date(dateString); | |
| const options = { day: '2-digit', month: '2-digit', year: 'numeric' }; | |
| return date.toLocaleDateString('de-DE', options); | |
| } | |
| // Kalender Funktionen | |
| function renderCalendar() { | |
| const firstDay = new Date(currentYear, currentMonth, 1); | |
| const lastDay = new Date(currentYear, currentMonth + 1, 0); | |
| const prevLastDay = new Date(currentYear, currentMonth, 0); | |
| const firstDayIndex = firstDay.getDay(); | |
| const lastDayIndex = lastDay.getDay(); | |
| const nextDays = 7 - lastDayIndex - 1; | |
| document.getElementById('calendarTitle').textContent = | |
| new Date(currentYear, currentMonth).toLocaleDateString('de-DE', { month: 'long', year: 'numeric' }); | |
| let days = ''; | |
| let dayNum = 1; | |
| // Wochentage Header | |
| const weekDays = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']; | |
| weekDays.forEach(day => { | |
| days += `<div class="calendar-day-header">${day}</div>`; | |
| }); | |
| // Leere Tage am Anfang | |
| for (let x = firstDayIndex; x > 0; x--) { | |
| days += `<div class="calendar-day">${prevLastDay.getDate() - x + 1}</div>`; | |
| } | |
| // Tage des Monats | |
| for (let i = 1; i <= lastDay.getDate(); i++) { | |
| const currentDate = new Date(currentYear, currentMonth, i); | |
| const dateKey = toDateKey(currentDate); | |
| let classes = 'calendar-day'; | |
| if (currentDate.toDateString() === new Date().toDateString()) { | |
| classes += ' calendar-today'; | |
| } | |
| if (cachedLearningDays.includes(dateKey)) { | |
| classes += ' calendar-learning'; | |
| } | |
| days += `<div class="${classes}">${i}</div>`; | |
| } | |
| // Leere Tage am Ende | |
| for (let j |