<title>Student Management System</title>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', 'Khmer OS', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 10px;
}
h2 {
color: #666;
text-align: center;
margin-bottom: 30px;
font-size: 18px;
}
h3 {
color: #333;
margin-top: 20px;
margin-bottom: 15px;
}
.controls {
display: flex;
gap: 5px;
margin-bottom: 10px;
flex-wrap: nowrap;
justify-content: center;
}
.btn {
padding: 5px 10px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 8px;
font-weight: bold;
transition: all 0.3s ease;
text-decoration: none;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
}
.btn-success {
background-color: #2ecc71;
color: white;
}
.btn-success:hover {
background-color: #27ae60;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(46, 204, 113, 0.4);
}
.btn-info {
background-color: #1abc9c;
color: white;
}
.btn-info:hover {
background-color: #16a085;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(26, 188, 156, 0.4);
}
.btn-warning {
background-color: #f39c12;
color: white;
}
.btn-warning:hover {
background-color: #d68910;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(243, 156, 18, 0.4);
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-danger:hover {
background-color: #c0392b;
}
#tableContainer {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 13px;
text-wrap: nowrap;
text-content: left;
}
table, th, td {
border: 1px solid #ddd;
}
th {
background-color: #34495e;
color: white;
padding: 12px 8px;
text-align: center;
font-weight: bold;
}
td {
padding: 10px 8px;
text-align: left;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
tr:hover {
background-color: #f0f0f0;
}
.edit-btn {
background-color: #e74c3c;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 8px;
}
.edit-btn:hover {
background-color: #c0392b;
}
.delete-btn {
background-color: #e74c3c;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 8px;
}
.delete-btn:hover {
background-color: #c0392b;
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
overflow-y: auto;
}
.modal-content {
background-color: white;
margin: 5% auto;
padding: 30px;
border-radius: 10px;
width: 90%;
max-width: 1200px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
max-height: 90vh;
overflow-y: auto;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: #000;
}
.report-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin: 20px 0;
}
.report-card {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.report-card h3 {
margin-top: 0;
color: #333;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.report-card canvas {
max-height: 300px;
}
#statistics {
padding: 15px;
background: white;
border-radius: 5px;
line-height: 2;
}
.stat-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.stat-label {
font-weight: bold;
color: #333;
}
.stat-value {
color: #3498db;
font-weight: bold;
}
#printReportBtn {
margin-top: 20px;
}
@media print {
body {
background: white;
}
.controls, .close {
display: none;
}
.report-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.container {
padding: 15px;
}
.controls {
flex-direction: column;
}
.btn {
width: 100%;
}
table {
font-size: 11px;
}
.report-grid {
grid-template-columns: 1fr;
}
}
</style>
ប្រព័ន្ធគ្រប់គ្រងព័ត៌មានសិស្ស
Student Management System
<div class="controls">
<button id="importBtn" class="btn btn-primary">📥 Import XLSX</button>
<button id="exportJsonBtn" class="btn btn-success">📤 Export JSON</button>
<button id="exportExcelBtn" class="btn btn-info">📊 Export Excel</button>
<button id="reportBtn" class="btn btn-warning">📈 View Report</button>
<input type="file" id="fileInput" accept=".xlsx,.xls" style="display:none;">
</div>
<div id="tableContainer">
<h3>Student List</h3>
<table id="studentTable">
<thead>
<tr>
<th>##</th>
<th>Student ID</th>
<th>Last Name</th>
<th>First Name</th>
<th>DOB</th>
<th>Age</th>
<th>Sex</th>
<th>Grade</th>
<th>Phone</th>
<th>Address</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody id="studentTableBody">
</tbody>
</table>
</div>
</div>
<!-- Report Modal -->
<div id="reportModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<h2>Student Report</h2>
<div class="report-grid">
<div class="report-card">
<h3>Gender Distribution</h3>
<canvas id="genderChart"></canvas>
</div>
<div class="report-card">
<h3>Students by Grade</h3>
<canvas id="gradeChart"></canvas>
</div>
<div class="report-card">
<h3>Students by Age</h3>
<canvas id="ageChart"></canvas>
</div>
<div class="report-card">
<h3>Male/Female by Grade</h3>
<canvas id="genderGradeChart"></canvas>
</div>
<div class="report-card">
<h3>Male/Female by Age</h3>
<canvas id="genderAgeChart"></canvas>
</div>
<div class="report-card">
<h3>Statistics</h3>
<div id="statistics"></div>
</div>
</div>
<button id="printReportBtn" class="btn btn-primary">🖨️ Print Report</button>
</div>
</div>
<script>
class StudentManager {
constructor() {
this.students = [];
this.charts = {};
this.initEventListeners();
this.loadFromLocalStorage();
}
initEventListeners() {
document.getElementById('importBtn').addEventListener('click', () => {
document.getElementById('fileInput').click();
});
document.getElementById('fileInput').addEventListener('change', (e) => {
this.importExcel(e);
});
document.getElementById('exportJsonBtn').addEventListener('click', () => {
this.exportJSON();
});
document.getElementById('exportExcelBtn').addEventListener('click', () => {
this.exportExcel();
});
document.getElementById('reportBtn').addEventListener('click', () => {
this.showReport();
});
document.querySelector('.close').addEventListener('click', () => {
document.getElementById('reportModal').style.display = 'none';
});
window.addEventListener('click', (event) => {
const modal = document.getElementById('reportModal');
if (event.target === modal) {
modal.style.display = 'none';
}
});
document.getElementById('printReportBtn').addEventListener('click', () => {
window.print();
});
}
importExcel(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' });
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const jsonData = XLSX.utils.sheet_to_json(worksheet);
this.students = jsonData.map((row, index) => ({
id: index + 1,
studentId: row['Student ID'] || row['studentId'] || '',
lastName: row['Last Name'] || row['lastName'] || '',
firstName: row['First Name'] || row['firstName'] || '',
dob: row['DOB'] || row['dob'] || '',
age: this.calculateAge(row['DOB'] || row['dob'] || ''),
sex: row['Sex'] || row['sex'] || '',
grade: row['Grade'] || row['grade'] || '',
phone: row['Phone'] || row['phoneNumber'] || '',
address: row['Address'] || row['fullAddress'] || '',
status: row['Status'] || row['status'] || 'Active'
}));
this.saveToLocalStorage();
this.renderTable();
alert('✅ Excel file imported successfully!');
} catch (error) {
alert('❌ Error importing file: ' + error.message);
console.error(error);
}
};
reader.readAsArrayBuffer(file);
}
calculateAge(dateString) {
if (!dateString) return 0;
const today = new Date();
let birthDate = new Date(dateString);
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
renderTable() {
const tbody = document.getElementById('studentTableBody');
tbody.innerHTML = '';
this.students.forEach((student, index) => {
const row = tbody.insertRow();
row.innerHTML = `
${index + 1}
${student.studentId}
${student.lastName}
${student.firstName}
${student.dob}
${student.age}
${student.sex}
${student.grade}
${student.phone}
${student.address}
${student.status}
Delete
Edit
`;
});
if (this.students.length === 0) {
const row = tbody.insertRow();
row.innerHTML = 'No students data. Import Excel file to get started.';
}
}
// បន្ថែម method editStudent ទៅក្នុង class StudentManager
editStudent(id) {
// ស្វែងរកនិស្សិតតាម id
const student = this.students.find(s => s.id === id);
if (!student) return;
// បង្ហាញ prompt សម្រាប់កែប្រែទិន្នន័យ (អាចប្តូរជា form modal ក្នុងពេលក្រោយ)
const newLastName = prompt("Enter Last Name:", student.lastName);
const newFirstName = prompt("Enter First Name:", student.firstName);
const newDob = prompt("Enter DOB (YYYY-MM-DD):", student.dob);
const newSex = prompt("Enter Sex:", student.sex);
const newGrade = prompt("Enter Grade:", student.grade);
const newPhone = prompt("Enter Phone:", student.phone);
const newAddress = prompt("Enter Address:", student.address);
const newStatus = prompt("Enter Status:", student.status);
// បន្ទាប់ពីកែប្រែ ប្តូរ data លើ object និស្សិត
student.lastName = newLastName || student.lastName;
student.firstName = newFirstName || student.firstName;
student.dob = newDob || student.dob;
student.age = this.calculateAge(newDob) || student.age;
student.sex = newSex || student.sex;
student.grade = newGrade || student.grade;
student.phone = newPhone || student.phone;
student.address = newAddress || student.address;
student.status = newStatus || student.status;
this.saveToLocalStorage();
this.renderTable();
}
deleteStudent(id) {
if (confirm('Are you sure you want to delete this student?')) {
this.students = this.students.filter(s => s.id !== id);
this.saveToLocalStorage();
this.renderTable();
}
}
exportJSON() {
const dataStr = JSON.stringify(this.students, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `students_${new Date().toISOString().split('T')[0]}.json`;
link.click();
URL.revokeObjectURL(url);
}
exportExcel() {
const ws = XLSX.utils.json_to_sheet(this.students);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Students');
XLSX.writeFile(wb, `students_${new Date().toISOString().split('T')[0]}.xlsx`);
}
showReport() {
document.getElementById('reportModal').style.display = 'block';
setTimeout(() => {
this.generateReports();
}, 100);
}
generateReports() {
this.createGenderChart();
this.createGradeChart();
this.createAgeChart();
this.createGenderGradeChart();
this.createGenderAgeChart();
this.createStatistics();
}
createGenderChart() {
const genderData = this.aggregateData('sex');
const ctx = document.getElementById('genderChart').getContext('2d');
if (this.charts.gender) {
this.charts.gender.destroy();
}
this.charts.gender = new Chart(ctx, {
type: 'pie',
data: {
labels: Object.keys(genderData),
datasets: [{
data: Object.values(genderData),
backgroundColor: ['#FF6B6B', '#4ECDC4', '#95E1D3'],
borderColor: ['#FF5252', '#45B7AA', '#7FD8BE'],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
createGradeChart() {
const gradeData = this.aggregateData('grade');
const ctx = document.getElementById('gradeChart').getContext('2d');
if (this.charts.grade) {
this.charts.grade.destroy();
}
this.charts.grade = new Chart(ctx, {
type: 'pie',
data: {
labels: Object.keys(gradeData),
datasets: [{
data: Object.values(gradeData),
backgroundColor: this.getRandomColors(Object.keys(gradeData).length),
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'bottom',
labels: {
font: { size: 10 }
}
}
}
}
});
}
createAgeChart() {
const ageData = this.aggregateData('age');
const sortedAges = Object.keys(ageData).sort((a, b) => a - b);
const sortedData = sortedAges.map(age => ageData[age]);
const ctx = document.getElementById('ageChart').getContext('2d');
if (this.charts.age) {
this.charts.age.destroy();
}
this.charts.age = new Chart(ctx, {
type: 'bar',
data: {
labels: sortedAges,
datasets: [{
label: 'Number of Students',
data: sortedData,
backgroundColor: '#3498db',
borderColor: '#2980b9',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
indexAxis: 'x',
plugins: {
legend: {
display: true
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
createGenderGradeChart() {
const gradeGenderData = this.createCrossTabulation('grade', 'sex');
const grades = Object.keys(gradeGenderData);
const genders = new Set();
grades.forEach(grade => {
Object.keys(gradeGenderData[grade]).forEach(gender => {
genders.add(gender);
});
});
const genderArray = Array.from(genders);
const datasets = genderArray.map((gender, index) => ({
label: gender,
data: grades.map(grade => gradeGenderData[grade][gender] || 0),
backgroundColor: this.getChartColors(index, genderArray.length),
borderWidth: 1
}));
const ctx = document.getElementById('genderGradeChart').getContext('2d');
if (this.charts.genderGrade) {
this.charts.genderGrade.destroy();
}
this.charts.genderGrade = new Chart(ctx, {
type: 'bar',
data: {
labels: grades,
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: {
stacked: false
},
y: {
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1
}
}
},
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
createGenderAgeChart() {
const ageGenderData = this.createCrossTabulation('age', 'sex');
const ages = Object.keys(ageGenderData).sort((a, b) => a - b);
const genders = new Set();
ages.forEach(age => {
Object.keys(ageGenderData[age]).forEach(gender => {
genders.add(gender);
});
});
const genderArray = Array.from(genders);
const datasets = genderArray.map((gender, index) => ({
label: gender,
data: ages.map(age => ageGenderData[age][gender] || 0),
backgroundColor: this.getChartColors(index, genderArray.length),
borderWidth: 1
}));
const ctx = document.getElementById('genderAgeChart').getContext('2d');
if (this.charts.genderAge) {
this.charts.genderAge.destroy();
}
this.charts.genderAge = new Chart(ctx, {
type: 'bar',
data: {
labels: ages,
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: {
stacked: false
},
y: {
stacked: false,
beginAtZero: true,
ticks: {
stepSize: 1
}
}
},
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
createStatistics() {
const stats = {
'Total Students': this.students.length,
'Males': this.students.filter(s => s.sex && s.sex.toLowerCase().includes('ប្រុស')).length,
'Females': this.students.filter(s => s.sex && s.sex.toLowerCase().includes('ស្រី')).length,
'Average Age': (this.students.reduce((sum, s) => sum + (s.age || 0), 0) / this.students.length).toFixed(1),
'Youngest': Math.min(...this.students.map(s => s.age || 999)),
'Oldest': Math.max(...this.students.map(s => s.age || 0)),
'Grades Count': Object.keys(this.aggregateData('grade')).length,
};
const statsDiv = document.getElementById('statistics');
statsDiv.innerHTML = Object.entries(stats).map(([key, value]) => `
${key}:
${value}
`).join('');
}
aggregateData(field) {
return this.students.reduce((acc, student) => {
const value = student[field] || 'Unknown';
acc[value] = (acc[value] || 0) + 1;
return acc;
}, {});
}
createCrossTabulation(field1, field2) {
return this.students.reduce((acc, student) => {
const val1 = student[field1] || 'Unknown';
const val2 = student[field2] || 'Unknown';
if (!acc[val1]) {
acc[val1] = {};
}
acc[val1][val2] = (acc[val1][val2] || 0) + 1;
return acc;
}, {});
}
getRandomColors(count) {
const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8',
'#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B88B', '#A8D8EA'
];
const result = [];
for (let i = 0; i < count; i++) {
result.push(colors[i % colors.length]);
}
return result;
}
getChartColors(index, total) {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8'];
return colors[index % colors.length];
}
saveToLocalStorage() {
localStorage.setItem('studentData', JSON.stringify(this.students));
}
loadFromLocalStorage() {
const data = localStorage.getItem('studentData');
if (data) {
this.students = JSON.parse(data);
this.renderTable();
}
}
}
// Initialize the application
const studentManager = new StudentManager();
</script>
<script src="app.js"></script>
<script type="module" crossorigin="" src="/assets/index-B5p-6V1M.js"></script>
បានឃើញ និងឯកភាព
ថ្ងៃព្រហស្បតិ៍ ១១កើត ខែមាឃ ឆ្នាំម្សាញ់ សប្តស័ក ព.ស ២៥៦៩
រោគ ថ្ងៃទី២៩ ខែមករា ឆ្នាំ២០២៦
នាយិកា
ថ្ងៃព្រហស្បតិ៍ ១១កើត ខែមាឃ ឆ្នាំម្សាញ់ សប្តស័ក ព.ស ២៥៦៩
រោគ ថ្ងៃទី២៩ ខែមករា ឆ្នាំ២០២៦
អ្នកធ្វើតារាង
<title>id</title>
##
Student ID
Last Name
First Name
DOB
Age
Sex
Grade
Phone
Address
Status
Extra
Action
1
1860987
ក្តាម
កន
17/09/2013
12
ប្រុស
6A
097...
ភ្នំពេញ
Active
ក្រ១
Edit
Delete
<script>
function editRow(button) {
const row = button.closest("tr");
const cells = row.querySelectorAll("td");
// Example: replace DOB with date input
cells[4].innerHTML = ``;
// Age as number
cells[5].innerHTML = ``;
// Sex dropdown
cells[6].innerHTML = `
ប្រុស
ស្រី
`;
// Grade dropdown
cells[7].innerHTML = `
1A
2B
3A
4B
5A
6B
HL
1B
2A
3B
4A
5B
6A
`;
// Phone
cells[8].innerHTML = ``;
// Address editable
cells[9].setAttribute("contenteditable","true");
// Status dropdown
cells[10].innerHTML = `
Active
Inactive
`;
// Extra dropdown (example: ក្រ១, ក្រ២, ផ្សេង)
cells[11].innerHTML = `
ក្រ១
ក្រ២
ផ្សេងៗ
`;
// Replace action buttons
cells[12].innerHTML = `
Save
Cancel
`;
}
function saveRow(button) {
const row = button.closest("tr");
const inputs = row.querySelectorAll("input, select");
inputs.forEach(input => {
const cell = input.closest("td");
cell.innerText = input.value;
});
// Restore action buttons
const actionCell = row.querySelectorAll("td")[12];
actionCell.innerHTML = `
Edit
Delete
`;
}
function cancelEdit(button) {
// For simplicity, just reload page or re-render row from stored data
alert("Cancel clicked — restore original values here.");
}
function deleteRow(button) {
const row = button.closest("tr");
row.remove();
}
</script>