Add 4 files, update 15 files
This commit is contained in:
1 parent
a97794acf8
commit
2db87a1227
19 files changed
+414
-33
No files matched your search
@@ -54,6 +54,10 @@ interface CommunicationRoom {
|
||||
studentId: string;
|
||||
teacherId: string;
|
||||
roomType: string;
|
||||
audienceType: string;
|
||||
escalationEligibleAt: string | null;
|
||||
escalatedAt: string | null;
|
||||
resolutionStatus: string;
|
||||
status: string;
|
||||
requestedBy: string;
|
||||
acceptedAt: string | null;
|
||||
@@ -532,6 +536,17 @@ function ChatArea({
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscalate = async () => {
|
||||
try {
|
||||
await apiPut(`/api/communication-rooms/${roomId}`, { action: 'escalate' });
|
||||
toast.success('Conversation escalated to administration');
|
||||
fetchRoom();
|
||||
onRefresh();
|
||||
} catch {
|
||||
toast.error('This conversation cannot be escalated yet');
|
||||
}
|
||||
};
|
||||
|
||||
// Voice recording with Web Audio API
|
||||
const startRecording = async () => {
|
||||
try {
|
||||
@@ -704,6 +719,11 @@ function ChatArea({
|
||||
const isStudent = room?.studentId === currentUserId;
|
||||
const otherPerson = isStudent ? room?.teacher : room?.student;
|
||||
const isAdmin = role === 'SCHOOL_ADMIN' || role === 'VICE_PRINCIPAL' || role === 'SUPER_ADMIN';
|
||||
const canEscalate = (role === 'STUDENT' || role === 'PARENT')
|
||||
&& room?.audienceType === 'direct'
|
||||
&& room?.resolutionStatus !== 'resolved'
|
||||
&& !room?.escalatedAt
|
||||
&& Boolean(room?.escalationEligibleAt && new Date(room.escalationEligibleAt) <= new Date());
|
||||
|
||||
const formatTime = (dateStr: string) => {
|
||||
const date = new Date(dateStr);
|
||||
@@ -766,12 +786,17 @@ function ChatArea({
|
||||
room?.status === 'active' ? t('communication.active') :
|
||||
t('communication.closed')}
|
||||
</Badge>
|
||||
{isAdmin && (
|
||||
{isAdmin && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
<Eye className="h-3 w-3 mr-1" />
|
||||
{t('communication.read_only')}
|
||||
</Badge>
|
||||
)}
|
||||
)}
|
||||
{room?.escalatedAt && (
|
||||
<Badge variant="outline" className="text-xs border-blue-300 text-blue-700">
|
||||
<Shield className="h-3 w-3 mr-1" />Escalated
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -788,7 +813,7 @@ function ChatArea({
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{role === 'TEACHER' && room?.status === 'active' && (
|
||||
{role === 'TEACHER' && room?.status === 'active' && (
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="h-8" onClick={() => setShowTeacherNotes(!showTeacherNotes)}>
|
||||
<StickyNote className="h-3.5 w-3.5 mr-1" />
|
||||
@@ -799,7 +824,12 @@ function ChatArea({
|
||||
{t('communication.close')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
)}
|
||||
{canEscalate && (
|
||||
<Button variant="outline" size="sm" className="h-8 border-amber-300 text-amber-700" onClick={handleEscalate}>
|
||||
<AlertTriangle className="h-3.5 w-3.5 mr-1" />Escalate
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ interface IllnessReport {
|
||||
parentApprovalStatus: string;
|
||||
parentApprovedBy: string | null;
|
||||
parentApprovedAt: string | null;
|
||||
leaveType: string;
|
||||
teacherNotifiedAt: string | null;
|
||||
adminApprovalStatus: string;
|
||||
adminApprovedAt: string | null;
|
||||
calendarEventId: string | null;
|
||||
isVisibleToTeacher: boolean;
|
||||
isVisibleToAdmin: boolean;
|
||||
status: string;
|
||||
@@ -334,6 +339,15 @@ function ReportCard({
|
||||
const [isApproving, setIsApproving] = useState(false);
|
||||
|
||||
const getStatusBadge = () => {
|
||||
if (report.adminApprovalStatus === 'approved') {
|
||||
return <Badge variant="outline" className="bg-emerald-50 text-emerald-700 border-emerald-300 dark:bg-emerald-950/30 dark:text-emerald-300 dark:border-emerald-800"><CheckCircle2 className="h-3 w-3 mr-1" />Approved and on calendar</Badge>;
|
||||
}
|
||||
if (report.adminApprovalStatus === 'rejected') {
|
||||
return <Badge variant="outline" className="bg-rose-50 text-rose-700 border-rose-300 dark:bg-rose-950/30 dark:text-rose-300 dark:border-rose-800"><XCircle className="h-3 w-3 mr-1" />Rejected by administration</Badge>;
|
||||
}
|
||||
if (report.parentApprovalStatus === 'approved') {
|
||||
return <Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-300 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-800"><Clock className="h-3 w-3 mr-1" />Awaiting administration</Badge>;
|
||||
}
|
||||
switch (report.parentApprovalStatus) {
|
||||
case 'pending':
|
||||
return <Badge variant="outline" className="bg-amber-50 text-amber-700 border-amber-300 dark:bg-amber-950/30 dark:text-amber-300 dark:border-amber-800"><Clock className="h-3 w-3 mr-1" />{t('illness.pending_approval')}</Badge>;
|
||||
@@ -381,6 +395,10 @@ function ReportCard({
|
||||
}
|
||||
};
|
||||
|
||||
const canAdminApprove = (role === 'SCHOOL_ADMIN' || role === 'SUPER_ADMIN')
|
||||
&& report.parentApprovalStatus === 'approved'
|
||||
&& report.adminApprovalStatus === 'pending';
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await apiDelete(`/api/illness-reports/${report.id}`);
|
||||
@@ -440,7 +458,8 @@ function ReportCard({
|
||||
<motion.div
|
||||
key={i}
|
||||
className={`h-1.5 flex-1 rounded-full ${
|
||||
report.parentApprovalStatus === 'approved' ? 'bg-emerald-400' :
|
||||
report.adminApprovalStatus === 'approved' ? 'bg-emerald-400' :
|
||||
report.parentApprovalStatus === 'approved' ? 'bg-blue-400' :
|
||||
report.parentApprovalStatus === 'pending' ? 'bg-amber-400' : 'bg-rose-400'
|
||||
}`}
|
||||
initial={{ scaleX: 0 }}
|
||||
@@ -481,6 +500,28 @@ function ReportCard({
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{canAdminApprove && (
|
||||
<>
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-emerald-500 hover:bg-emerald-600 text-white h-8"
|
||||
onClick={handleApprove}
|
||||
disabled={isApproving}
|
||||
>
|
||||
<CheckCircle2 className="h-3.5 w-3.5 mr-1" />
|
||||
Final approve
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 text-rose-600 border-rose-300 hover:bg-rose-50"
|
||||
onClick={handleReject}
|
||||
disabled={isApproving}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8" onClick={onToggle}>
|
||||
{expanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user