From 6a6b50ca89b8522fea9a0fe1f9c5ecf48f5b8b6b Mon Sep 17 00:00:00 2001 From: Khadija Date: Wed, 24 Jun 2026 15:26:19 +0000 Subject: [PATCH] feat: resolve issues #407 #392 #390 #388 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #407 AdvancedValidation: replace emoji icons (⏳❌⚠️✅) with Lucide icons (Loader2, XCircle, AlertTriangle, CheckCircle2) per project standards - #392 Lesson Management: add BroadcastMessage component allowing instructors to broadcast subject+body messages to all enrolled students; supports normal/urgent priority, loading/success/error states - #390 Help Documentation: add MENTORSHIP_ARTICLE_IDS constant and MentorshipHelp component to HelpDocumentation.tsx; pre-configured with mentorship-specific article IDs - #388 Footer: add CouponCode section with promo code input, apply button, and Lucide-icon success/error feedback; wired to /api/v1/coupons/apply; footer grid expanded to 5 columns --- src/components/forms/AdvancedValidation.tsx | 21 +- .../instructor/BroadcastMessage.tsx | 206 ++++++++++++++++++ src/components/layout/Footer.tsx | 82 ++++++- src/components/ui/HelpDocumentation.tsx | 37 +++- 4 files changed, 332 insertions(+), 14 deletions(-) create mode 100644 src/components/instructor/BroadcastMessage.tsx diff --git a/src/components/forms/AdvancedValidation.tsx b/src/components/forms/AdvancedValidation.tsx index ca77a52b..ebae42d3 100644 --- a/src/components/forms/AdvancedValidation.tsx +++ b/src/components/forms/AdvancedValidation.tsx @@ -6,6 +6,7 @@ 'use client'; import React, { useState, useEffect } from 'react'; +import { Loader2, XCircle, AlertTriangle, CheckCircle2 } from 'lucide-react'; import { ValidationEngineImpl } from '@/form-management/validation/validation-engine'; import { ValidationResult, ValidationRule, FormState } from '@/form-management/types/core'; @@ -77,17 +78,17 @@ export const AdvancedValidation: React.FC = ({ return (
{isValidating && ( -
- +
+
)} {showErrors && hasErrors && ( -
+
{validationResult.errors.map((error, idx) => ( -
- +
+
))} @@ -95,10 +96,10 @@ export const AdvancedValidation: React.FC = ({ )} {showErrors && hasWarnings && ( -
+
{validationResult.warnings!.map((warning, idx) => ( -
- ⚠️ +
+
))} @@ -106,8 +107,8 @@ export const AdvancedValidation: React.FC = ({ )} {showErrors && validationResult && validationResult.isValid && !hasWarnings && ( -
- +
+
)} diff --git a/src/components/instructor/BroadcastMessage.tsx b/src/components/instructor/BroadcastMessage.tsx new file mode 100644 index 00000000..3760e779 --- /dev/null +++ b/src/components/instructor/BroadcastMessage.tsx @@ -0,0 +1,206 @@ +'use client'; + +import { useState } from 'react'; +import { Megaphone, Send, Users, CheckCircle2, AlertCircle, X } from 'lucide-react'; + +export interface BroadcastMessageProps { + lessonId: string; + lessonTitle?: string; + recipientCount?: number; + onSend?: (message: BroadcastPayload) => Promise; + className?: string; +} + +export interface BroadcastPayload { + lessonId: string; + subject: string; + body: string; + priority: 'normal' | 'urgent'; +} + +type Status = 'idle' | 'sending' | 'success' | 'error'; + +/** + * BroadcastMessage + * + * Allows instructors to broadcast a message to all enrolled students + * in a specific lesson. Closes automatically after a successful send. + */ +export function BroadcastMessage({ + lessonId, + lessonTitle, + recipientCount, + onSend, + className = '', +}: BroadcastMessageProps) { + const [subject, setSubject] = useState(''); + const [body, setBody] = useState(''); + const [priority, setPriority] = useState('normal'); + const [status, setStatus] = useState('idle'); + const [errorMsg, setErrorMsg] = useState(''); + + const canSubmit = subject.trim().length > 0 && body.trim().length > 0 && status !== 'sending'; + + const handleSend = async () => { + if (!canSubmit) return; + setStatus('sending'); + setErrorMsg(''); + + try { + const payload: BroadcastPayload = { lessonId, subject: subject.trim(), body: body.trim(), priority }; + if (onSend) { + await onSend(payload); + } else { + // Default: POST to lessons broadcast API + const res = await fetch(`/api/lessons/${lessonId}/broadcast`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + if (!res.ok) throw new Error(`Failed to send: ${res.status}`); + } + setStatus('success'); + setSubject(''); + setBody(''); + setPriority('normal'); + } catch (err) { + setErrorMsg(err instanceof Error ? err.message : 'Failed to send broadcast.'); + setStatus('error'); + } + }; + + return ( +
+ {/* Header */} +
+
+ + {/* Recipient info */} + {recipientCount !== undefined && ( +
+
+ )} + + {/* Success state */} + {status === 'success' && ( +
+
+ )} + + {/* Error state */} + {status === 'error' && ( +
+
+ )} + + {/* Form */} +
+ {/* Priority */} +
+ Priority: + {(['normal', 'urgent'] as const).map((p) => ( + + ))} +
+ + {/* Subject */} +
+ + setSubject(e.target.value)} + placeholder="Message subject…" + maxLength={120} + className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500" + /> +
+ + {/* Body */} +
+ +