Files
rikkei_simple_care/management/src/components/WorkspaceSeatingChart.tsx
PhuocNTB d7a49d2b5c
All checks were successful
Deploy on Master Change / deploy (push) Successful in 1m1s
fix
2026-07-06 09:25:16 +07:00

682 lines
27 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState, useMemo } from 'react';
import type { SeatingTemplate } from './SeatingTemplatesSection';
import { StudentAvatar } from './StudentAvatar';
interface WorkspaceSeatingChartProps {
workspaceId: string; // 'class-<id>' or 'exam-<id>'
students: {
rkId: number;
fullName: string;
studentCode: string;
avatar?: string;
}[];
onlineIds: number[];
onStudentClick?: (studentRkId: number) => void;
}
interface SeatingLayout {
rows: number;
cols: number;
boardPosition: 'top' | 'bottom' | 'left' | 'right';
lockedSeats: string[]; // "row,col"
seats: Record<string, number>; // "row,col" -> studentRkId
}
export const WorkspaceSeatingChart: React.FC<WorkspaceSeatingChartProps> = ({
workspaceId,
students,
onlineIds,
onStudentClick,
}) => {
const [layout, setLayout] = useState<SeatingLayout | null>(null);
const [templates, setTemplates] = useState<SeatingTemplate[]>([]);
const [searchQuery, setSearchQuery] = useState('');
// Custom setup states
const [setupMode, setSetupMode] = useState<'template' | 'custom'>('template');
const [customRows, setCustomRows] = useState(5);
const [customCols, setCustomCols] = useState(6);
const [customBoard, setCustomBoard] = useState<'top' | 'bottom' | 'left' | 'right'>('top');
const [selectedTemplateId, setSelectedTemplateId] = useState('');
// Edit mode for structural changes
const [isEditingStructure, setIsEditingStructure] = useState(false);
const [dragOverCell, setDragOverCell] = useState<string | null>(null);
const [saveNotice, setSaveNotice] = useState<string | null>(null);
// Load templates and layout
useEffect(() => {
// Load templates
const storedTemplates = localStorage.getItem('sc_seating_templates');
if (storedTemplates) {
try {
setTemplates(JSON.parse(storedTemplates));
} catch {
setTemplates([]);
}
}
// Load layout for this room
const storedLayout = localStorage.getItem(`sc_seating_layout_${workspaceId}`);
if (storedLayout) {
try {
setLayout(JSON.parse(storedLayout));
} catch {
setLayout(null);
}
}
}, [workspaceId]);
// Save layout
const saveLayout = (newLayout: SeatingLayout | null) => {
if (newLayout) {
localStorage.setItem(`sc_seating_layout_${workspaceId}`, JSON.stringify(newLayout));
setSaveNotice('Đã lưu thay đổi sơ đồ chỗ ngồi!');
} else {
localStorage.removeItem(`sc_seating_layout_${workspaceId}`);
setSaveNotice('Đã xóa sơ đồ chỗ ngồi!');
}
setLayout(newLayout);
};
useEffect(() => {
if (saveNotice) {
const timer = setTimeout(() => {
setSaveNotice(null);
}, 2000);
return () => clearTimeout(timer);
}
}, [saveNotice]);
// Get active seated list
const seatedStudentIds = useMemo(() => {
if (!layout) return new Set<number>();
return new Set<number>(Object.values(layout.seats));
}, [layout]);
// Filter unseated students list
const unseatedStudents = useMemo(() => {
return students.filter((s) => !seatedStudentIds.has(s.rkId));
}, [students, seatedStudentIds]);
// Search unseated students
const filteredUnseatedStudents = useMemo(() => {
return unseatedStudents.filter(
(s) =>
s.fullName.toLowerCase().includes(searchQuery.toLowerCase()) ||
s.studentCode.toLowerCase().includes(searchQuery.toLowerCase())
);
}, [unseatedStudents, searchQuery]);
// Initialize with Template
const handleApplyTemplate = () => {
const tpl = templates.find((t) => t.id === selectedTemplateId);
if (!tpl) {
alert('Vui lòng chọn sơ đồ mẫu.');
return;
}
const newLayout: SeatingLayout = {
rows: tpl.rows,
cols: tpl.cols,
boardPosition: tpl.boardPosition,
lockedSeats: [...tpl.lockedSeats],
seats: {},
};
saveLayout(newLayout);
};
// Initialize with Custom values
const handleApplyCustom = () => {
const newLayout: SeatingLayout = {
rows: customRows,
cols: customCols,
boardPosition: customBoard,
lockedSeats: [],
seats: {},
};
saveLayout(newLayout);
};
// Drag and drop handlers
const handleDragStart = (e: React.DragEvent, source: string) => {
e.dataTransfer.setData('text/plain', source);
};
const handleDragOver = (e: React.DragEvent, cellKey: string) => {
e.preventDefault();
if (dragOverCell !== cellKey) {
setDragOverCell(cellKey);
}
};
const handleDragLeave = () => {
setDragOverCell(null);
};
const handleDrop = (e: React.DragEvent, targetRow: number, targetCol: number) => {
e.preventDefault();
setDragOverCell(null);
if (!layout) return;
const source = e.dataTransfer.getData('text/plain');
if (!source) return;
const targetKey = `${targetRow},${targetCol}`;
if (layout.lockedSeats.includes(targetKey)) return;
const newSeats = { ...layout.seats };
const targetStudentId = newSeats[targetKey];
if (source.startsWith('unseated:')) {
const studentId = Number(source.replace('unseated:', ''));
// Place in target cell
newSeats[targetKey] = studentId;
// If target had a student, B is kicked to unseated automatically (since they are no longer in newSeats)
// Clean up B from any other slots (shouldn't exist, but safety)
Object.keys(newSeats).forEach((key) => {
if (key !== targetKey && newSeats[key] === studentId) {
delete newSeats[key];
}
});
} else if (source.startsWith('cell:')) {
const sourceKey = source.replace('cell:', '');
const studentId = newSeats[sourceKey];
if (!studentId) return;
if (targetStudentId) {
// Swap students
newSeats[targetKey] = studentId;
newSeats[sourceKey] = targetStudentId;
} else {
// Move to empty cell
newSeats[targetKey] = studentId;
delete newSeats[sourceKey];
}
}
saveLayout({ ...layout, seats: newSeats });
};
// Unseat student
const handleUnseat = (cellKey: string) => {
if (!layout) return;
const newSeats = { ...layout.seats };
delete newSeats[cellKey];
saveLayout({ ...layout, seats: newSeats });
};
// Auto assign random
const handleAutoAssign = () => {
if (!layout) return;
// Find all empty, unlocked cells
const emptyCells: string[] = [];
for (let r = 0; r < layout.rows; r++) {
for (let c = 0; c < layout.cols; c++) {
const key = `${r},${c}`;
if (!layout.lockedSeats.includes(key) && !layout.seats[key]) {
emptyCells.push(key);
}
}
}
if (unseatedStudents.length > emptyCells.length) {
alert(`Không đủ chỗ ngồi cho tất cả học viên! Số học viên chưa có chỗ: ${unseatedStudents.length}, Số chỗ trống khả dụng: ${emptyCells.length}. Thiếu ${unseatedStudents.length - emptyCells.length} chỗ.`);
}
// Shuffle empty cells
const shuffledCells = [...emptyCells].sort(() => Math.random() - 0.5);
const newSeats = { ...layout.seats };
const assignCount = Math.min(unseatedStudents.length, shuffledCells.length);
for (let i = 0; i < assignCount; i++) {
newSeats[shuffledCells[i]] = unseatedStudents[i].rkId;
}
saveLayout({ ...layout, seats: newSeats });
};
// Clear all seats
const handleClearAllSeats = () => {
if (!layout) return;
if (confirm('Bạn có chắc chắn muốn giải tán tất cả chỗ ngồi?')) {
saveLayout({ ...layout, seats: {} });
}
};
// Reset entire layout configuration
const handleResetLayout = () => {
if (confirm('Lưu ý: Xóa sơ đồ sẽ xóa toàn bộ vị trí chỗ ngồi hiện tại. Bạn có chắc chắn muốn thực hiện?')) {
saveLayout(null);
setIsEditingStructure(false);
}
};
// Toggle cell lock dynamically
const handleToggleCellLock = (r: number, c: number) => {
if (!layout) return;
const key = `${r},${c}`;
let newLocked = [...layout.lockedSeats];
let newSeats = { ...layout.seats };
if (newLocked.includes(key)) {
newLocked = newLocked.filter((k) => k !== key);
} else {
newLocked.push(key);
delete newSeats[key]; // remove student if locked
}
saveLayout({
...layout,
lockedSeats: newLocked,
seats: newSeats,
});
};
// Render Seating Grid
const renderSeatingGrid = () => {
if (!layout) return null;
const gridItems = [];
for (let r = 0; r < layout.rows; r++) {
for (let c = 0; c < layout.cols; c++) {
const key = `${r},${c}`;
const isLocked = layout.lockedSeats.includes(key);
const studentId = layout.seats[key];
const student = studentId ? students.find((s) => s.rkId === studentId) : null;
const isOnline = student ? onlineIds.includes(student.rkId) : false;
const isHovered = dragOverCell === key;
gridItems.push(
<div
key={key}
onDragOver={(e) => !isLocked && handleDragOver(e, key)}
onDragLeave={handleDragLeave}
onDrop={(e) => !isLocked && handleDrop(e, r, c)}
onClick={() => isEditingStructure && handleToggleCellLock(r, c)}
className={`seating-grid-cell ${isLocked ? 'locked' : ''} ${isHovered ? 'dragover' : ''}`}
style={{
minHeight: '80px',
border: isEditingStructure ? '1.5px dashed var(--border-color)' : '1px solid var(--border-color)',
borderRadius: '8px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
padding: '0.4rem',
position: 'relative',
backgroundColor: isLocked
? '#e5e7eb'
: isHovered
? '#dbeafe'
: student
? '#ffffff'
: '#f9fafb',
color: isLocked ? '#9ca3af' : 'inherit',
cursor: isEditingStructure ? 'pointer' : student ? 'grab' : 'default',
boxShadow: student ? 'var(--shadow-sm)' : 'none',
transition: 'var(--transition)',
borderColor: isHovered ? 'var(--accent)' : isLocked ? '#e5e7eb' : 'var(--border-color)',
}}
draggable={!!student && !isEditingStructure}
onDragStart={(e) => student && handleDragStart(e, `cell:${key}`)}
>
{isLocked ? (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }}>
<span style={{ fontSize: '1rem' }}>🔒</span>
<span style={{ fontSize: '0.72rem', color: '#9ca3af', fontWeight: 600 }}>Khóa</span>
</div>
) : student ? (
<div
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', width: '100%', cursor: (!isEditingStructure && onStudentClick) ? 'pointer' : 'inherit' }}
onClick={() => {
if (!isEditingStructure && onStudentClick) {
onStudentClick(student.rkId);
}
}}
>
{!isEditingStructure && (
<button
onClick={(e) => { e.stopPropagation(); handleUnseat(key); }}
style={{
position: 'absolute',
top: '2px',
right: '2px',
border: 'none',
background: '#fee2e2',
color: 'var(--danger)',
width: '18px',
height: '18px',
borderRadius: '50%',
fontSize: '0.65rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
fontWeight: 700,
}}
title="Cho học viên rời chỗ"
>
&times;
</button>
)}
<StudentAvatar fullName={student.fullName} avatar={student.avatar} isOnline={isOnline} size={32} />
<span style={{ fontSize: '0.75rem', fontWeight: 700, marginTop: '4px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', width: '100%', padding: '0 2px' }}>
{student.fullName.split(' ').pop()}
</span>
<span style={{ fontSize: '0.65rem', color: 'var(--text-secondary)', fontFamily: 'monospace' }}>
{student.studentCode}
</span>
</div>
) : (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', opacity: 0.4 }}>
<span style={{ fontSize: '0.72rem', color: 'var(--text-muted)', fontWeight: 600 }}>
H{r + 1}-C{c + 1}
</span>
<span style={{ fontSize: '0.65rem', color: 'var(--text-muted)' }}>Trống</span>
</div>
)}
</div>
);
}
}
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', flex: 1, minWidth: 0, minHeight: 0 }}>
{/* Board Top */}
<div style={{ display: 'flex', justifyContent: 'center' }}>
{layout.boardPosition === 'top' && (
<div className="seating-board-label" style={{ width: '50%', textAlign: 'center', background: '#4b5563', color: '#fff', padding: '0.35rem 0.5rem', borderRadius: '6px', fontSize: '0.85rem', fontWeight: 700 }}>
📢 BẢNG GIẢNG ĐƯNG
</div>
)}
</div>
<div style={{ display: 'flex', alignItems: 'stretch', gap: '0.75rem', flex: 1, minHeight: 0 }}>
{/* Board Left */}
{layout.boardPosition === 'left' && (
<div className="seating-board-label" style={{ writingMode: 'vertical-lr', textOrientation: 'mixed', textAlign: 'center', background: '#4b5563', color: '#fff', padding: '0.5rem 0.35rem', borderRadius: '6px', fontSize: '0.85rem', fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
📢 BẢNG GIẢNG ĐƯNG
</div>
)}
{/* Grid Container */}
<div
style={{
display: 'grid',
gridTemplateColumns: `repeat(${layout.cols}, minmax(75px, 1fr))`,
gap: '8px',
flex: 1,
padding: '8px',
backgroundColor: 'var(--bg-app)',
borderRadius: 'var(--radius-md)',
border: '1px solid var(--border-color)',
alignContent: 'start',
overflow: 'auto',
minHeight: 0,
}}
>
{gridItems}
</div>
{/* Board Right */}
{layout.boardPosition === 'right' && (
<div className="seating-board-label" style={{ writingMode: 'vertical-lr', textOrientation: 'mixed', textAlign: 'center', background: '#4b5563', color: '#fff', padding: '0.5rem 0.35rem', borderRadius: '6px', fontSize: '0.85rem', fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
📢 BẢNG GIẢNG ĐƯNG
</div>
)}
</div>
{/* Board Bottom */}
<div style={{ display: 'flex', justifyContent: 'center' }}>
{layout.boardPosition === 'bottom' && (
<div className="seating-board-label" style={{ width: '50%', textAlign: 'center', background: '#4b5563', color: '#fff', padding: '0.35rem 0.5rem', borderRadius: '6px', fontSize: '0.85rem', fontWeight: 700 }}>
📢 BẢNG GIẢNG ĐƯNG
</div>
)}
</div>
</div>
);
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', flex: 1, minHeight: 0, height: '100%', overflow: 'hidden' }}>
{!layout ? (
// Empty state: select template or customize
<div className="empty-state" style={{ minHeight: '350px', padding: '2rem', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
<div className="empty-state-icon" style={{ fontSize: '3rem', marginBottom: '1rem' }}>🪑</div>
<h2>Chưa thiết lập đ chỗ ngồi</h2>
<p style={{ color: 'var(--text-secondary)', maxWidth: '450px', textAlign: 'center', marginBottom: '1.5rem', fontSize: '0.9rem' }}>
Thiết lập nhanh đ từ mẫu lớp học sẵn hoặc tạo tự do kích thước mong muốn đ xếp vị trí cho sinh viên.
</p>
<div style={{ display: 'flex', gap: '1rem', width: '100%', maxWidth: '600px', justifyContent: 'center' }}>
<div className="content-card" style={{ flex: 1, padding: '1.25rem', display: 'flex', flexDirection: 'column', gap: '0.75rem', border: setupMode === 'template' ? '2px solid var(--accent)' : '1px solid var(--border-color)' }} onClick={() => setSetupMode('template')}>
<h4 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.9rem', cursor: 'pointer' }}>
<input type="radio" checked={setupMode === 'template'} readOnly />
Chọn từ đ mẫu
</h4>
<select
className="select-filter"
style={{ width: '100%', padding: '0.4rem', fontSize: '0.8rem' }}
value={selectedTemplateId}
onChange={(e) => setSelectedTemplateId(e.target.value)}
disabled={setupMode !== 'template'}
>
<option value="">-- Chọn đ mẫu --</option>
{templates.map((t) => (
<option key={t.id} value={t.id}>
{t.name} ({t.rows}x{t.cols})
</option>
))}
</select>
<button className="btn btn-primary btn-sm" style={{ marginTop: 'auto' }} onClick={handleApplyTemplate} disabled={setupMode !== 'template'}>
Áp dụng mẫu
</button>
</div>
<div className="content-card" style={{ flex: 1, padding: '1.25rem', display: 'flex', flexDirection: 'column', gap: '0.75rem', border: setupMode === 'custom' ? '2px solid var(--accent)' : '1px solid var(--border-color)' }} onClick={() => setSetupMode('custom')}>
<h4 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.9rem', cursor: 'pointer' }}>
<input type="radio" checked={setupMode === 'custom'} readOnly />
Tạo tự do tùy ý
</h4>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
<input
type="number"
placeholder="Hàng"
min={1}
max={10}
className="search-input"
style={{ padding: '0.4rem', fontSize: '0.8rem' }}
value={customRows}
onChange={(e) => setCustomRows(Math.max(1, Number(e.target.value)))}
disabled={setupMode !== 'custom'}
title="Số hàng"
/>
<input
type="number"
placeholder="Cột"
min={1}
max={15}
className="search-input"
style={{ padding: '0.4rem', fontSize: '0.8rem' }}
value={customCols}
onChange={(e) => setCustomCols(Math.max(1, Number(e.target.value)))}
disabled={setupMode !== 'custom'}
title="Số cột"
/>
</div>
<select
className="select-filter"
style={{ width: '100%', padding: '0.4rem', fontSize: '0.8rem' }}
value={customBoard}
onChange={(e) => setCustomBoard(e.target.value as any)}
disabled={setupMode !== 'custom'}
>
<option value="top">Bảng TOP</option>
<option value="bottom">Bảng BOTTOM</option>
<option value="left">Bảng LEFT</option>
<option value="right">Bảng RIGHT</option>
</select>
<button className="btn btn-primary btn-sm" style={{ marginTop: 'auto' }} onClick={handleApplyCustom} disabled={setupMode !== 'custom'}>
Tạo đ trống
</button>
</div>
</div>
</div>
) : (
// Seating Workspace View
<div className="seating-workspace-grid">
{/* Main Seating Panel */}
<div className="seating-workspace-main">
{/* Toolbar */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '10px' }}>
<div style={{ display: 'flex', gap: '8px' }}>
<button className="btn btn-primary btn-sm" onClick={handleAutoAssign}>
Xếp ngẫu nhiên
</button>
<button className="btn btn-secondary btn-sm" onClick={handleClearAllSeats}>
🗑 Giải tán chỗ
</button>
</div>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<button
className={`btn btn-sm ${isEditingStructure ? 'btn-primary' : 'btn-secondary'}`}
onClick={() => setIsEditingStructure(!isEditingStructure)}
style={{
backgroundColor: isEditingStructure ? 'var(--accent)' : '',
borderColor: isEditingStructure ? 'var(--accent)' : '',
color: isEditingStructure ? '#fff' : '',
}}
>
{isEditingStructure ? '✔️ Xong thiết lập' : '⚙️ Khóa/Mở ô chỗ'}
</button>
<button className="btn btn-secondary btn-sm text-danger" style={{ borderColor: '#fca5a5' }} onClick={handleResetLayout}>
Reset đ
</button>
</div>
</div>
{isEditingStructure && (
<div style={{ backgroundColor: '#fffbeb', border: '1px solid #fef3c7', padding: '0.65rem 1rem', borderRadius: '8px', color: '#b45309', fontSize: '0.8rem', fontWeight: 600 }}>
💡 Đang chế đ chỉnh sửa: Click chuột vào các ô trong đ đ thay đi khóa/mở các vị trí.
</div>
)}
{/* Render Grid */}
{renderSeatingGrid()}
</div>
{/* Right Panel: Unseated Students list */}
<div className="seating-workspace-sidebar">
<div>
<h4 style={{ margin: 0, fontSize: '0.95rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>Chưa vào chỗ</span>
<span className="badge" style={{ background: 'var(--accent-light)', color: 'var(--accent)', fontSize: '0.75rem', fontWeight: 700, padding: '0.15rem 0.4rem', borderRadius: '10px' }}>
{unseatedStudents.length} SV
</span>
</h4>
<p style={{ margin: '4px 0 0 0', color: 'var(--text-secondary)', fontSize: '0.78rem' }}>
Kéo thả sinh viên bên dưới vào vị trí trống trên đ.
</p>
</div>
<input
type="search"
placeholder="Tìm tên, mã sinh viên..."
className="search-input"
style={{ width: '100%', padding: '0.4rem 0.6rem', fontSize: '0.8rem' }}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<div
style={{
flex: 1,
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
gap: '8px',
paddingRight: '4px',
minHeight: '300px',
}}
>
{filteredUnseatedStudents.length === 0 ? (
<div style={{ textAlign: 'center', padding: '2rem 0', color: 'var(--text-muted)', fontSize: '0.8rem' }}>
Không tìm thấy sinh viên nào.
</div>
) : (
filteredUnseatedStudents.map((s) => {
const isOnline = onlineIds.includes(s.rkId);
return (
<div
key={s.rkId}
draggable
onDragStart={(e) => handleDragStart(e, `unseated:${s.rkId}`)}
onClick={() => onStudentClick && onStudentClick(s.rkId)}
style={{
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
padding: '0.5rem',
border: '1px solid var(--border-color)',
borderRadius: '8px',
backgroundColor: '#ffffff',
cursor: onStudentClick ? 'pointer' : 'grab',
transition: 'var(--transition)',
userSelect: 'none',
boxShadow: 'var(--shadow-sm)',
}}
className="student-drag-item"
>
<StudentAvatar fullName={s.fullName} avatar={s.avatar} isOnline={isOnline} size={28} />
<div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden', flex: 1 }}>
<span style={{ fontSize: '0.8rem', fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{s.fullName}
</span>
<span style={{ fontSize: '0.7rem', color: 'var(--text-secondary)', fontFamily: 'monospace' }}>
{s.studentCode}
</span>
</div>
<span className={isOnline ? 'pulse-dot-online' : 'status-badge-offline'} style={{ width: '6px', height: '6px' }} />
</div>
);
})
)}
</div>
</div>
</div>
)}
{saveNotice && (
<div style={{
position: 'fixed',
bottom: '24px',
right: '24px',
background: '#10b981',
color: '#fff',
padding: '0.65rem 1.25rem',
borderRadius: '8px',
boxShadow: 'var(--shadow-lg)',
fontSize: '0.85rem',
fontWeight: 600,
zIndex: 1000,
display: 'flex',
alignItems: 'center',
gap: '8px',
}}>
<span style={{ fontSize: '1.1rem' }}></span> {saveNotice}
</div>
)}
</div>
);
};