diff --git a/admin-dashboard.html b/admin-dashboard.html
new file mode 100644
index 0000000..8243d18
--- /dev/null
+++ b/admin-dashboard.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+ แดชบอร์ดผู้ดูแลระบบ - กาฬสินธุ์ 2025 ✨
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
สถานะกลุ่มทั้งหมด
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/css/animations.css b/assets/css/animations.css
new file mode 100644
index 0000000..416b18c
--- /dev/null
+++ b/assets/css/animations.css
@@ -0,0 +1,359 @@
+/* 🎭 Animation Library */
+/* Smooth, elegant animations for enhanced user experience */
+
+/* === KEYFRAME ANIMATIONS === */
+
+/* Gradient Background Animation */
+@keyframes gradientShift {
+ 0%, 100% {
+ transform: translateX(0) translateY(0) rotate(0deg);
+ opacity: 1;
+ }
+ 25% {
+ transform: translateX(5%) translateY(-5%) rotate(1deg);
+ opacity: 0.8;
+ }
+ 50% {
+ transform: translateX(-3%) translateY(3%) rotate(-0.5deg);
+ opacity: 0.9;
+ }
+ 75% {
+ transform: translateX(2%) translateY(-2%) rotate(0.5deg);
+ opacity: 0.85;
+ }
+}
+
+/* Elegant Spinner */
+@keyframes elegantSpin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Pulse Animation */
+@keyframes pulse {
+ 0%, 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ 50% {
+ transform: scale(1.05);
+ opacity: 0.8;
+ }
+}
+
+/* Bounce In Animation */
+@keyframes bounceIn {
+ 0% {
+ transform: scale(0.3) translateY(-50px);
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(1.05) translateY(0);
+ opacity: 0.8;
+ }
+ 70% {
+ transform: scale(0.9);
+ opacity: 0.9;
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+/* Fade In Up */
+@keyframes fadeInUp {
+ 0% {
+ transform: translateY(30px);
+ opacity: 0;
+ }
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+/* Slide In Right */
+@keyframes slideInRight {
+ 0% {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ 100% {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+/* Scale In */
+@keyframes scaleIn {
+ 0% {
+ transform: scale(0);
+ opacity: 0;
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+/* Float Animation */
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0px);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
+}
+
+/* Progress Bar Fill */
+@keyframes progressFill {
+ 0% {
+ width: 0%;
+ }
+ 100% {
+ width: var(--progress-width, 0%);
+ }
+}
+
+/* Shimmer Effect */
+@keyframes shimmer {
+ 0% {
+ background-position: -200px 0;
+ }
+ 100% {
+ background-position: calc(200px + 100%) 0;
+ }
+}
+
+/* === ANIMATION CLASSES === */
+
+/* Loading Spinner */
+.elegant-spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid rgba(255, 255, 255, 0.3);
+ border-top: 3px solid var(--white);
+ border-radius: 50%;
+ animation: elegantSpin 1s linear infinite;
+}
+
+/* Large Spinner for Pages */
+.spinner-large {
+ width: 60px;
+ height: 60px;
+ border-width: 4px;
+}
+
+/* Bounce In Animation */
+.animate-bounce-in {
+ animation: bounceIn 0.6s ease-out;
+}
+
+/* Fade In Up */
+.animate-fade-in-up {
+ animation: fadeInUp 0.5s ease-out;
+}
+
+/* Slide In Right */
+.animate-slide-in-right {
+ animation: slideInRight 0.5s ease-out;
+}
+
+/* Scale In */
+.animate-scale-in {
+ animation: scaleIn 0.3s ease-out;
+}
+
+/* Float */
+.animate-float {
+ animation: float 3s ease-in-out infinite;
+}
+
+/* Pulse */
+.animate-pulse {
+ animation: pulse 2s ease-in-out infinite;
+}
+
+/* === HOVER EFFECTS === */
+
+/* Card Hover Effect */
+.card-hover {
+ transition: all var(--transition-normal);
+ cursor: pointer;
+}
+
+.card-hover:hover {
+ transform: translateY(-8px) scale(1.02);
+ box-shadow: var(--shadow-heavy);
+}
+
+/* 3D Card Effect */
+.card-3d {
+ perspective: 1000px;
+ transition: all var(--transition-normal);
+}
+
+.card-3d:hover {
+ transform: rotateY(5deg) rotateX(5deg) translateY(-10px);
+ box-shadow:
+ 0 20px 40px rgba(0, 0, 0, 0.15),
+ 0 10px 20px rgba(102, 126, 234, 0.1);
+}
+
+/* Button Hover Glow */
+.button-glow:hover {
+ box-shadow:
+ 0 0 20px rgba(102, 126, 234, 0.4),
+ 0 5px 15px rgba(0, 0, 0, 0.1);
+}
+
+/* === PROGRESS ANIMATIONS === */
+
+/* Animated Progress Bar */
+.progress-animated {
+ overflow: hidden;
+ position: relative;
+}
+
+.progress-animated::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ background: linear-gradient(
+ 90deg,
+ transparent 0%,
+ rgba(255, 255, 255, 0.4) 50%,
+ transparent 100%
+ );
+ width: 50px;
+ animation: shimmer 2s ease-in-out infinite;
+}
+
+/* Progress Bar Fill Animation */
+.progress-fill {
+ transition: width 0.8s ease-out;
+ animation: progressFill 1.5s ease-out;
+}
+
+/* === STATE ANIMATIONS === */
+
+/* Success State */
+.animate-success {
+ animation: bounceIn 0.6s ease-out, pulse 2s ease-in-out 0.6s;
+}
+
+/* Error State */
+.animate-error {
+ animation: pulse 0.3s ease-in-out 3;
+ border-color: #ff4757 !important;
+}
+
+/* Loading State */
+.animate-loading {
+ animation: pulse 1.5s ease-in-out infinite;
+}
+
+/* === ENTRANCE ANIMATIONS === */
+
+/* Stagger Animation for Lists */
+.stagger-children > * {
+ opacity: 0;
+ transform: translateY(20px);
+ animation: fadeInUp 0.5s ease-out forwards;
+}
+
+.stagger-children > *:nth-child(1) { animation-delay: 0.1s; }
+.stagger-children > *:nth-child(2) { animation-delay: 0.2s; }
+.stagger-children > *:nth-child(3) { animation-delay: 0.3s; }
+.stagger-children > *:nth-child(4) { animation-delay: 0.4s; }
+.stagger-children > *:nth-child(5) { animation-delay: 0.5s; }
+.stagger-children > *:nth-child(6) { animation-delay: 0.6s; }
+
+/* === UTILITY ANIMATIONS === */
+
+/* Delay Classes */
+.delay-100 { animation-delay: 0.1s; }
+.delay-200 { animation-delay: 0.2s; }
+.delay-300 { animation-delay: 0.3s; }
+.delay-500 { animation-delay: 0.5s; }
+.delay-700 { animation-delay: 0.7s; }
+.delay-1000 { animation-delay: 1s; }
+
+/* Duration Classes */
+.duration-fast { animation-duration: 0.2s; }
+.duration-normal { animation-duration: 0.5s; }
+.duration-slow { animation-duration: 1s; }
+
+/* === MICRO-INTERACTIONS === */
+
+/* Input Focus Animation */
+.input-focus-animation {
+ position: relative;
+ overflow: hidden;
+}
+
+.input-focus-animation::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: 0;
+ height: 2px;
+ background: var(--primary-gradient);
+ transition: all var(--transition-normal);
+ transform: translateX(-50%);
+}
+
+.input-focus-animation:focus-within::after {
+ width: 100%;
+}
+
+/* Button Click Animation */
+.button-click {
+ position: relative;
+ overflow: hidden;
+}
+
+.button-click::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: all var(--transition-fast);
+}
+
+.button-click:active::before {
+ width: 200px;
+ height: 200px;
+}
+
+/* === REDUCED MOTION === */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ scroll-behavior: auto !important;
+ }
+
+ .animate-pulse,
+ .animate-float,
+ .animated-background::before {
+ animation: none !important;
+ }
+}
\ No newline at end of file
diff --git a/assets/css/components.css b/assets/css/components.css
new file mode 100644
index 0000000..6a8460a
--- /dev/null
+++ b/assets/css/components.css
@@ -0,0 +1,452 @@
+/* 🧩 Reusable Components */
+/* Consistent, flexible components for the Group Mission Workflow */
+
+/* === CARD COMPONENTS === */
+
+/* Base Card */
+.card {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-xl);
+ box-shadow: var(--shadow-light);
+ border: 1px solid rgba(0, 0, 0, 0.05);
+ transition: all var(--transition-normal);
+}
+
+/* Glass Card */
+.card-glass {
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(20px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+/* Gradient Cards */
+.card-primary {
+ background: var(--primary-gradient);
+ color: var(--white);
+}
+
+.card-secondary {
+ background: var(--secondary-gradient);
+ color: var(--white);
+}
+
+.card-success {
+ background: var(--success-gradient);
+ color: var(--white);
+}
+
+.card-warning {
+ background: var(--warning-gradient);
+ color: var(--white);
+}
+
+/* Card Hover States */
+.card-interactive {
+ cursor: pointer;
+ transition: all var(--transition-normal);
+}
+
+.card-interactive:hover {
+ transform: translateY(-5px);
+ box-shadow: var(--shadow-medium);
+}
+
+/* === BUTTON COMPONENTS === */
+
+/* Base Button */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-md) var(--space-lg);
+ font-size: var(--font-size-base);
+ font-weight: 600;
+ border: none;
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: all var(--transition-normal);
+ text-decoration: none;
+ position: relative;
+ overflow: hidden;
+}
+
+/* Button Variants */
+.btn-primary {
+ background: var(--primary-gradient);
+ color: var(--white);
+}
+
+.btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
+}
+
+.btn-secondary {
+ background: var(--secondary-gradient);
+ color: var(--white);
+}
+
+.btn-secondary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(255, 106, 136, 0.3);
+}
+
+.btn-success {
+ background: var(--success-gradient);
+ color: var(--white);
+}
+
+.btn-success:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(17, 153, 142, 0.3);
+}
+
+.btn-outline {
+ background: transparent;
+ color: var(--primary-blue);
+ border: 2px solid var(--primary-blue);
+}
+
+.btn-outline:hover {
+ background: var(--primary-blue);
+ color: var(--white);
+}
+
+/* Button Sizes */
+.btn-sm {
+ padding: var(--space-sm) var(--space-md);
+ font-size: var(--font-size-sm);
+}
+
+.btn-lg {
+ padding: var(--space-lg) var(--space-xl);
+ font-size: var(--font-size-lg);
+}
+
+.btn-xl {
+ padding: var(--space-xl) var(--space-2xl);
+ font-size: var(--font-size-xl);
+}
+
+/* Full Width Button */
+.btn-full {
+ width: 100%;
+}
+
+/* === FORM COMPONENTS === */
+
+/* Input Group */
+.form-group {
+ margin-bottom: var(--space-lg);
+}
+
+.form-label {
+ display: block;
+ margin-bottom: var(--space-sm);
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.form-input {
+ width: 100%;
+ padding: var(--space-md);
+ font-size: var(--font-size-base);
+ border: 2px solid rgba(0, 0, 0, 0.1);
+ border-radius: var(--radius-md);
+ transition: all var(--transition-normal);
+ background: var(--white);
+}
+
+.form-input:focus {
+ outline: none;
+ border-color: var(--primary-blue);
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.form-textarea {
+ min-height: 120px;
+ resize: vertical;
+}
+
+.form-select {
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
+ background-position: right 0.5rem center;
+ background-repeat: no-repeat;
+ background-size: 1.5em 1.5em;
+ padding-right: 2.5rem;
+}
+
+/* === PROGRESS COMPONENTS === */
+
+/* Progress Bar */
+.progress {
+ width: 100%;
+ height: 8px;
+ background: rgba(0, 0, 0, 0.1);
+ border-radius: var(--radius-sm);
+ overflow: hidden;
+ position: relative;
+}
+
+.progress-bar {
+ height: 100%;
+ background: var(--primary-gradient);
+ transition: width 0.8s ease-out;
+ border-radius: var(--radius-sm);
+}
+
+/* Colored Progress Bars */
+.progress-bar-success {
+ background: var(--success-gradient);
+}
+
+.progress-bar-warning {
+ background: var(--warning-gradient);
+}
+
+.progress-bar-danger {
+ background: var(--secondary-gradient);
+}
+
+/* Large Progress Bar */
+.progress-lg {
+ height: 12px;
+}
+
+.progress-xl {
+ height: 16px;
+}
+
+/* === AVATAR COMPONENTS === */
+
+.avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: var(--primary-gradient);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--white);
+ font-weight: 600;
+ font-size: var(--font-size-sm);
+}
+
+.avatar-sm {
+ width: 32px;
+ height: 32px;
+ font-size: var(--font-size-xs);
+}
+
+.avatar-lg {
+ width: 56px;
+ height: 56px;
+ font-size: var(--font-size-lg);
+}
+
+.avatar-xl {
+ width: 72px;
+ height: 72px;
+ font-size: var(--font-size-xl);
+}
+
+/* Avatar Group */
+.avatar-group {
+ display: flex;
+ align-items: center;
+}
+
+.avatar-group .avatar {
+ margin-left: -0.5rem;
+ border: 2px solid var(--white);
+}
+
+.avatar-group .avatar:first-child {
+ margin-left: 0;
+}
+
+/* === BADGE COMPONENTS === */
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ padding: var(--space-xs) var(--space-sm);
+ font-size: var(--font-size-xs);
+ font-weight: 600;
+ border-radius: var(--radius-sm);
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
+}
+
+.badge-primary {
+ background: var(--primary-blue);
+ color: var(--white);
+}
+
+.badge-success {
+ background: var(--success-teal);
+ color: var(--white);
+}
+
+.badge-warning {
+ background: var(--warning-yellow);
+ color: var(--dark-gray);
+}
+
+.badge-danger {
+ background: var(--secondary-pink);
+ color: var(--white);
+}
+
+/* === ALERT COMPONENTS === */
+
+.alert {
+ padding: var(--space-lg);
+ border-radius: var(--radius-md);
+ margin-bottom: var(--space-lg);
+ border-left: 4px solid;
+}
+
+.alert-info {
+ background: rgba(102, 126, 234, 0.1);
+ border-color: var(--primary-blue);
+ color: var(--primary-purple);
+}
+
+.alert-success {
+ background: rgba(17, 153, 142, 0.1);
+ border-color: var(--success-teal);
+ color: var(--success-teal);
+}
+
+.alert-warning {
+ background: rgba(255, 210, 0, 0.1);
+ border-color: var(--warning-yellow);
+ color: var(--warning-orange);
+}
+
+.alert-danger {
+ background: rgba(255, 106, 136, 0.1);
+ border-color: var(--secondary-pink);
+ color: var(--secondary-pink);
+}
+
+/* === MODAL COMPONENTS === */
+
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(5px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 9999;
+ opacity: 0;
+ visibility: hidden;
+ transition: all var(--transition-normal);
+}
+
+.modal-overlay.active {
+ opacity: 1;
+ visibility: visible;
+}
+
+.modal {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-2xl);
+ max-width: 500px;
+ width: 90%;
+ max-height: 90vh;
+ overflow-y: auto;
+ transform: scale(0.9) translateY(20px);
+ transition: all var(--transition-normal);
+}
+
+.modal-overlay.active .modal {
+ transform: scale(1) translateY(0);
+}
+
+.modal-header {
+ margin-bottom: var(--space-lg);
+ padding-bottom: var(--space-lg);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.modal-title {
+ font-size: var(--font-size-xl);
+ font-weight: 700;
+ color: var(--dark-gray);
+ margin: 0;
+}
+
+.modal-body {
+ margin-bottom: var(--space-lg);
+}
+
+.modal-footer {
+ display: flex;
+ gap: var(--space-md);
+ justify-content: flex-end;
+}
+
+/* === UTILITY COMPONENTS === */
+
+/* Loading Spinner */
+.spinner {
+ width: 24px;
+ height: 24px;
+ border: 2px solid rgba(0, 0, 0, 0.1);
+ border-top: 2px solid var(--primary-blue);
+ border-radius: 50%;
+ animation: elegantSpin 1s linear infinite;
+}
+
+/* Divider */
+.divider {
+ height: 1px;
+ background: rgba(0, 0, 0, 0.1);
+ margin: var(--space-lg) 0;
+}
+
+/* Text Utilities */
+.text-center { text-align: center; }
+.text-left { text-align: left; }
+.text-right { text-align: right; }
+
+.text-sm { font-size: var(--font-size-sm); }
+.text-base { font-size: var(--font-size-base); }
+.text-lg { font-size: var(--font-size-lg); }
+.text-xl { font-size: var(--font-size-xl); }
+
+.font-light { font-weight: 300; }
+.font-normal { font-weight: 400; }
+.font-medium { font-weight: 500; }
+.font-semibold { font-weight: 600; }
+.font-bold { font-weight: 700; }
+
+/* Color Utilities */
+.text-primary { color: var(--primary-blue); }
+.text-success { color: var(--success-teal); }
+.text-warning { color: var(--warning-orange); }
+.text-danger { color: var(--secondary-pink); }
+.text-muted { color: var(--medium-gray); }
+
+/* Spacing Utilities */
+.m-0 { margin: 0; }
+.mb-sm { margin-bottom: var(--space-sm); }
+.mb-md { margin-bottom: var(--space-md); }
+.mb-lg { margin-bottom: var(--space-lg); }
+.mb-xl { margin-bottom: var(--space-xl); }
+
+.p-0 { padding: 0; }
+.p-sm { padding: var(--space-sm); }
+.p-md { padding: var(--space-md); }
+.p-lg { padding: var(--space-lg); }
+.p-xl { padding: var(--space-xl); }
\ No newline at end of file
diff --git a/assets/css/main.css b/assets/css/main.css
new file mode 100644
index 0000000..7debf0c
--- /dev/null
+++ b/assets/css/main.css
@@ -0,0 +1,1617 @@
+/* 🎨 Group Mission Workflow - Main Styles */
+/* Design Philosophy: "Minimal Steps, Maximum Experience - สวย เก่ง ใช้ง่าย ไม่ซับซ้อน" */
+
+/* === GLOBAL RESET & VARIABLES === */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ /* 🎨 Color Palette */
+ --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ --secondary-gradient: linear-gradient(135deg, #ff6a88 0%, #ff9a56 100%);
+ --success-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
+ --warning-gradient: linear-gradient(135deg, #ffd200 0%, #f7971e 100%);
+
+ /* Individual Colors */
+ --primary-blue: #667eea;
+ --primary-purple: #764ba2;
+ --secondary-pink: #ff6a88;
+ --secondary-orange: #ff9a56;
+ --success-teal: #11998e;
+ --success-green: #38ef7d;
+ --warning-yellow: #ffd200;
+ --warning-orange: #f7971e;
+
+ /* Neutral Colors */
+ --white: #ffffff;
+ --light-gray: #f8f9fa;
+ --medium-gray: #6c757d;
+ --dark-gray: #343a40;
+ --black: #000000;
+
+ /* Shadows */
+ --shadow-light: 0 2px 10px rgba(0, 0, 0, 0.1);
+ --shadow-medium: 0 4px 20px rgba(0, 0, 0, 0.15);
+ --shadow-heavy: 0 8px 30px rgba(0, 0, 0, 0.2);
+ --shadow-card: 0 10px 40px rgba(102, 126, 234, 0.15);
+
+ /* Typography */
+ --font-family: 'Segoe UI', 'Noto Sans Thai', Tahoma, Geneva, Verdana, sans-serif;
+ --font-size-xs: 0.75rem;
+ --font-size-sm: 0.875rem;
+ --font-size-base: 1rem;
+ --font-size-lg: 1.125rem;
+ --font-size-xl: 1.25rem;
+ --font-size-2xl: 1.5rem;
+ --font-size-3xl: 1.875rem;
+ --font-size-4xl: 2.25rem;
+
+ /* Spacing */
+ --space-xs: 0.25rem;
+ --space-sm: 0.5rem;
+ --space-md: 1rem;
+ --space-lg: 1.5rem;
+ --space-xl: 2rem;
+ --space-2xl: 3rem;
+ --space-3xl: 4rem;
+
+ /* Border Radius */
+ --radius-sm: 0.375rem;
+ --radius-md: 0.5rem;
+ --radius-lg: 0.75rem;
+ --radius-xl: 1rem;
+ --radius-2xl: 1.5rem;
+
+ /* Transitions */
+ --transition-fast: 0.15s ease-out;
+ --transition-normal: 0.3s ease-out;
+ --transition-slow: 0.5s ease-out;
+}
+
+/* === GLOBAL BASE STYLES === */
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ font-family: var(--font-family);
+ font-size: var(--font-size-base);
+ line-height: 1.6;
+ color: var(--dark-gray);
+ background: var(--light-gray);
+ overflow-x: hidden;
+}
+
+/* === ANIMATED BACKGROUND === */
+.animated-background {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: var(--primary-gradient);
+ z-index: -1;
+}
+
+.animated-background::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background:
+ radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(255, 106, 136, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(56, 239, 125, 0.2) 0%, transparent 50%);
+ animation: gradientShift 10s ease-in-out infinite;
+}
+
+/* === LOGIN PAGE SPECIFIC === */
+.login-page {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-md);
+}
+
+.login-container {
+ width: 100%;
+ max-width: 450px;
+ position: relative;
+ z-index: 1;
+}
+
+.magic-login-card {
+ background: rgba(255, 255, 255, 0.95);
+ backdrop-filter: blur(20px);
+ border-radius: var(--radius-2xl);
+ padding: var(--space-3xl) var(--space-xl);
+ box-shadow: var(--shadow-card);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ transform: translateY(0);
+ transition: all var(--transition-normal);
+}
+
+.magic-login-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 20px 60px rgba(102, 126, 234, 0.25);
+}
+
+.login-header {
+ text-align: center;
+ margin-bottom: var(--space-2xl);
+}
+
+.login-title {
+ font-size: var(--font-size-3xl);
+ font-weight: 700;
+ background: var(--primary-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ margin-bottom: var(--space-sm);
+ line-height: 1.3;
+}
+
+.login-subtitle {
+ font-size: var(--font-size-lg);
+ color: var(--medium-gray);
+ font-weight: 400;
+}
+
+.login-form {
+ margin-bottom: var(--space-xl);
+}
+
+.input-group {
+ position: relative;
+ margin-bottom: var(--space-xl);
+}
+
+.magic-input {
+ width: 100%;
+ padding: var(--space-lg) var(--space-md);
+ font-size: var(--font-size-lg);
+ border: 2px solid rgba(102, 126, 234, 0.2);
+ border-radius: var(--radius-lg);
+ background: rgba(255, 255, 255, 0.9);
+ transition: all var(--transition-normal);
+ outline: none;
+ color: var(--dark-gray);
+}
+
+.magic-input:focus {
+ border-color: var(--primary-blue);
+ background: var(--white);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
+}
+
+.magic-input::placeholder {
+ color: var(--medium-gray);
+ font-style: italic;
+}
+
+.input-border {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ width: 0;
+ height: 3px;
+ background: var(--primary-gradient);
+ border-radius: var(--radius-sm);
+ transition: all var(--transition-normal);
+ transform: translateX(-50%);
+}
+
+.magic-input:focus + .input-border {
+ width: 100%;
+}
+
+.cta-button {
+ width: 100%;
+ padding: var(--space-lg) var(--space-xl);
+ font-size: var(--font-size-xl);
+ font-weight: 600;
+ color: var(--white);
+ background: var(--primary-gradient);
+ border: none;
+ border-radius: var(--radius-lg);
+ cursor: pointer;
+ position: relative;
+ overflow: hidden;
+ transition: all var(--transition-normal);
+ transform: translateY(0);
+}
+
+.cta-button:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
+}
+
+.cta-button:active {
+ transform: translateY(-1px);
+}
+
+.button-text {
+ position: relative;
+ z-index: 2;
+}
+
+.button-ripple {
+ 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: all var(--transition-fast);
+ pointer-events: none;
+}
+
+.cta-button:active .button-ripple {
+ width: 300px;
+ height: 300px;
+}
+
+.login-footer {
+ text-align: center;
+}
+
+.help-text {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+ font-style: italic;
+}
+
+/* === LOADING OVERLAY === */
+.loading-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(5px);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ z-index: 9999;
+ opacity: 0;
+ visibility: hidden;
+ transition: all var(--transition-normal);
+}
+
+.loading-overlay.active {
+ opacity: 1;
+ visibility: visible;
+}
+
+.loading-text {
+ color: var(--white);
+ font-size: var(--font-size-lg);
+ margin-top: var(--space-lg);
+ font-weight: 500;
+}
+
+/* === RESPONSIVE DESIGN === */
+/* Mobile (< 768px) */
+@media (max-width: 767px) {
+ .login-container {
+ max-width: 100%;
+ padding: 0 var(--space-md);
+ }
+
+ .magic-login-card {
+ padding: var(--space-2xl) var(--space-lg);
+ }
+
+ .login-title {
+ font-size: var(--font-size-2xl);
+ }
+
+ .magic-input {
+ padding: var(--space-md);
+ font-size: var(--font-size-base);
+ }
+
+ .cta-button {
+ padding: var(--space-md) var(--space-lg);
+ font-size: var(--font-size-lg);
+ }
+}
+
+/* Tablet (768px - 1024px) */
+@media (min-width: 768px) and (max-width: 1024px) {
+ .login-container {
+ max-width: 400px;
+ }
+}
+
+/* Desktop (> 1024px) */
+@media (min-width: 1025px) {
+ .magic-login-card {
+ padding: var(--space-3xl) var(--space-2xl);
+ }
+
+ .login-title {
+ font-size: var(--font-size-4xl);
+ }
+}
+
+/* === GROUP SELECTION PAGE === */
+.select-group-page {
+ min-height: 100vh;
+ background: var(--light-gray);
+}
+
+.page-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: var(--space-lg);
+ position: relative;
+ z-index: 1;
+}
+
+.page-header {
+ text-align: center;
+ margin-bottom: var(--space-3xl);
+ padding-top: var(--space-xl);
+}
+
+.header-content {
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.page-title {
+ font-size: var(--font-size-3xl);
+ font-weight: 700;
+ background: var(--primary-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ margin-bottom: var(--space-md);
+}
+
+.page-subtitle {
+ font-size: var(--font-size-lg);
+ color: var(--medium-gray);
+ margin-bottom: var(--space-xl);
+}
+
+.user-info {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-md);
+ padding: var(--space-md) var(--space-lg);
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(10px);
+ border-radius: var(--radius-xl);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: var(--shadow-light);
+ display: inline-flex;
+}
+
+.user-name {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+/* Groups Grid */
+.groups-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: var(--space-xl);
+ margin-bottom: var(--space-3xl);
+}
+
+.group-card {
+ background: var(--white);
+ border-radius: var(--radius-2xl);
+ overflow: hidden;
+ box-shadow: var(--shadow-medium);
+ transition: all var(--transition-normal);
+ position: relative;
+ cursor: pointer;
+}
+
+.group-card:hover {
+ transform: translateY(-8px) scale(1.02);
+ box-shadow: var(--shadow-heavy);
+}
+
+.card-inner {
+ padding: var(--space-xl);
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background: var(--white);
+ position: relative;
+ overflow: hidden;
+}
+
+.card-inner.card-primary {
+ background: var(--primary-gradient);
+ color: var(--white);
+}
+
+.card-inner.card-secondary {
+ background: var(--secondary-gradient);
+ color: var(--white);
+}
+
+.card-inner.card-success {
+ background: var(--success-gradient);
+ color: var(--white);
+}
+
+.card-inner.card-warning {
+ background: var(--warning-gradient);
+ color: var(--white);
+}
+
+.card-inner.card-gradient-special {
+ background: linear-gradient(135deg, #667eea 0%, #f093fb 100%);
+ color: var(--white);
+}
+
+.card-header {
+ text-align: center;
+ margin-bottom: var(--space-lg);
+}
+
+.group-icon {
+ font-size: var(--font-size-3xl);
+ margin-bottom: var(--space-sm);
+}
+
+.group-name {
+ font-size: var(--font-size-xl);
+ font-weight: 700;
+ margin-bottom: var(--space-xs);
+}
+
+.group-code {
+ font-size: var(--font-size-sm);
+ opacity: 0.8;
+ font-weight: 500;
+}
+
+.card-body {
+ flex: 1;
+ margin-bottom: var(--space-lg);
+}
+
+.member-info {
+ text-align: center;
+}
+
+.member-count {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ margin-bottom: var(--space-md);
+}
+
+.current {
+ color: inherit;
+}
+
+.separator {
+ margin: 0 var(--space-xs);
+ opacity: 0.7;
+}
+
+.max {
+ opacity: 0.8;
+}
+
+.label {
+ font-size: var(--font-size-base);
+ margin-left: var(--space-xs);
+ opacity: 0.8;
+}
+
+.progress {
+ margin: var(--space-md) 0;
+}
+
+.status-text {
+ font-size: var(--font-size-sm);
+ font-weight: 600;
+}
+
+.status-complete {
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.status-incomplete {
+ color: rgba(255, 255, 255, 0.8);
+}
+
+.card-footer {
+ text-align: center;
+}
+
+.btn-card {
+ width: 100%;
+ background: rgba(255, 255, 255, 0.2);
+ color: inherit;
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ backdrop-filter: blur(10px);
+}
+
+.btn-card:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: translateY(-2px);
+}
+
+.user-badge {
+ position: absolute;
+ top: var(--space-md);
+ right: var(--space-md);
+ background: rgba(255, 255, 255, 0.2);
+ backdrop-filter: blur(10px);
+ padding: var(--space-xs) var(--space-sm);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-xs);
+ font-weight: 600;
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.user-group {
+ border: 3px solid rgba(255, 255, 255, 0.5);
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.3);
+}
+
+/* Page Footer */
+.page-footer {
+ text-align: center;
+ padding-top: var(--space-xl);
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.footer-actions {
+ display: flex;
+ gap: var(--space-md);
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* Member Count Display in Modal */
+.member-count-display {
+ font-size: var(--font-size-3xl);
+ font-weight: 700;
+ color: var(--primary-blue);
+ margin: var(--space-lg) 0;
+}
+
+.member-count-display .count {
+ color: var(--secondary-pink);
+}
+
+/* Responsive Design for Group Selection */
+@media (max-width: 767px) {
+ .groups-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-lg);
+ }
+
+ .page-title {
+ font-size: var(--font-size-2xl);
+ }
+
+ .footer-actions {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .footer-actions .btn {
+ width: 100%;
+ max-width: 300px;
+ }
+}
+
+@media (min-width: 768px) and (max-width: 1024px) {
+ .groups-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+/* === MISSION PAGE === */
+.mission-page {
+ min-height: 100vh;
+ background: var(--light-gray);
+}
+
+.mission-header {
+ padding: var(--space-xl) 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.header-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 var(--space-lg);
+}
+
+.group-info {
+ display: flex;
+ align-items: center;
+ gap: var(--space-lg);
+ margin-bottom: var(--space-xl);
+ justify-content: center;
+}
+
+.group-details h1 {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ margin-bottom: var(--space-xs);
+ color: var(--dark-gray);
+}
+
+.week-info {
+ color: var(--medium-gray);
+ font-size: var(--font-size-base);
+}
+
+.mission-progress {
+ display: flex;
+ justify-content: center;
+}
+
+.progress-steps {
+ display: flex;
+ gap: var(--space-md);
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(10px);
+ padding: var(--space-lg);
+ border-radius: var(--radius-xl);
+ box-shadow: var(--shadow-light);
+}
+
+.step {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--space-xs);
+ padding: var(--space-md);
+ border-radius: var(--radius-md);
+ transition: all var(--transition-normal);
+ cursor: pointer;
+ min-width: 80px;
+}
+
+.step.active {
+ background: var(--primary-gradient);
+ color: var(--white);
+ transform: scale(1.05);
+}
+
+.step.completed {
+ background: var(--success-gradient);
+ color: var(--white);
+}
+
+.step.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.step-icon {
+ font-size: var(--font-size-xl);
+}
+
+.step-label {
+ font-size: var(--font-size-sm);
+ font-weight: 600;
+ text-align: center;
+}
+
+.mission-content {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: var(--space-2xl) var(--space-lg);
+}
+
+.step-content {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-2xl);
+ box-shadow: var(--shadow-light);
+ margin-bottom: var(--space-xl);
+}
+
+.step-header {
+ text-align: center;
+ margin-bottom: var(--space-2xl);
+}
+
+.step-title {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ color: var(--dark-gray);
+ margin-bottom: var(--space-sm);
+}
+
+.step-subtitle {
+ color: var(--medium-gray);
+ font-size: var(--font-size-lg);
+}
+
+.step-actions {
+ display: flex;
+ gap: var(--space-md);
+ justify-content: space-between;
+ margin-top: var(--space-2xl);
+ flex-wrap: wrap;
+}
+
+/* Check-in Step */
+.checkin-content {
+ display: grid;
+ gap: var(--space-2xl);
+}
+
+.members-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: var(--space-lg);
+}
+
+.member-card {
+ background: var(--light-gray);
+ border-radius: var(--radius-lg);
+ padding: var(--space-lg);
+ border: 2px solid transparent;
+ transition: all var(--transition-normal);
+}
+
+.member-card.new-member {
+ border-color: var(--success-teal);
+ background: rgba(17, 153, 142, 0.1);
+}
+
+.member-info {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+ margin-bottom: var(--space-md);
+}
+
+.member-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background: var(--primary-gradient);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--white);
+ font-weight: 600;
+}
+
+.member-name {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.new-badge {
+ background: var(--success-gradient);
+ color: var(--white);
+ padding: var(--space-xs) var(--space-sm);
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-xs);
+ font-weight: 600;
+}
+
+.attendance-toggle {
+ display: flex;
+ gap: var(--space-sm);
+}
+
+.toggle-btn {
+ flex: 1;
+ padding: var(--space-sm) var(--space-md);
+ border: 2px solid rgba(0, 0, 0, 0.1);
+ border-radius: var(--radius-md);
+ background: var(--white);
+ cursor: pointer;
+ transition: all var(--transition-normal);
+ font-weight: 600;
+}
+
+.toggle-btn.present {
+ color: var(--success-teal);
+ border-color: var(--success-teal);
+}
+
+.toggle-btn.absent {
+ color: var(--secondary-pink);
+ border-color: var(--secondary-pink);
+}
+
+.toggle-btn.active.present {
+ background: var(--success-gradient);
+ color: var(--white);
+}
+
+.toggle-btn.active.absent {
+ background: var(--secondary-gradient);
+ color: var(--white);
+}
+
+.add-member-section {
+ text-align: center;
+}
+
+.section-title {
+ font-size: var(--font-size-lg);
+ font-weight: 600;
+ margin-bottom: var(--space-lg);
+ color: var(--dark-gray);
+}
+
+.add-member-form {
+ display: flex;
+ gap: var(--space-md);
+ max-width: 400px;
+ margin: 0 auto;
+}
+
+.attendance-summary {
+ text-align: center;
+}
+
+.summary-card {
+ display: flex;
+ justify-content: center;
+ gap: var(--space-xl);
+ background: var(--light-gray);
+ padding: var(--space-lg);
+ border-radius: var(--radius-lg);
+}
+
+.summary-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--space-xs);
+}
+
+.summary-item .count {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ color: var(--primary-blue);
+}
+
+.summary-item .label {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+ font-weight: 600;
+}
+
+/* Announce Step */
+.announce-content {
+ display: grid;
+ gap: var(--space-xl);
+}
+
+.input-with-button {
+ display: flex;
+ gap: var(--space-md);
+}
+
+.announce-list {
+ min-height: 200px;
+}
+
+.announce-items {
+ display: grid;
+ gap: var(--space-md);
+ margin-top: var(--space-lg);
+}
+
+.announce-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--light-gray);
+ padding: var(--space-md) var(--space-lg);
+ border-radius: var(--radius-md);
+}
+
+.announce-name {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.announce-progress {
+ text-align: center;
+}
+
+.progress-info {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--space-md);
+}
+
+.progress-label {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.progress-count {
+ font-weight: 700;
+ color: var(--primary-blue);
+}
+
+/* Upload Step */
+.upload-zone {
+ border: 3px dashed rgba(102, 126, 234, 0.3);
+ border-radius: var(--radius-xl);
+ padding: var(--space-3xl);
+ text-align: center;
+ transition: all var(--transition-normal);
+ cursor: pointer;
+}
+
+.upload-zone:hover,
+.upload-zone.drag-over {
+ border-color: var(--primary-blue);
+ background: rgba(102, 126, 234, 0.05);
+}
+
+.upload-icon {
+ font-size: var(--font-size-4xl);
+ margin-bottom: var(--space-lg);
+}
+
+.upload-title {
+ font-size: var(--font-size-xl);
+ font-weight: 600;
+ margin-bottom: var(--space-sm);
+ color: var(--dark-gray);
+}
+
+.upload-subtitle {
+ color: var(--medium-gray);
+ margin-bottom: var(--space-lg);
+}
+
+.file-list {
+ display: grid;
+ gap: var(--space-md);
+ margin-top: var(--space-lg);
+}
+
+.file-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--light-gray);
+ padding: var(--space-md) var(--space-lg);
+ border-radius: var(--radius-md);
+}
+
+.file-info {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+}
+
+.file-icon {
+ font-size: var(--font-size-xl);
+}
+
+.file-name {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.file-size {
+ color: var(--medium-gray);
+ font-size: var(--font-size-sm);
+}
+
+/* Report Step */
+.report-content {
+ display: grid;
+ gap: var(--space-xl);
+}
+
+.suggestion-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-sm);
+ margin-top: var(--space-md);
+}
+
+.suggestion-tag {
+ background: var(--light-gray);
+ border: 2px solid rgba(0, 0, 0, 0.1);
+ padding: var(--space-sm) var(--space-md);
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: all var(--transition-normal);
+ font-size: var(--font-size-sm);
+ font-weight: 500;
+}
+
+.suggestion-tag:hover {
+ border-color: var(--primary-blue);
+ background: rgba(102, 126, 234, 0.1);
+ color: var(--primary-blue);
+}
+
+/* Mission Footer */
+.mission-footer {
+ text-align: center;
+ padding: var(--space-xl);
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+/* Responsive Design for Mission */
+@media (max-width: 767px) {
+ .group-info {
+ flex-direction: column;
+ text-align: center;
+ gap: var(--space-md);
+ }
+
+ .progress-steps {
+ padding: var(--space-md);
+ gap: var(--space-sm);
+ }
+
+ .step {
+ min-width: 60px;
+ padding: var(--space-sm);
+ }
+
+ .step-label {
+ font-size: var(--font-size-xs);
+ }
+
+ .mission-content {
+ padding: var(--space-lg);
+ }
+
+ .step-content {
+ padding: var(--space-lg);
+ }
+
+ .step-actions {
+ flex-direction: column;
+ }
+
+ .members-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .add-member-form {
+ flex-direction: column;
+ }
+
+ .input-with-button {
+ flex-direction: column;
+ }
+
+ .summary-card {
+ gap: var(--space-lg);
+ }
+}
+
+/* === WEEKLY SUMMARY PAGE === */
+.summary-page {
+ min-height: 100vh;
+ background: var(--light-gray);
+}
+
+.summary-header {
+ text-align: center;
+ padding: var(--space-2xl) 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.completion-badge {
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(10px);
+ border-radius: var(--radius-2xl);
+ padding: var(--space-2xl);
+ margin: 0 auto var(--space-xl);
+ max-width: 400px;
+ box-shadow: var(--shadow-card);
+}
+
+.badge-icon {
+ font-size: var(--font-size-4xl);
+ margin-bottom: var(--space-md);
+}
+
+.badge-title {
+ font-size: var(--font-size-3xl);
+ font-weight: 700;
+ background: var(--success-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ margin-bottom: var(--space-sm);
+}
+
+.badge-subtitle {
+ color: var(--medium-gray);
+ font-size: var(--font-size-lg);
+}
+
+.summary-content {
+ max-width: 1000px;
+ margin: 0 auto;
+ padding: var(--space-2xl) var(--space-lg);
+}
+
+.summary-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: var(--space-xl);
+}
+
+.summary-card {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-xl);
+ box-shadow: var(--shadow-light);
+ transition: all var(--transition-normal);
+}
+
+.summary-card:hover {
+ transform: translateY(-5px);
+ box-shadow: var(--shadow-medium);
+}
+
+.summary-card.full-width {
+ grid-column: 1 / -1;
+}
+
+.card-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+ margin-bottom: var(--space-lg);
+ padding-bottom: var(--space-md);
+ border-bottom: 2px solid var(--light-gray);
+}
+
+.card-icon {
+ font-size: var(--font-size-2xl);
+}
+
+.card-title {
+ font-size: var(--font-size-xl);
+ font-weight: 700;
+ color: var(--dark-gray);
+}
+
+.attendance-stats {
+ display: flex;
+ justify-content: space-around;
+ margin-bottom: var(--space-lg);
+}
+
+.stat-item {
+ text-align: center;
+}
+
+.stat-number {
+ display: block;
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ margin-bottom: var(--space-xs);
+}
+
+.stat-item.present .stat-number {
+ color: var(--success-teal);
+}
+
+.stat-item.absent .stat-number {
+ color: var(--secondary-pink);
+}
+
+.stat-item.new .stat-number {
+ color: var(--primary-blue);
+}
+
+.stat-label {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+ font-weight: 600;
+}
+
+.list-title {
+ font-weight: 600;
+ color: var(--dark-gray);
+ margin-bottom: var(--space-sm);
+}
+
+.member-names {
+ list-style: none;
+ padding: 0;
+}
+
+.member-names li {
+ padding: var(--space-xs) 0;
+ color: var(--medium-gray);
+}
+
+.announce-stats {
+ text-align: center;
+ margin-bottom: var(--space-lg);
+}
+
+.big-number {
+ display: flex;
+ align-items: baseline;
+ justify-content: center;
+ gap: var(--space-xs);
+ margin-bottom: var(--space-sm);
+}
+
+.big-number .number {
+ font-size: var(--font-size-4xl);
+ font-weight: 700;
+ color: var(--primary-blue);
+}
+
+.big-number .unit {
+ font-size: var(--font-size-lg);
+ color: var(--medium-gray);
+}
+
+.stats-label {
+ color: var(--medium-gray);
+ font-weight: 600;
+}
+
+.progress-indicator {
+ margin-top: var(--space-lg);
+}
+
+.progress-text {
+ text-align: center;
+ margin-top: var(--space-sm);
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+}
+
+.media-status {
+ text-align: center;
+ padding: var(--space-lg);
+ border-radius: var(--radius-lg);
+ margin-bottom: var(--space-lg);
+}
+
+.media-status.success {
+ background: rgba(17, 153, 142, 0.1);
+ color: var(--success-teal);
+}
+
+.status-icon {
+ font-size: var(--font-size-2xl);
+ margin-bottom: var(--space-sm);
+}
+
+.status-text {
+ font-weight: 600;
+}
+
+.file-info {
+ background: var(--light-gray);
+ border-radius: var(--radius-md);
+ padding: var(--space-md);
+}
+
+.file-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+}
+
+.file-icon {
+ font-size: var(--font-size-lg);
+}
+
+.file-name {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.report-content {
+ margin-bottom: var(--space-lg);
+}
+
+.report-text {
+ font-style: italic;
+ font-size: var(--font-size-lg);
+ line-height: 1.6;
+ color: var(--dark-gray);
+ border-left: 4px solid var(--primary-blue);
+ padding-left: var(--space-lg);
+ margin: 0;
+}
+
+.report-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-sm);
+}
+
+.tag {
+ background: var(--primary-gradient);
+ color: var(--white);
+ padding: var(--space-xs) var(--space-sm);
+ border-radius: var(--radius-md);
+ font-size: var(--font-size-sm);
+ font-weight: 600;
+}
+
+.summary-footer {
+ text-align: center;
+ padding: var(--space-xl);
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+/* === ADMIN DASHBOARD PAGE === */
+.admin-page {
+ min-height: 100vh;
+ background: var(--light-gray);
+}
+
+.admin-header {
+ padding: var(--space-xl) 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.admin-info {
+ display: flex;
+ align-items: center;
+ gap: var(--space-lg);
+ justify-content: center;
+ margin-bottom: var(--space-lg);
+}
+
+.admin-avatar {
+ background: var(--warning-gradient);
+}
+
+.admin-name {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ color: var(--dark-gray);
+ margin-bottom: var(--space-xs);
+}
+
+.admin-subtitle {
+ color: var(--medium-gray);
+ font-size: var(--font-size-base);
+}
+
+.refresh-info {
+ text-align: center;
+}
+
+.auto-refresh {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-sm);
+ margin-bottom: var(--space-sm);
+ color: var(--primary-blue);
+ font-weight: 600;
+}
+
+.refresh-icon {
+ font-size: var(--font-size-lg);
+}
+
+.last-update {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+}
+
+.admin-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: var(--space-2xl) var(--space-lg);
+}
+
+.overview-stats {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: var(--space-lg);
+ margin-bottom: var(--space-3xl);
+}
+
+.stat-card {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-xl);
+ box-shadow: var(--shadow-light);
+ display: flex;
+ align-items: center;
+ gap: var(--space-lg);
+ transition: all var(--transition-normal);
+}
+
+.stat-card:hover {
+ transform: translateY(-3px);
+ box-shadow: var(--shadow-medium);
+}
+
+.stat-icon {
+ font-size: var(--font-size-3xl);
+ opacity: 0.8;
+}
+
+.stat-info {
+ flex: 1;
+}
+
+.stat-card .stat-number {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ color: var(--primary-blue);
+ display: block;
+ margin-bottom: var(--space-xs);
+}
+
+.stat-card .stat-label {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+ font-weight: 600;
+}
+
+.section-title {
+ font-size: var(--font-size-2xl);
+ font-weight: 700;
+ color: var(--dark-gray);
+ margin-bottom: var(--space-xl);
+ text-align: center;
+}
+
+.dashboard-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
+ gap: var(--space-xl);
+}
+
+.group-dashboard-card {
+ background: var(--white);
+ border-radius: var(--radius-xl);
+ padding: var(--space-xl);
+ box-shadow: var(--shadow-light);
+ transition: all var(--transition-normal);
+}
+
+.group-dashboard-card:hover {
+ transform: translateY(-5px);
+ box-shadow: var(--shadow-medium);
+}
+
+.group-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+ margin-bottom: var(--space-lg);
+}
+
+.group-avatar {
+ width: 50px;
+ height: 50px;
+ font-size: var(--font-size-lg);
+}
+
+.group-title {
+ font-size: var(--font-size-lg);
+ font-weight: 700;
+ margin-bottom: var(--space-xs);
+}
+
+.group-code {
+ font-size: var(--font-size-sm);
+ color: var(--medium-gray);
+}
+
+.group-metrics {
+ display: grid;
+ gap: var(--space-md);
+}
+
+.metric-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: var(--space-sm) 0;
+ border-bottom: 1px solid var(--light-gray);
+}
+
+.metric-label {
+ font-weight: 600;
+ color: var(--dark-gray);
+}
+
+.metric-value {
+ font-weight: 700;
+}
+
+.metric-value.success {
+ color: var(--success-teal);
+}
+
+.metric-value.warning {
+ color: var(--warning-orange);
+}
+
+.metric-value.danger {
+ color: var(--secondary-pink);
+}
+
+.group-progress {
+ margin-top: var(--space-lg);
+}
+
+.progress-label {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--space-sm);
+ font-size: var(--font-size-sm);
+ font-weight: 600;
+}
+
+.admin-footer {
+ text-align: center;
+ padding: var(--space-xl);
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+/* Responsive Design for Summary and Admin */
+@media (max-width: 767px) {
+ .completion-badge {
+ margin: 0 var(--space-md) var(--space-xl);
+ }
+
+ .summary-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-lg);
+ }
+
+ .attendance-stats {
+ flex-direction: column;
+ gap: var(--space-md);
+ }
+
+ .admin-info {
+ flex-direction: column;
+ text-align: center;
+ gap: var(--space-md);
+ }
+
+ .overview-stats {
+ grid-template-columns: 1fr;
+ gap: var(--space-md);
+ }
+
+ .dashboard-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-lg);
+ }
\ No newline at end of file
diff --git a/assets/js/auth.js b/assets/js/auth.js
new file mode 100644
index 0000000..acf0e2d
--- /dev/null
+++ b/assets/js/auth.js
@@ -0,0 +1,285 @@
+/* 🔐 Authentication Logic */
+/* Magic Login functionality for Group Mission Workflow */
+
+class AuthManager {
+ constructor() {
+ this.form = null;
+ this.input = null;
+ this.submitButton = null;
+ this.init();
+ }
+
+ init() {
+ // Wait for DOM to be ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => this.setupEventListeners());
+ } else {
+ this.setupEventListeners();
+ }
+ }
+
+ setupEventListeners() {
+ this.form = document.getElementById('magicLoginForm');
+ this.input = document.getElementById('accessCode');
+ this.submitButton = this.form?.querySelector('button[type="submit"]');
+
+ if (!this.form || !this.input) {
+ console.error('Login form elements not found');
+ return;
+ }
+
+ // Form submission
+ this.form.addEventListener('submit', (e) => this.handleLogin(e));
+
+ // Input validation on typing
+ this.input.addEventListener('input', Utils.debounce((e) => this.validateInput(e), 300));
+
+ // Enter key support
+ this.input.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ this.handleLogin(e);
+ }
+ });
+
+ // Auto-focus input
+ this.input.focus();
+
+ // Check for existing session
+ this.checkExistingSession();
+ }
+
+ async handleLogin(event) {
+ event.preventDefault();
+
+ const accessCode = this.input.value.trim();
+
+ if (!accessCode) {
+ this.showInputError('กรุณาใส่รหัสเข้าใช้งาน');
+ return;
+ }
+
+ // Show loading
+ Utils.showLoading(true, 'กำลังตรวจสอบรหัส...');
+ this.setButtonLoading(true);
+
+ try {
+ // Simulate API delay for better UX
+ await this.delay(1500);
+
+ // Validate access code
+ const userData = Utils.validateAccessCode(accessCode);
+
+ if (!userData) {
+ throw new Error('รหัสไม่ถูกต้อง กรุณาตรวจสอบอีกครั้ง');
+ }
+
+ // Save session
+ Utils.saveUserSession({
+ ...userData,
+ loginTime: new Date().toISOString(),
+ sessionId: Utils.generateId()
+ });
+
+ // Success animation
+ this.showSuccessAnimation();
+
+ // Redirect based on user type
+ await this.delay(1000);
+ this.redirectUser(userData);
+
+ } catch (error) {
+ Utils.showLoading(false);
+ this.setButtonLoading(false);
+ this.showInputError(error.message);
+ }
+ }
+
+ validateInput(event) {
+ const value = event.target.value.trim();
+
+ // Clear previous error states
+ this.clearInputError();
+
+ if (value.length === 0) {
+ return;
+ }
+
+ // Basic format validation
+ if (value.length < 6) {
+ this.showInputHint('รหัสต้องมีอย่างน้อย 6 ตัวอักษร');
+ return;
+ }
+
+ // Check if it matches expected patterns
+ const isGroupCode = /^GROUP\d{3}$/i.test(value);
+ const isAdminCode = /^ADMIN\d{3}$/i.test(value);
+
+ if (!isGroupCode && !isAdminCode) {
+ this.showInputHint('รูปแบบ: GROUP001 หรือ ADMIN001');
+ return;
+ }
+
+ // Show valid state
+ this.showInputValid();
+ }
+
+ showInputError(message) {
+ this.input.classList.add('animate-error');
+ this.input.style.borderColor = '#ff4757';
+ Utils.showAlert(message, 'danger', 4000);
+
+ // Shake animation
+ setTimeout(() => {
+ this.input.classList.remove('animate-error');
+ this.input.style.borderColor = '';
+ }, 1000);
+ }
+
+ showInputHint(message) {
+ // Create or update hint element
+ let hint = this.form.querySelector('.input-hint');
+ if (!hint) {
+ hint = document.createElement('div');
+ hint.className = 'input-hint';
+ hint.style.cssText = `
+ font-size: 0.875rem;
+ color: var(--medium-gray);
+ margin-top: 0.5rem;
+ font-style: italic;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ `;
+ this.input.parentElement.appendChild(hint);
+ }
+
+ hint.textContent = message;
+ hint.style.opacity = '1';
+ }
+
+ showInputValid() {
+ this.input.style.borderColor = '#11998e';
+ const hint = this.form.querySelector('.input-hint');
+ if (hint) {
+ hint.style.opacity = '0';
+ }
+ }
+
+ clearInputError() {
+ this.input.style.borderColor = '';
+ this.input.classList.remove('animate-error');
+ const hint = this.form.querySelector('.input-hint');
+ if (hint) {
+ hint.style.opacity = '0';
+ }
+ }
+
+ setButtonLoading(loading) {
+ if (!this.submitButton) return;
+
+ const buttonText = this.submitButton.querySelector('.button-text');
+
+ if (loading) {
+ this.submitButton.disabled = true;
+ this.submitButton.style.opacity = '0.7';
+ if (buttonText) {
+ buttonText.innerHTML = 'กำลังเข้าสู่ระบบ...';
+ }
+ } else {
+ this.submitButton.disabled = false;
+ this.submitButton.style.opacity = '1';
+ if (buttonText) {
+ buttonText.textContent = 'เข้าสู่ระบบ';
+ }
+ }
+ }
+
+ showSuccessAnimation() {
+ // Success feedback
+ this.input.style.borderColor = '#11998e';
+ this.input.style.background = 'rgba(17, 153, 142, 0.1)';
+
+ const buttonText = this.submitButton.querySelector('.button-text');
+ if (buttonText) {
+ buttonText.innerHTML = '✅ เข้าสู่ระบบสำเร็จ!';
+ }
+
+ this.submitButton.style.background = 'var(--success-gradient)';
+
+ Utils.showAlert('เข้าสู่ระบบสำเร็จ! 🎉', 'success', 2000);
+ }
+
+ redirectUser(userData) {
+ let targetPage;
+
+ if (userData.type === 'admin') {
+ targetPage = 'admin-dashboard.html';
+ } else if (userData.type === 'group') {
+ // Check if group has enough members
+ if (userData.data.members >= userData.data.maxMembers) {
+ targetPage = 'weekly-mission.html';
+ } else {
+ targetPage = 'select-group.html';
+ }
+ }
+
+ if (targetPage) {
+ Utils.showLoading(true, 'กำลังโหลดหน้าถัดไป...');
+ setTimeout(() => {
+ window.location.href = targetPage;
+ }, 500);
+ }
+ }
+
+ checkExistingSession() {
+ const session = Utils.getUserSession();
+ if (session) {
+ // Check if session is still valid (within 24 hours)
+ const loginTime = new Date(session.loginTime);
+ const now = new Date();
+ const hoursPassed = (now - loginTime) / (1000 * 60 * 60);
+
+ if (hoursPassed < 24) {
+ // Show option to continue or login again
+ Utils.showModal(
+ 'พบเซสชันการเข้าใช้งาน',
+ `คุณเคยเข้าใช้งานด้วย ${session.data.name} แล้ว
+ ต้องการดำเนินการต่อหรือเข้าใช้งานใหม่?
`,
+ [
+ {
+ text: 'เข้าใช้งานใหม่',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.clearUserSession();
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'ดำเนินการต่อ',
+ class: 'btn-primary',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ this.redirectUser(session);
+ }
+ }
+ ]
+ );
+ } else {
+ // Clear expired session
+ Utils.clearUserSession();
+ }
+ }
+ }
+
+ delay(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+}
+
+// Initialize authentication manager
+document.addEventListener('DOMContentLoaded', () => {
+ new AuthManager();
+});
+
+// Export for testing
+window.AuthManager = AuthManager;
\ No newline at end of file
diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js
new file mode 100644
index 0000000..d98e9ee
--- /dev/null
+++ b/assets/js/dashboard.js
@@ -0,0 +1,352 @@
+/* 👑 Admin Dashboard Logic */
+/* Real-time monitoring and management dashboard */
+
+class AdminDashboardManager {
+ constructor() {
+ this.userSession = null;
+ this.refreshInterval = null;
+ this.groupsData = Utils.APP_CONFIG.GROUP_CODES;
+ this.init();
+ }
+
+ init() {
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => this.setupPage());
+ } else {
+ this.setupPage();
+ }
+ }
+
+ setupPage() {
+ // Check authentication
+ this.userSession = Utils.getUserSession();
+ if (!this.userSession || this.userSession.type !== 'admin') {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ this.setupHeader();
+ this.setupEventListeners();
+ this.renderDashboard();
+ this.startAutoRefresh();
+ }
+
+ setupHeader() {
+ const adminName = document.getElementById('adminName');
+ const currentWeek = document.getElementById('currentWeek');
+
+ if (this.userSession && this.userSession.data && adminName) {
+ adminName.textContent = this.userSession.data.name;
+ }
+
+ if (currentWeek) {
+ currentWeek.textContent = Utils.getCurrentWeek();
+ }
+
+ this.updateLastRefreshTime();
+ }
+
+ setupEventListeners() {
+ const exportReportBtn = document.getElementById('exportReportBtn');
+ const backButton = document.getElementById('backButton');
+ const logoutButton = document.getElementById('logoutButton');
+
+ if (exportReportBtn) {
+ exportReportBtn.addEventListener('click', () => this.exportReport());
+ }
+
+ if (backButton) {
+ backButton.addEventListener('click', () => {
+ window.location.href = 'index.html';
+ });
+ }
+
+ if (logoutButton) {
+ logoutButton.addEventListener('click', () => this.handleLogout());
+ }
+ }
+
+ renderDashboard() {
+ this.renderOverviewStats();
+ this.renderGroupCards();
+ }
+
+ renderOverviewStats() {
+ const totalGroups = Object.keys(this.groupsData).length;
+ const readyGroups = Object.values(this.groupsData).filter(g => g.members >= g.maxMembers).length;
+ const overallProgress = Math.round((readyGroups / totalGroups) * 100);
+ const totalAnnouncements = this.calculateTotalAnnouncements();
+
+ // Update stat cards
+ const statCards = document.querySelectorAll('.stat-card');
+ if (statCards.length >= 4) {
+ statCards[0].querySelector('.stat-number').textContent = totalGroups;
+ statCards[1].querySelector('.stat-number').textContent = readyGroups;
+ statCards[2].querySelector('.stat-number').textContent = `${overallProgress}%`;
+ statCards[3].querySelector('.stat-number').textContent = totalAnnouncements;
+ }
+ }
+
+ renderGroupCards() {
+ const dashboardGrid = document.getElementById('dashboardGrid');
+ if (!dashboardGrid) return;
+
+ dashboardGrid.innerHTML = '';
+
+ Object.entries(this.groupsData).forEach(([code, data], index) => {
+ const card = this.createGroupDashboardCard(code, data, index);
+ dashboardGrid.appendChild(card);
+ });
+ }
+
+ createGroupDashboardCard(code, data, index) {
+ const completion = (data.members / data.maxMembers) * 100;
+ const isReady = data.members >= data.maxMembers;
+ const announcements = this.getGroupAnnouncements(code);
+ const lastActivity = this.getLastActivity(code);
+
+ // Simulate some mission progress data
+ const missionProgress = isReady ? Math.floor(Math.random() * 100) : 0;
+ const attendanceRate = isReady ? Math.floor(Math.random() * 40) + 60 : 0;
+
+ const card = document.createElement('div');
+ card.className = 'group-dashboard-card animate-fade-in-up';
+ card.style.animationDelay = `${index * 0.1}s`;
+
+ const gradientClasses = ['primary', 'secondary', 'success', 'warning', 'primary'];
+ const gradientClass = gradientClasses[index % gradientClasses.length];
+
+ card.innerHTML = `
+
+
+
+
+ สมาชิก
+
+ ${data.members}/${data.maxMembers}
+
+
+
+
+ การประกาศ
+
+ ${announcements} คน
+
+
+
+ ${isReady ? `
+
+ ความคืบหน้าพันธกิจ
+
+ ${missionProgress}%
+
+
+
+
+ อัตราเข้าร่วม
+
+ ${attendanceRate}%
+
+
+ ` : ''}
+
+
+ กิจกรรมล่าสุด
+ ${lastActivity}
+
+
+
+
+
+ ความสมบูรณ์
+ ${Math.round(completion)}%
+
+
+
+
+ ${isReady ? `
+
+
+ ความคืบหน้าพันธกิจ
+ ${missionProgress}%
+
+
+
+ ` : ''}
+ `;
+
+ return card;
+ }
+
+ getGroupIcon(index) {
+ const icons = ['🌟', '💝', '💖', '🙏', '✨'];
+ return icons[index % icons.length];
+ }
+
+ getGroupAnnouncements(code) {
+ // Simulate announcement data
+ const baseAnnouncements = {
+ 'GROUP001': 1,
+ 'GROUP002': 3,
+ 'GROUP003': 2,
+ 'GROUP004': 0,
+ 'GROUP005': 2
+ };
+ return baseAnnouncements[code] || 0;
+ }
+
+ getLastActivity(code) {
+ const activities = [
+ '10 นาทีที่แล้ว',
+ '1 ชั่วโมงที่แล้ว',
+ '3 ชั่วโมงที่แล้ว',
+ 'เมื่อวาน',
+ '2 วันที่แล้ว'
+ ];
+ return activities[Math.floor(Math.random() * activities.length)];
+ }
+
+ calculateTotalAnnouncements() {
+ return Object.keys(this.groupsData).reduce((total, code) => {
+ return total + this.getGroupAnnouncements(code);
+ }, 0);
+ }
+
+ startAutoRefresh() {
+ this.refreshInterval = setInterval(() => {
+ this.updateLastRefreshTime();
+ this.renderDashboard();
+ }, 30000); // Refresh every 30 seconds
+ }
+
+ updateLastRefreshTime() {
+ const lastUpdate = document.getElementById('lastUpdate');
+ if (lastUpdate) {
+ const now = new Date();
+ const timeString = now.toLocaleTimeString('th-TH', {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ lastUpdate.textContent = timeString;
+ }
+ }
+
+ exportReport() {
+ Utils.showLoading(true, 'กำลังสร้างรายงาน...');
+
+ setTimeout(() => {
+ // Simulate report generation
+ const reportData = this.generateReportData();
+ this.downloadReport(reportData);
+ Utils.showLoading(false);
+ Utils.showAlert('ส่งออกรายงานสำเร็จ! 📄', 'success');
+ }, 2000);
+ }
+
+ generateReportData() {
+ const totalGroups = Object.keys(this.groupsData).length;
+ const readyGroups = Object.values(this.groupsData).filter(g => g.members >= g.maxMembers).length;
+ const totalAnnouncements = this.calculateTotalAnnouncements();
+
+ return {
+ title: `รายงานสรุปกลุ่มพันธกิจ - สัปดาห์ที่ ${Utils.getCurrentWeek()}`,
+ date: Utils.formatThaiDate(new Date()),
+ summary: {
+ totalGroups,
+ readyGroups,
+ completionRate: Math.round((readyGroups / totalGroups) * 100),
+ totalAnnouncements
+ },
+ groups: Object.entries(this.groupsData).map(([code, data]) => ({
+ code,
+ name: data.name,
+ members: `${data.members}/${data.maxMembers}`,
+ ready: data.members >= data.maxMembers,
+ announcements: this.getGroupAnnouncements(code)
+ }))
+ };
+ }
+
+ downloadReport(data) {
+ // Create a simple text report
+ let reportText = `${data.title}\n`;
+ reportText += `วันที่: ${data.date}\n\n`;
+ reportText += `สรุปภาพรวม:\n`;
+ reportText += `- กลุ่มทั้งหมด: ${data.summary.totalGroups} กลุ่ม\n`;
+ reportText += `- กลุ่มที่พร้อม: ${data.summary.readyGroups} กลุ่ม\n`;
+ reportText += `- อัตราความสำเร็จ: ${data.summary.completionRate}%\n`;
+ reportText += `- การประกาศรวม: ${data.summary.totalAnnouncements} คน\n\n`;
+ reportText += `รายละเอียดกลุ่ม:\n`;
+
+ data.groups.forEach(group => {
+ reportText += `\n${group.name} (${group.code}):\n`;
+ reportText += ` - สมาชิก: ${group.members}\n`;
+ reportText += ` - สถานะ: ${group.ready ? 'พร้อม' : 'ยังไม่พร้อม'}\n`;
+ reportText += ` - การประกาศ: ${group.announcements} คน\n`;
+ });
+
+ // Create download link
+ const blob = new Blob([reportText], { type: 'text/plain;charset=utf-8' });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `group-mission-report-week-${Utils.getCurrentWeek()}.txt`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ window.URL.revokeObjectURL(url);
+ }
+
+ handleLogout() {
+ Utils.showModal(
+ 'ออกจากระบบ',
+ 'คุณต้องการออกจากระบบใช่หรือไม่?
',
+ [
+ {
+ text: 'ยกเลิก',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'ออกจากระบบ',
+ class: 'btn-secondary',
+ onClick: () => {
+ if (this.refreshInterval) {
+ clearInterval(this.refreshInterval);
+ }
+ Utils.clearUserSession();
+ window.location.href = 'index.html';
+ }
+ }
+ ]
+ );
+ }
+}
+
+// Initialize admin dashboard manager
+document.addEventListener('DOMContentLoaded', () => {
+ new AdminDashboardManager();
+});
+
+// Export for testing
+window.AdminDashboardManager = AdminDashboardManager;
\ No newline at end of file
diff --git a/assets/js/group-selection.js b/assets/js/group-selection.js
new file mode 100644
index 0000000..050d06f
--- /dev/null
+++ b/assets/js/group-selection.js
@@ -0,0 +1,264 @@
+/* 🎯 Group Selection Logic */
+/* 3D Card selection with validation for Group Mission Workflow */
+
+class GroupSelectionManager {
+ constructor() {
+ this.userSession = null;
+ this.groupsData = Utils.APP_CONFIG.GROUP_CODES;
+ this.init();
+ }
+
+ init() {
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => this.setupPage());
+ } else {
+ this.setupPage();
+ }
+ }
+
+ setupPage() {
+ // Check authentication
+ this.userSession = Utils.getUserSession();
+ if (!this.userSession) {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ this.setupUserInfo();
+ this.renderGroupCards();
+ this.setupEventListeners();
+ }
+
+ setupUserInfo() {
+ const userAvatar = document.getElementById('userAvatar');
+ const userName = document.getElementById('userName');
+
+ if (userAvatar && userName && this.userSession) {
+ // Set avatar with first letter of group name or admin symbol
+ if (this.userSession.type === 'group') {
+ const groupName = this.userSession.data.name;
+ userAvatar.textContent = groupName.charAt(0);
+ userName.textContent = groupName;
+ } else {
+ userAvatar.textContent = '👑';
+ userName.textContent = this.userSession.data.name;
+ }
+ }
+ }
+
+ renderGroupCards() {
+ const grid = document.getElementById('groupsGrid');
+ if (!grid) return;
+
+ grid.innerHTML = '';
+
+ Object.entries(this.groupsData).forEach(([code, data], index) => {
+ const card = this.createGroupCard(code, data, index);
+ grid.appendChild(card);
+ });
+ }
+
+ createGroupCard(code, data, index) {
+ const progress = (data.members / data.maxMembers) * 100;
+ const isComplete = data.members >= data.maxMembers;
+ const isUserGroup = this.userSession.code === code;
+
+ const card = document.createElement('div');
+ card.className = `group-card card-3d card-interactive ${isUserGroup ? 'user-group' : ''} ${isComplete ? 'complete' : 'incomplete'}`;
+ card.dataset.groupCode = code;
+ card.style.animationDelay = `${index * 0.1}s`;
+
+ // Determine gradient class based on index
+ const gradientClasses = ['card-primary', 'card-secondary', 'card-success', 'card-warning', 'card-gradient-special'];
+ const gradientClass = gradientClasses[index % gradientClasses.length];
+
+ card.innerHTML = `
+
+
+
+
+
+
+ ${data.members}
+ /
+ ${data.maxMembers}
+ คน
+
+
+
+
+
+ ${isComplete ?
+ '✅ พร้อมเริ่มพันธกิจ' :
+ `⏳ ต้องการอีก ${data.maxMembers - data.members} คน`
+ }
+
+
+
+
+
+
+ ${isUserGroup ? '
กลุ่มของคุณ
' : ''}
+
+ `;
+
+ return card;
+ }
+
+ getGroupIcon(index) {
+ const icons = ['🌟', '💝', '💖', '🙏', '✨'];
+ return icons[index % icons.length];
+ }
+
+ getProgressColor(percentage) {
+ if (percentage < 40) return 'progress-bar-danger';
+ if (percentage < 80) return 'progress-bar-warning';
+ return 'progress-bar-success';
+ }
+
+ setupEventListeners() {
+ // Group card clicks
+ document.addEventListener('click', (e) => {
+ if (e.target.matches('[data-action="select-group"]')) {
+ const code = e.target.dataset.code;
+ this.handleGroupSelection(code);
+ }
+ });
+
+ // Navigation buttons
+ const backButton = document.getElementById('backButton');
+ const logoutButton = document.getElementById('logoutButton');
+
+ if (backButton) {
+ backButton.addEventListener('click', () => {
+ window.location.href = 'index.html';
+ });
+ }
+
+ if (logoutButton) {
+ logoutButton.addEventListener('click', () => {
+ this.handleLogout();
+ });
+ }
+
+ // Card hover effects
+ document.addEventListener('mouseenter', (e) => {
+ if (e.target.closest('.group-card')) {
+ e.target.closest('.group-card').style.transform = 'translateY(-8px) scale(1.02) rotateY(5deg)';
+ }
+ }, true);
+
+ document.addEventListener('mouseleave', (e) => {
+ if (e.target.closest('.group-card')) {
+ e.target.closest('.group-card').style.transform = '';
+ }
+ }, true);
+ }
+
+ async handleGroupSelection(code) {
+ const groupData = this.groupsData[code];
+ if (!groupData) return;
+
+ Utils.showLoading(true, 'กำลังตรวจสอบกลุ่ม...');
+
+ try {
+ await this.delay(1000);
+
+ if (groupData.members < groupData.maxMembers) {
+ // Show warning modal
+ Utils.showModal(
+ '⚠️ กลุ่มยังไม่พร้อม',
+ `
+
+
${groupData.name}
+
ต้องประกาศข่าวประเสริฐให้ครบ 5 คนก่อนเช็คชื่อ
+
+
+ ${groupData.members}
+ /
+ ${groupData.maxMembers}
+ คน
+
+
ต้องการอีก ${groupData.maxMembers - groupData.members} คน
+
+
+ `,
+ [
+ {
+ text: 'เข้าใจแล้ว',
+ class: 'btn-primary',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ }
+ ]
+ );
+ } else {
+ // Group is ready, proceed to mission
+ Utils.showAlert('กลุ่มพร้อมแล้ว! กำลังเข้าสู่พันธกิจ 🎉', 'success');
+
+ // Update user session with selected group
+ this.userSession.selectedGroup = code;
+ Utils.saveUserSession(this.userSession);
+
+ // Redirect to weekly mission
+ setTimeout(() => {
+ window.location.href = 'weekly-mission.html';
+ }, 1500);
+ }
+
+ } catch (error) {
+ Utils.showAlert('เกิดข้อผิดพลาด: ' + error.message, 'danger');
+ } finally {
+ Utils.showLoading(false);
+ }
+ }
+
+ handleLogout() {
+ Utils.showModal(
+ 'ออกจากระบบ',
+ 'คุณต้องการออกจากระบบใช่หรือไม่?
',
+ [
+ {
+ text: 'ยกเลิก',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'ออกจากระบบ',
+ class: 'btn-secondary',
+ onClick: () => {
+ Utils.clearUserSession();
+ window.location.href = 'index.html';
+ }
+ }
+ ]
+ );
+ }
+
+ delay(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+}
+
+// Initialize group selection manager
+document.addEventListener('DOMContentLoaded', () => {
+ new GroupSelectionManager();
+});
+
+// Export for testing
+window.GroupSelectionManager = GroupSelectionManager;
\ No newline at end of file
diff --git a/assets/js/mission.js b/assets/js/mission.js
new file mode 100644
index 0000000..783f9ae
--- /dev/null
+++ b/assets/js/mission.js
@@ -0,0 +1,699 @@
+/* 📱 Mission Workflow Logic */
+/* 4-step weekly mission process for Group Mission Workflow */
+
+class MissionManager {
+ constructor() {
+ this.userSession = null;
+ this.currentStep = 1;
+ this.missionData = {
+ attendance: [],
+ announcements: [],
+ media: [],
+ report: ''
+ };
+ this.steps = {
+ 1: 'checkin',
+ 2: 'announce',
+ 3: 'upload',
+ 4: 'report'
+ };
+ this.init();
+ }
+
+ init() {
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => this.setupPage());
+ } else {
+ this.setupPage();
+ }
+ }
+
+ setupPage() {
+ // Check authentication
+ this.userSession = Utils.getUserSession();
+ if (!this.userSession || this.userSession.type !== 'group') {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ this.setupHeader();
+ this.setupEventListeners();
+ this.loadCurrentStep();
+ }
+
+ setupHeader() {
+ const groupAvatar = document.getElementById('groupAvatar');
+ const groupName = document.getElementById('groupName');
+ const currentWeek = document.getElementById('currentWeek');
+ const currentDate = document.getElementById('currentDate');
+
+ if (this.userSession && this.userSession.data) {
+ const groupData = this.userSession.data;
+
+ if (groupAvatar) {
+ groupAvatar.textContent = groupData.name.charAt(0);
+ }
+
+ if (groupName) {
+ groupName.textContent = groupData.name;
+ }
+ }
+
+ if (currentWeek) {
+ currentWeek.textContent = Utils.getCurrentWeek();
+ }
+
+ if (currentDate) {
+ currentDate.textContent = Utils.formatThaiDate(new Date());
+ }
+ }
+
+ setupEventListeners() {
+ // Navigation buttons
+ const backButton = document.getElementById('backButton');
+ const logoutButton = document.getElementById('logoutButton');
+
+ if (backButton) {
+ backButton.addEventListener('click', () => {
+ window.location.href = 'select-group.html';
+ });
+ }
+
+ if (logoutButton) {
+ logoutButton.addEventListener('click', () => {
+ this.handleLogout();
+ });
+ }
+
+ // Step navigation
+ document.addEventListener('click', (e) => {
+ if (e.target.closest('.step[data-step]')) {
+ const step = parseInt(e.target.closest('.step').dataset.step);
+ if (step <= this.currentStep || step === this.currentStep + 1) {
+ this.navigateToStep(step);
+ }
+ }
+ });
+ }
+
+ loadCurrentStep() {
+ this.updateProgressSteps();
+ this.renderStepContent();
+ }
+
+ updateProgressSteps() {
+ const steps = document.querySelectorAll('.step');
+ steps.forEach((step, index) => {
+ const stepNumber = index + 1;
+ step.classList.remove('active', 'completed', 'disabled');
+
+ if (stepNumber < this.currentStep) {
+ step.classList.add('completed');
+ } else if (stepNumber === this.currentStep) {
+ step.classList.add('active');
+ } else {
+ step.classList.add('disabled');
+ }
+ });
+ }
+
+ renderStepContent() {
+ const content = document.getElementById('missionContent');
+ if (!content) return;
+
+ const stepMethod = `render${this.steps[this.currentStep].charAt(0).toUpperCase() + this.steps[this.currentStep].slice(1)}Step`;
+
+ if (this[stepMethod]) {
+ content.innerHTML = '';
+ this[stepMethod](content);
+ }
+ }
+
+ renderCheckinStep(container) {
+ const stepContent = document.createElement('div');
+ stepContent.className = 'step-content animate-fade-in-up';
+ stepContent.innerHTML = `
+
+
+
+
+
+
+
+
+
เพิ่มสมาชิกใหม่
+
+
+
+
+
+
+
+
+
+ 0
+ มา
+
+
+ 0
+ ไม่มา
+
+
+ 0
+ ใหม่
+
+
+
+
+
+
+
+
+ `;
+
+ container.appendChild(stepContent);
+ this.setupCheckinHandlers();
+ this.loadMembers();
+ }
+
+ renderAnnounceStep(container) {
+ const stepContent = document.createElement('div');
+ stepContent.className = 'step-content animate-fade-in-up';
+ stepContent.innerHTML = `
+
+
+
+
+
+
+
รายชื่อที่ประกาศแล้ว
+
+
+
+
+
+
+
+ ความคืบหน้า
+ 0 คน
+
+
+
+
+
+
+
+
+
+ `;
+
+ container.appendChild(stepContent);
+ this.setupAnnounceHandlers();
+ }
+
+ renderUploadStep(container) {
+ const stepContent = document.createElement('div');
+ stepContent.className = 'step-content animate-fade-in-up';
+ stepContent.innerHTML = `
+
+
+
+
+
📁
+
ลากไฟล์มาวางที่นี่
+
หรือคลิกเพื่อเลือกไฟล์
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ container.appendChild(stepContent);
+ this.setupUploadHandlers();
+ }
+
+ renderReportStep(container) {
+ const stepContent = document.createElement('div');
+ stepContent.className = 'step-content animate-fade-in-up';
+ stepContent.innerHTML = `
+
+
+
+
+
+
+
+
+
+
💡 ข้อเสนอแนะ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ container.appendChild(stepContent);
+ this.setupReportHandlers();
+ }
+
+ setupCheckinHandlers() {
+ const addMemberBtn = document.getElementById('addMemberBtn');
+ const newMemberName = document.getElementById('newMemberName');
+ const nextStepBtn = document.getElementById('nextStepBtn');
+
+ if (addMemberBtn && newMemberName) {
+ addMemberBtn.addEventListener('click', () => this.addNewMember());
+ newMemberName.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') this.addNewMember();
+ });
+ }
+
+ if (nextStepBtn) {
+ nextStepBtn.addEventListener('click', () => this.navigateToStep(2));
+ }
+ }
+
+ setupAnnounceHandlers() {
+ const addAnnounceBtn = document.getElementById('addAnnounceBtn');
+ const announceToName = document.getElementById('announceToName');
+ const prevStepBtn = document.getElementById('prevStepBtn');
+ const nextStepBtn = document.getElementById('nextStepBtn');
+
+ if (addAnnounceBtn && announceToName) {
+ addAnnounceBtn.addEventListener('click', () => this.addAnnouncement());
+ announceToName.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') this.addAnnouncement();
+ });
+ }
+
+ if (prevStepBtn) {
+ prevStepBtn.addEventListener('click', () => this.navigateToStep(1));
+ }
+
+ if (nextStepBtn) {
+ nextStepBtn.addEventListener('click', () => this.navigateToStep(3));
+ }
+ }
+
+ setupUploadHandlers() {
+ const uploadZone = document.getElementById('uploadZone');
+ const fileInput = document.getElementById('fileInput');
+ const selectFileBtn = document.getElementById('selectFileBtn');
+ const prevStepBtn = document.getElementById('prevStepBtn');
+ const nextStepBtn = document.getElementById('nextStepBtn');
+
+ // File selection
+ if (selectFileBtn && fileInput) {
+ selectFileBtn.addEventListener('click', () => fileInput.click());
+ fileInput.addEventListener('change', (e) => this.handleFileSelect(e.target.files));
+ }
+
+ // Drag and drop
+ if (uploadZone) {
+ uploadZone.addEventListener('dragover', (e) => {
+ e.preventDefault();
+ uploadZone.classList.add('drag-over');
+ });
+
+ uploadZone.addEventListener('dragleave', () => {
+ uploadZone.classList.remove('drag-over');
+ });
+
+ uploadZone.addEventListener('drop', (e) => {
+ e.preventDefault();
+ uploadZone.classList.remove('drag-over');
+ this.handleFileSelect(e.dataTransfer.files);
+ });
+ }
+
+ if (prevStepBtn) {
+ prevStepBtn.addEventListener('click', () => this.navigateToStep(2));
+ }
+
+ if (nextStepBtn) {
+ nextStepBtn.addEventListener('click', () => this.navigateToStep(4));
+ }
+ }
+
+ setupReportHandlers() {
+ const reportText = document.getElementById('reportText');
+ const suggestionTags = document.querySelectorAll('.suggestion-tag');
+ const prevStepBtn = document.getElementById('prevStepBtn');
+ const completeBtn = document.getElementById('completeBtn');
+
+ // Suggestion tags
+ suggestionTags.forEach(tag => {
+ tag.addEventListener('click', () => {
+ const text = tag.dataset.text;
+ if (reportText) {
+ const currentText = reportText.value;
+ const newText = currentText ? `${currentText} ${text}` : text;
+ reportText.value = newText;
+ }
+ });
+ });
+
+ if (prevStepBtn) {
+ prevStepBtn.addEventListener('click', () => this.navigateToStep(3));
+ }
+
+ if (completeBtn) {
+ completeBtn.addEventListener('click', () => this.completeMission());
+ }
+ }
+
+ loadMembers() {
+ // Load existing members and create attendance grid
+ const membersGrid = document.getElementById('membersGrid');
+ if (!membersGrid) return;
+
+ const existingMembers = [
+ 'สมชาย ใจดี',
+ 'สมหญิง รักดี',
+ 'สมศรี สุขใจ'
+ ];
+
+ membersGrid.innerHTML = '';
+ existingMembers.forEach((member, index) => {
+ const memberCard = this.createMemberCard(member, false);
+ membersGrid.appendChild(memberCard);
+ });
+
+ this.updateAttendanceSummary();
+ }
+
+ createMemberCard(name, isNew = false) {
+ const card = document.createElement('div');
+ card.className = `member-card ${isNew ? 'new-member' : ''}`;
+ card.innerHTML = `
+
+
${name.charAt(0)}
+
${name}
+ ${isNew ? '
ใหม่' : ''}
+
+
+
+
+
+ `;
+
+ // Add toggle functionality
+ const toggleBtns = card.querySelectorAll('.toggle-btn');
+ toggleBtns.forEach(btn => {
+ btn.addEventListener('click', () => {
+ toggleBtns.forEach(b => b.classList.remove('active'));
+ btn.classList.add('active');
+ this.updateAttendanceSummary();
+ this.checkNextStepAvailable();
+ });
+ });
+
+ return card;
+ }
+
+ addNewMember() {
+ const newMemberName = document.getElementById('newMemberName');
+ if (!newMemberName || !newMemberName.value.trim()) {
+ Utils.showAlert('กรุณาใส่ชื่อสมาชิกใหม่', 'warning');
+ return;
+ }
+
+ const name = newMemberName.value.trim();
+ const membersGrid = document.getElementById('membersGrid');
+
+ if (membersGrid) {
+ const memberCard = this.createMemberCard(name, true);
+ membersGrid.appendChild(memberCard);
+ memberCard.classList.add('animate-bounce-in');
+ }
+
+ newMemberName.value = '';
+ this.updateAttendanceSummary();
+ Utils.showAlert(`เพิ่ม ${name} เรียบร้อยแล้ว! 👍`, 'success');
+ }
+
+ updateAttendanceSummary() {
+ const presentCount = document.querySelectorAll('.toggle-btn.present.active').length;
+ const absentCount = document.querySelectorAll('.toggle-btn.absent.active').length;
+ const newMemberCount = document.querySelectorAll('.member-card.new-member').length;
+
+ const presentCountEl = document.getElementById('presentCount');
+ const absentCountEl = document.getElementById('absentCount');
+ const newMemberCountEl = document.getElementById('newMemberCount');
+
+ if (presentCountEl) presentCountEl.textContent = presentCount;
+ if (absentCountEl) absentCountEl.textContent = absentCount;
+ if (newMemberCountEl) newMemberCountEl.textContent = newMemberCount;
+ }
+
+ checkNextStepAvailable() {
+ const nextStepBtn = document.getElementById('nextStepBtn');
+ const presentCount = document.querySelectorAll('.toggle-btn.present.active').length;
+
+ if (nextStepBtn) {
+ nextStepBtn.disabled = presentCount === 0;
+ }
+ }
+
+ addAnnouncement() {
+ const announceToName = document.getElementById('announceToName');
+ if (!announceToName || !announceToName.value.trim()) {
+ Utils.showAlert('กรุณาใส่ชื่อผู้ที่ประกาศให้', 'warning');
+ return;
+ }
+
+ const name = announceToName.value.trim();
+ this.missionData.announcements.push({
+ name: name,
+ timestamp: new Date().toISOString()
+ });
+
+ this.updateAnnounceList();
+ announceToName.value = '';
+ Utils.showAlert(`บันทึกการประกาศให้ ${name} เรียบร้อยแล้ว! 📢`, 'success');
+ }
+
+ updateAnnounceList() {
+ const announceItems = document.getElementById('announceItems');
+ const announceCount = document.getElementById('announceCount');
+ const announceProgressBar = document.getElementById('announceProgressBar');
+
+ if (announceItems) {
+ announceItems.innerHTML = '';
+ this.missionData.announcements.forEach((announce, index) => {
+ const item = document.createElement('div');
+ item.className = 'announce-item animate-fade-in-up';
+ item.innerHTML = `
+ ${announce.name}
+
+ `;
+ announceItems.appendChild(item);
+ });
+
+ // Add remove functionality
+ announceItems.addEventListener('click', (e) => {
+ if (e.target.classList.contains('remove-btn')) {
+ const index = parseInt(e.target.dataset.index);
+ this.missionData.announcements.splice(index, 1);
+ this.updateAnnounceList();
+ }
+ });
+ }
+
+ if (announceCount) {
+ announceCount.textContent = this.missionData.announcements.length;
+ }
+
+ if (announceProgressBar) {
+ const progress = Math.min((this.missionData.announcements.length / 5) * 100, 100);
+ announceProgressBar.style.width = `${progress}%`;
+ }
+ }
+
+ handleFileSelect(files) {
+ const uploadPreview = document.getElementById('uploadPreview');
+ const fileList = document.getElementById('fileList');
+ const nextStepBtn = document.getElementById('nextStepBtn');
+
+ if (!files || files.length === 0) return;
+
+ this.missionData.media = Array.from(files);
+
+ if (fileList) {
+ fileList.innerHTML = '';
+ this.missionData.media.forEach((file, index) => {
+ const item = document.createElement('div');
+ item.className = 'file-item animate-fade-in-up';
+ item.innerHTML = `
+
+
📸
+
${file.name}
+
(${(file.size / 1024 / 1024).toFixed(2)} MB)
+
+
+ `;
+ fileList.appendChild(item);
+ });
+ }
+
+ if (uploadPreview) {
+ uploadPreview.style.display = 'block';
+ }
+
+ if (nextStepBtn) {
+ nextStepBtn.disabled = false;
+ }
+
+ Utils.showAlert(`เลือกไฟล์ ${files.length} ไฟล์เรียบร้อยแล้ว! 📁`, 'success');
+ }
+
+ async completeMission() {
+ const reportText = document.getElementById('reportText');
+ if (!reportText || !reportText.value.trim()) {
+ Utils.showAlert('กรุณาเขียนรายงานผลก่อนส่ง', 'warning');
+ return;
+ }
+
+ this.missionData.report = reportText.value.trim();
+
+ Utils.showLoading(true, 'กำลังส่งรายงาน...');
+
+ try {
+ await this.delay(2000);
+
+ // Save mission data
+ this.saveMissionData();
+
+ Utils.showLoading(false);
+ Utils.showAlert('ส่งรายงานสำเร็จ! 🎉', 'success', 3000);
+
+ // Redirect to summary
+ setTimeout(() => {
+ window.location.href = 'weekly-summary.html';
+ }, 2000);
+
+ } catch (error) {
+ Utils.showLoading(false);
+ Utils.showAlert('เกิดข้อผิดพลาด: ' + error.message, 'danger');
+ }
+ }
+
+ saveMissionData() {
+ const weeklyData = {
+ week: Utils.getCurrentWeek(),
+ date: new Date().toISOString(),
+ groupCode: this.userSession.code,
+ ...this.missionData
+ };
+
+ try {
+ localStorage.setItem(Utils.APP_CONFIG.STORAGE_KEYS.WEEKLY_DATA, JSON.stringify(weeklyData));
+ } catch (error) {
+ console.error('Error saving mission data:', error);
+ }
+ }
+
+ navigateToStep(step) {
+ if (step < 1 || step > 4) return;
+
+ this.currentStep = step;
+ this.loadCurrentStep();
+ }
+
+ handleLogout() {
+ Utils.showModal(
+ 'ออกจากระบบ',
+ 'คุณต้องการออกจากระบบใช่หรือไม่?
⚠️ ข้อมูลที่ยังไม่ได้บันทึกจะสูญหาย
',
+ [
+ {
+ text: 'ยกเลิก',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'ออกจากระบบ',
+ class: 'btn-secondary',
+ onClick: () => {
+ Utils.clearUserSession();
+ window.location.href = 'index.html';
+ }
+ }
+ ]
+ );
+ }
+
+ delay(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+}
+
+// Initialize mission manager
+document.addEventListener('DOMContentLoaded', () => {
+ new MissionManager();
+});
+
+// Export for testing
+window.MissionManager = MissionManager;
\ No newline at end of file
diff --git a/assets/js/utils.js b/assets/js/utils.js
new file mode 100644
index 0000000..533d6f3
--- /dev/null
+++ b/assets/js/utils.js
@@ -0,0 +1,381 @@
+/* 🛠️ Utility Functions */
+/* Helper functions for the Group Mission Workflow */
+
+// === CONSTANTS ===
+const APP_CONFIG = {
+ // Group codes (รหัสกลุ่ม)
+ GROUP_CODES: {
+ 'GROUP001': { name: 'กลุ่มแสงแรก', members: 3, maxMembers: 5 },
+ 'GROUP002': { name: 'กลุ่มความหวัง', members: 5, maxMembers: 5 },
+ 'GROUP003': { name: 'กลุ่มความรัก', members: 4, maxMembers: 5 },
+ 'GROUP004': { name: 'กลุ่มความเชื่อ', members: 2, maxMembers: 5 },
+ 'GROUP005': { name: 'กลุ่มพระคุณ', members: 5, maxMembers: 5 },
+ },
+
+ // Admin codes (รหัสแอดมิน)
+ ADMIN_CODES: {
+ 'ADMIN001': { name: 'ผู้ดูแลระบบหลัก', level: 'super' },
+ 'ADMIN002': { name: 'ผู้ช่วยดูแลระบบ', level: 'regular' }
+ },
+
+ // Session storage keys
+ STORAGE_KEYS: {
+ USER_SESSION: 'groupMissionUserSession',
+ GROUP_DATA: 'groupMissionGroupData',
+ WEEKLY_DATA: 'groupMissionWeeklyData'
+ }
+};
+
+// === UTILITY FUNCTIONS ===
+
+/**
+ * Display loading overlay
+ * @param {boolean} show - Whether to show or hide the overlay
+ * @param {string} text - Loading text to display
+ */
+function showLoading(show = true, text = 'กำลังโหลด...') {
+ const overlay = document.getElementById('loadingOverlay');
+ if (!overlay) return;
+
+ const loadingText = overlay.querySelector('.loading-text');
+ if (loadingText) {
+ loadingText.textContent = text;
+ }
+
+ if (show) {
+ overlay.classList.add('active');
+ } else {
+ overlay.classList.remove('active');
+ }
+}
+
+/**
+ * Show notification/alert message
+ * @param {string} message - Message to display
+ * @param {string} type - Type of alert ('success', 'warning', 'danger', 'info')
+ * @param {number} duration - How long to show (milliseconds)
+ */
+function showAlert(message, type = 'info', duration = 3000) {
+ // Remove existing alerts
+ const existingAlerts = document.querySelectorAll('.alert-notification');
+ existingAlerts.forEach(alert => alert.remove());
+
+ // Create new alert
+ const alert = document.createElement('div');
+ alert.className = `alert alert-${type} alert-notification animate-bounce-in`;
+ alert.style.cssText = `
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ z-index: 10000;
+ max-width: 400px;
+ box-shadow: var(--shadow-heavy);
+ `;
+ alert.textContent = message;
+
+ document.body.appendChild(alert);
+
+ // Auto remove after duration
+ setTimeout(() => {
+ alert.style.animation = 'fadeInUp 0.3s ease-out reverse';
+ setTimeout(() => alert.remove(), 300);
+ }, duration);
+}
+
+/**
+ * Show modal popup
+ * @param {string} title - Modal title
+ * @param {string} content - Modal content (HTML)
+ * @param {Array} buttons - Array of button objects {text, class, onClick}
+ */
+function showModal(title, content, buttons = []) {
+ // Remove existing modal
+ const existingModal = document.querySelector('.modal-overlay');
+ if (existingModal) {
+ existingModal.remove();
+ }
+
+ // Create modal
+ const modalOverlay = document.createElement('div');
+ modalOverlay.className = 'modal-overlay';
+ modalOverlay.innerHTML = `
+
+ `;
+
+ document.body.appendChild(modalOverlay);
+
+ // Add event listeners
+ buttons.forEach((btn, index) => {
+ const buttonEl = modalOverlay.querySelectorAll('.modal-footer .btn')[index];
+ if (buttonEl && btn.onClick) {
+ buttonEl.addEventListener('click', btn.onClick);
+ }
+ });
+
+ // Show modal
+ setTimeout(() => {
+ modalOverlay.classList.add('active');
+ }, 10);
+
+ // Close on overlay click
+ modalOverlay.addEventListener('click', (e) => {
+ if (e.target === modalOverlay) {
+ closeModal(modalOverlay);
+ }
+ });
+
+ return modalOverlay;
+}
+
+/**
+ * Close modal
+ * @param {Element} modal - Modal element to close
+ */
+function closeModal(modal) {
+ if (!modal) return;
+
+ modal.classList.remove('active');
+ setTimeout(() => {
+ modal.remove();
+ }, 300);
+}
+
+/**
+ * Validate access code (group or admin)
+ * @param {string} code - Code to validate
+ * @returns {Object|null} User data if valid, null if invalid
+ */
+function validateAccessCode(code) {
+ const upperCode = code.toUpperCase().trim();
+
+ // Check group codes
+ if (APP_CONFIG.GROUP_CODES[upperCode]) {
+ return {
+ type: 'group',
+ code: upperCode,
+ data: APP_CONFIG.GROUP_CODES[upperCode]
+ };
+ }
+
+ // Check admin codes
+ if (APP_CONFIG.ADMIN_CODES[upperCode]) {
+ return {
+ type: 'admin',
+ code: upperCode,
+ data: APP_CONFIG.ADMIN_CODES[upperCode]
+ };
+ }
+
+ return null;
+}
+
+/**
+ * Save user session to localStorage
+ * @param {Object} userData - User data to save
+ */
+function saveUserSession(userData) {
+ try {
+ localStorage.setItem(APP_CONFIG.STORAGE_KEYS.USER_SESSION, JSON.stringify(userData));
+ } catch (error) {
+ console.error('Error saving user session:', error);
+ }
+}
+
+/**
+ * Get user session from localStorage
+ * @returns {Object|null} User data or null
+ */
+function getUserSession() {
+ try {
+ const data = localStorage.getItem(APP_CONFIG.STORAGE_KEYS.USER_SESSION);
+ return data ? JSON.parse(data) : null;
+ } catch (error) {
+ console.error('Error getting user session:', error);
+ return null;
+ }
+}
+
+/**
+ * Clear user session
+ */
+function clearUserSession() {
+ try {
+ localStorage.removeItem(APP_CONFIG.STORAGE_KEYS.USER_SESSION);
+ } catch (error) {
+ console.error('Error clearing user session:', error);
+ }
+}
+
+/**
+ * Format date to Thai format
+ * @param {Date} date - Date to format
+ * @returns {string} Formatted date string
+ */
+function formatThaiDate(date) {
+ const options = {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ locale: 'th-TH'
+ };
+ return date.toLocaleDateString('th-TH', options);
+}
+
+/**
+ * Get current week number
+ * @returns {number} Current week number
+ */
+function getCurrentWeek() {
+ const now = new Date();
+ const start = new Date(now.getFullYear(), 0, 1);
+ return Math.ceil(((now - start) / 86400000 + start.getDay() + 1) / 7);
+}
+
+/**
+ * Animate progress bar
+ * @param {Element} progressBar - Progress bar element
+ * @param {number} percentage - Target percentage
+ */
+function animateProgressBar(progressBar, percentage) {
+ if (!progressBar) return;
+
+ progressBar.style.setProperty('--progress-width', `${percentage}%`);
+ progressBar.style.width = `${percentage}%`;
+
+ // Add animation class
+ progressBar.classList.add('progress-fill');
+}
+
+/**
+ * Get progress color based on percentage
+ * @param {number} percentage - Progress percentage
+ * @returns {string} CSS class for color
+ */
+function getProgressColor(percentage) {
+ if (percentage < 30) return 'progress-bar-danger';
+ if (percentage < 70) return 'progress-bar-warning';
+ return 'progress-bar-success';
+}
+
+/**
+ * Debounce function
+ * @param {Function} func - Function to debounce
+ * @param {number} wait - Wait time in milliseconds
+ * @returns {Function} Debounced function
+ */
+function debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+}
+
+/**
+ * Generate unique ID
+ * @returns {string} Unique ID string
+ */
+function generateId() {
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
+}
+
+/**
+ * Add ripple effect to button
+ * @param {Event} event - Click event
+ */
+function addRippleEffect(event) {
+ const button = event.currentTarget;
+ const ripple = button.querySelector('.button-ripple') ||
+ button.querySelector('.btn-ripple');
+
+ if (!ripple) return;
+
+ const rect = button.getBoundingClientRect();
+ const size = Math.max(rect.width, rect.height);
+ const x = event.clientX - rect.left - size / 2;
+ const y = event.clientY - rect.top - size / 2;
+
+ ripple.style.width = ripple.style.height = size + 'px';
+ ripple.style.left = x + 'px';
+ ripple.style.top = y + 'px';
+
+ ripple.classList.add('active');
+
+ setTimeout(() => {
+ ripple.classList.remove('active');
+ }, 600);
+}
+
+/**
+ * Initialize common event listeners
+ */
+function initializeCommonEvents() {
+ // Add ripple effect to buttons
+ document.addEventListener('click', (e) => {
+ if (e.target.matches('.btn, .cta-button, button')) {
+ addRippleEffect(e);
+ }
+ });
+
+ // Handle Enter key on inputs
+ document.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' && e.target.matches('input[type="text"], input[type="email"]')) {
+ const form = e.target.closest('form');
+ if (form) {
+ const submitButton = form.querySelector('button[type="submit"], .btn-submit');
+ if (submitButton) {
+ submitButton.click();
+ }
+ }
+ }
+ });
+
+ // Close modals with Escape key
+ document.addEventListener('keydown', (e) => {
+ if (e.key === 'Escape') {
+ const modal = document.querySelector('.modal-overlay.active');
+ if (modal) {
+ closeModal(modal);
+ }
+ }
+ });
+}
+
+// === EXPORT FOR MODULES ===
+window.Utils = {
+ showLoading,
+ showAlert,
+ showModal,
+ closeModal,
+ validateAccessCode,
+ saveUserSession,
+ getUserSession,
+ clearUserSession,
+ formatThaiDate,
+ getCurrentWeek,
+ animateProgressBar,
+ getProgressColor,
+ debounce,
+ generateId,
+ addRippleEffect,
+ APP_CONFIG
+};
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeCommonEvents);
\ No newline at end of file
diff --git a/assets/js/weekly-summary.js b/assets/js/weekly-summary.js
new file mode 100644
index 0000000..8b35271
--- /dev/null
+++ b/assets/js/weekly-summary.js
@@ -0,0 +1,329 @@
+/* 📊 Weekly Summary Logic */
+/* Display completed mission results */
+
+class WeeklySummaryManager {
+ constructor() {
+ this.userSession = null;
+ this.weeklyData = null;
+ this.init();
+ }
+
+ init() {
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => this.setupPage());
+ } else {
+ this.setupPage();
+ }
+ }
+
+ setupPage() {
+ // Check authentication
+ this.userSession = Utils.getUserSession();
+ if (!this.userSession || this.userSession.type !== 'group') {
+ window.location.href = 'index.html';
+ return;
+ }
+
+ // Load weekly data
+ this.loadWeeklyData();
+ this.setupHeader();
+ this.setupEventListeners();
+ this.renderSummary();
+ }
+
+ loadWeeklyData() {
+ try {
+ const data = localStorage.getItem(Utils.APP_CONFIG.STORAGE_KEYS.WEEKLY_DATA);
+ this.weeklyData = data ? JSON.parse(data) : this.createMockData();
+ } catch (error) {
+ console.error('Error loading weekly data:', error);
+ this.weeklyData = this.createMockData();
+ }
+ }
+
+ createMockData() {
+ // Create mock data for demonstration
+ return {
+ week: Utils.getCurrentWeek(),
+ date: new Date().toISOString(),
+ groupCode: this.userSession.code,
+ attendance: [
+ { name: 'สมชาย ใจดี', status: 'present', isNew: false },
+ { name: 'สมหญิง รักดี', status: 'absent', isNew: false },
+ { name: 'สมศรี สุขใจ', status: 'absent', isNew: false },
+ { name: 'สมพงษ์ มีความสุข', status: 'absent', isNew: true }
+ ],
+ announcements: [
+ { name: 'อลิซ สมิท', timestamp: new Date().toISOString() }
+ ],
+ media: [
+ { name: 'mission_photo.jpg', size: 2048000 }
+ ],
+ report: 'ได้แบ่งปันรากฐานความเชื่อกับ อลิซ สมิท เธอรู้สึกหนุนใจและมีความสุขมาก เราได้อธิษฐานร่วมกันและท้าทายให้เธอเติบโตในความเชื่อ'
+ };
+ }
+
+ setupHeader() {
+ const weekNumber = document.getElementById('weekNumber');
+ const groupAvatar = document.getElementById('groupAvatar');
+ const groupName = document.getElementById('groupName');
+ const completionDate = document.getElementById('completionDate');
+
+ if (weekNumber) {
+ weekNumber.textContent = this.weeklyData.week;
+ }
+
+ if (this.userSession && this.userSession.data) {
+ const groupData = this.userSession.data;
+
+ if (groupAvatar) {
+ groupAvatar.textContent = groupData.name.charAt(0);
+ }
+
+ if (groupName) {
+ groupName.textContent = groupData.name;
+ }
+ }
+
+ if (completionDate) {
+ completionDate.textContent = Utils.formatThaiDate(new Date(this.weeklyData.date));
+ }
+ }
+
+ setupEventListeners() {
+ const backToMissionBtn = document.getElementById('backToMissionBtn');
+ const startNewMissionBtn = document.getElementById('startNewMissionBtn');
+ const logoutButton = document.getElementById('logoutButton');
+
+ if (backToMissionBtn) {
+ backToMissionBtn.addEventListener('click', () => {
+ window.location.href = 'weekly-mission.html';
+ });
+ }
+
+ if (startNewMissionBtn) {
+ startNewMissionBtn.addEventListener('click', () => {
+ this.startNewMission();
+ });
+ }
+
+ if (logoutButton) {
+ logoutButton.addEventListener('click', () => {
+ this.handleLogout();
+ });
+ }
+ }
+
+ renderSummary() {
+ this.renderAttendanceSummary();
+ this.renderAnnouncementSummary();
+ this.renderMediaSummary();
+ this.renderReportSummary();
+ }
+
+ renderAttendanceSummary() {
+ const presentCount = document.getElementById('presentCount');
+ const absentCount = document.getElementById('absentCount');
+ const newMemberCount = document.getElementById('newMemberCount');
+ const presentMembers = document.getElementById('presentMembers');
+ const newMembers = document.getElementById('newMembers');
+
+ if (!this.weeklyData.attendance) return;
+
+ const present = this.weeklyData.attendance.filter(a => a.status === 'present');
+ const absent = this.weeklyData.attendance.filter(a => a.status === 'absent');
+ const newMembersData = this.weeklyData.attendance.filter(a => a.isNew);
+
+ if (presentCount) presentCount.textContent = present.length;
+ if (absentCount) absentCount.textContent = absent.length;
+ if (newMemberCount) newMemberCount.textContent = newMembersData.length;
+
+ if (presentMembers) {
+ const membersList = presentMembers.querySelector('.member-names');
+ if (membersList) {
+ membersList.innerHTML = '';
+ present.forEach(member => {
+ const li = document.createElement('li');
+ li.textContent = member.name;
+ membersList.appendChild(li);
+ });
+ }
+ }
+
+ if (newMembers) {
+ const membersList = newMembers.querySelector('.member-names');
+ if (membersList) {
+ membersList.innerHTML = '';
+ newMembersData.forEach(member => {
+ const li = document.createElement('li');
+ li.textContent = member.name;
+ membersList.appendChild(li);
+ });
+ }
+
+ // Hide section if no new members
+ if (newMembersData.length === 0) {
+ newMembers.style.display = 'none';
+ }
+ }
+ }
+
+ renderAnnouncementSummary() {
+ const announceCount = document.getElementById('announceCount');
+ const announceList = document.getElementById('announceList');
+
+ if (!this.weeklyData.announcements) return;
+
+ const count = this.weeklyData.announcements.length;
+
+ if (announceCount) {
+ announceCount.textContent = count;
+ }
+
+ if (announceList) {
+ const membersList = announceList.querySelector('.member-names');
+ if (membersList) {
+ membersList.innerHTML = '';
+ this.weeklyData.announcements.forEach(announce => {
+ const li = document.createElement('li');
+ li.textContent = announce.name;
+ membersList.appendChild(li);
+ });
+ }
+ }
+
+ // Update progress bar
+ const progressBar = document.querySelector('.announce-stats + .announce-list + .progress-indicator .progress-bar');
+ const progressText = document.querySelector('.progress-text');
+
+ if (progressBar && progressText) {
+ const percentage = Math.min((count / 5) * 100, 100);
+ progressBar.style.width = `${percentage}%`;
+ progressText.textContent = `${Math.round(percentage)}% ของเป้าหมาย 5 คน`;
+
+ // Update color based on progress
+ progressBar.className = 'progress-bar ' + Utils.getProgressColor(percentage);
+ }
+ }
+
+ renderMediaSummary() {
+ const mediaStatus = document.getElementById('mediaStatus');
+ const fileInfo = document.getElementById('fileInfo');
+
+ if (!this.weeklyData.media || this.weeklyData.media.length === 0) {
+ if (mediaStatus) {
+ mediaStatus.className = 'media-status warning';
+ mediaStatus.innerHTML = `
+ ⚠️
+ ยังไม่ได้อัพโหลด
+ `;
+ }
+ if (fileInfo) {
+ fileInfo.innerHTML = 'ไม่มีไฟล์
';
+ }
+ return;
+ }
+
+ if (fileInfo) {
+ fileInfo.innerHTML = '';
+ this.weeklyData.media.forEach(file => {
+ const fileItem = document.createElement('div');
+ fileItem.className = 'file-item';
+ fileItem.innerHTML = `
+ 📸
+ ${file.name}
+ `;
+ fileInfo.appendChild(fileItem);
+ });
+ }
+ }
+
+ renderReportSummary() {
+ const reportContent = document.getElementById('reportContent');
+
+ if (!this.weeklyData.report) {
+ if (reportContent) {
+ reportContent.innerHTML = 'ไม่มีรายงาน
';
+ }
+ return;
+ }
+
+ if (reportContent) {
+ const reportText = reportContent.querySelector('.report-text');
+ if (reportText) {
+ reportText.textContent = `"${this.weeklyData.report}"`;
+ }
+ }
+ }
+
+ startNewMission() {
+ Utils.showModal(
+ '🚀 เริ่มพันธกิจใหม่',
+ 'คุณต้องการเริ่มพันธกิจสัปดาห์ใหม่ใช่หรือไม่?
ข้อมูลของสัปดาห์ปัจจุบันจะถูกเก็บไว้
',
+ [
+ {
+ text: 'ยกเลิก',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'เริ่มใหม่',
+ class: 'btn-success',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ this.resetForNewMission();
+ }
+ }
+ ]
+ );
+ }
+
+ resetForNewMission() {
+ // Clear weekly data but keep user session
+ try {
+ localStorage.removeItem(Utils.APP_CONFIG.STORAGE_KEYS.WEEKLY_DATA);
+ } catch (error) {
+ console.error('Error clearing weekly data:', error);
+ }
+
+ Utils.showAlert('เริ่มพันธกิจใหม่เรียบร้อยแล้ว! 🚀', 'success');
+
+ setTimeout(() => {
+ window.location.href = 'weekly-mission.html';
+ }, 1500);
+ }
+
+ handleLogout() {
+ Utils.showModal(
+ 'ออกจากระบบ',
+ 'คุณต้องการออกจากระบบใช่หรือไม่?
',
+ [
+ {
+ text: 'ยกเลิก',
+ class: 'btn-outline',
+ onClick: () => {
+ Utils.closeModal(document.querySelector('.modal-overlay'));
+ }
+ },
+ {
+ text: 'ออกจากระบบ',
+ class: 'btn-secondary',
+ onClick: () => {
+ Utils.clearUserSession();
+ window.location.href = 'index.html';
+ }
+ }
+ ]
+ );
+ }
+}
+
+// Initialize weekly summary manager
+document.addEventListener('DOMContentLoaded', () => {
+ new WeeklySummaryManager();
+});
+
+// Export for testing
+window.WeeklySummaryManager = WeeklySummaryManager;
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..0dead23
--- /dev/null
+++ b/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+ เข้าร่วมนิมิตชีวิตสุขสันต์กาฬสินธุ์ 2025 ✨
+
+
+
+
+
+
+
+
+
+
+
+
+
กำลังเข้าสู่ระบบ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/select-group.html b/select-group.html
new file mode 100644
index 0000000..d8eafe3
--- /dev/null
+++ b/select-group.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+ เลือกกลุ่มพันธกิจ - กาฬสินธุ์ 2025 ✨
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/weekly-mission.html b/weekly-mission.html
new file mode 100644
index 0000000..3835164
--- /dev/null
+++ b/weekly-mission.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+ พันธกิจประจำสัปดาห์ - กาฬสินธุ์ 2025 ✨
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/weekly-summary.html b/weekly-summary.html
new file mode 100644
index 0000000..9f3684b
--- /dev/null
+++ b/weekly-summary.html
@@ -0,0 +1,167 @@
+
+
+
+
+
+ สรุปผลประจำสัปดาห์ - กาฬสินธุ์ 2025 ✨
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ มา
+
+
+ 3
+ ไม่มา
+
+
+ 1
+ สมาชิกใหม่
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ คน
+
+
ได้รับการประกาศ
+
+
+
+
+
20% ของเป้าหมาย 5 คน
+
+
+
+
+
+
+
+
+
+
+
+ 📸
+ mission_photo.jpg
+
+
+
+
+
+
+
+
+
+
+
+ "ได้แบ่งปันรากฐานความเชื่อกับ อลิซ สมิท เธอรู้สึกหนุนใจและมีความสุขมาก เราได้อธิษฐานร่วมกันและท้าทายให้เธอเติบโตในความเชื่อ"
+
+
+
+ แบ่งปันรากฐานความเชื่อ
+ หนุนใจ
+ ท้าทาย
+ อธิษฐาน
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file