feat: teacer user
This commit is contained in:
parent
5db88aa8c5
commit
c7c2a6221d
26
app/Http/Controllers/NodeJS/NodeJSController.php
Normal file
26
app/Http/Controllers/NodeJS/NodeJSController.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class NodeJSController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function dashboardStudent() {
|
||||
$topicCount = DB::table('projects')->count();
|
||||
return view('dashboard_student', compact('topicCountt'));
|
||||
}
|
||||
|
||||
public function dashboardTeacher() {
|
||||
$topicCount = DB::table('projects')->count();
|
||||
return view('dashboard_teacher', compact('topicCount'));
|
||||
}
|
||||
|
||||
}
|
||||
95
app/Http/Controllers/NodeJS/Teacher/DashboardController.php
Normal file
95
app/Http/Controllers/NodeJS/Teacher/DashboardController.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
namespace App\Http\Controllers\NodeJS\Teacher;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
// Statistik yang sudah ada
|
||||
$totalProyek = DB::table('projects')->count();
|
||||
|
||||
$totalSiswa = DB::table('users')
|
||||
->where('role', 'student')
|
||||
->count();
|
||||
|
||||
// Statistik baru
|
||||
|
||||
// 1. Hitung submission yang selesai (status 'completed' atau yang sesuai)
|
||||
$submissionSelesai = DB::table('submissions')
|
||||
->where('status', 'completed')
|
||||
->count();
|
||||
|
||||
// 2. Hitung siswa yang telah mengirimkan minimal satu proyek
|
||||
$siswaSubmisi = DB::table('submissions')
|
||||
->join('users', 'users.id', '=', 'submissions.user_id')
|
||||
->where('users.role', 'student')
|
||||
->distinct('submissions.user_id')
|
||||
->count('submissions.user_id');
|
||||
|
||||
// 3. Hitung total submission
|
||||
$totalSubmisi = DB::table('submissions')->count();
|
||||
|
||||
// 4. Hitung submission yang gagal (status 'failed')
|
||||
$submissionGagal = DB::table('submissions')
|
||||
->where('status', 'failed')
|
||||
->count();
|
||||
|
||||
// 5. Hitung tingkat keberhasilan (submission selesai vs total submission)
|
||||
$tingkatKeberhasilan = $totalSubmisi > 0
|
||||
? round(($submissionSelesai / $totalSubmisi) * 100)
|
||||
: 0;
|
||||
|
||||
// 6. Hitung tingkat kegagalan (submission gagal vs total submission)
|
||||
$tingkatKegagalan = $totalSubmisi > 0
|
||||
? round(($submissionGagal / $totalSubmisi) * 100)
|
||||
: 0;
|
||||
|
||||
// 7. Hitung proyek dengan minimal satu submission
|
||||
$proyekDenganSubmisi = DB::table('submissions')
|
||||
->distinct('project_id')
|
||||
->count('project_id');
|
||||
|
||||
// 8. Hitung proyek tanpa submission
|
||||
$proyekTanpaSubmisi = $totalProyek - $proyekDenganSubmisi;
|
||||
|
||||
// 9. Hitung persentase siswa yang telah membuat submission
|
||||
$tingkatPartisipasiSiswa = $totalSiswa > 0
|
||||
? round(($siswaSubmisi / $totalSiswa) * 100)
|
||||
: 0;
|
||||
|
||||
// 10. Hitung submission yang sedang diproses
|
||||
$submisiDalamProses = DB::table('submissions')
|
||||
->where('status', 'processing')
|
||||
->count();
|
||||
|
||||
// 11. Hitung submission yang tertunda
|
||||
$submisiTertunda = DB::table('submissions')
|
||||
->where('status', 'pending')
|
||||
->count();
|
||||
|
||||
// 12. Hitung rata-rata percobaan per submission
|
||||
$ratarataPecobaan = DB::table('submissions')
|
||||
->avg('attempts');
|
||||
$ratarataPecobaan = round($ratarataPecobaan, 1); // Bulatkan ke 1 angka desimal
|
||||
|
||||
return view('nodejs.dashboard-teacher.index', compact(
|
||||
'totalProyek',
|
||||
'totalSiswa',
|
||||
'submissionSelesai',
|
||||
'totalSubmisi',
|
||||
'tingkatPartisipasiSiswa',
|
||||
'submisiDalamProses',
|
||||
'submisiTertunda',
|
||||
'proyekDenganSubmisi',
|
||||
'proyekTanpaSubmisi',
|
||||
'siswaSubmisi',
|
||||
'ratarataPecobaan',
|
||||
'tingkatKeberhasilan',
|
||||
'submissionGagal',
|
||||
'tingkatKegagalan'
|
||||
));
|
||||
}
|
||||
}
|
||||
233
app/Http/Controllers/NodeJS/Teacher/RankController.php
Normal file
233
app/Http/Controllers/NodeJS/Teacher/RankController.php
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Teacher;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RankController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the ranking page
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('nodejs.rank-teacher.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ranking data for a specific project
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getRankingByProject(Request $request)
|
||||
{
|
||||
$projectId = $request->input('project_id');
|
||||
|
||||
if (!$projectId) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Project ID is required'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Get all submissions for this project
|
||||
$submissions = DB::table('submissions')
|
||||
->join('users', 'submissions.user_id', '=', 'users.id')
|
||||
->where('submissions.project_id', $projectId)
|
||||
->select(
|
||||
'submissions.id',
|
||||
'submissions.user_id',
|
||||
'users.name as user_name',
|
||||
'submissions.attempts',
|
||||
'submissions.results',
|
||||
'submissions.updated_at'
|
||||
)
|
||||
->get();
|
||||
|
||||
// Process the submissions to calculate scores
|
||||
$rankings = [];
|
||||
$processedUsers = [];
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$userId = $submission->user_id;
|
||||
|
||||
// Parse the results JSON
|
||||
$resultsData = json_decode($submission->results, true);
|
||||
|
||||
if (!$resultsData) {
|
||||
continue; // Skip invalid results
|
||||
}
|
||||
|
||||
// Calculate total tests and passed tests
|
||||
$totalTests = 0;
|
||||
$passedTests = 0;
|
||||
|
||||
foreach ($resultsData as $testSuite) {
|
||||
if (isset($testSuite['testResults'])) {
|
||||
foreach ($testSuite['testResults'] as $result) {
|
||||
if (isset($result['totalTests']) && isset($result['passedTests'])) {
|
||||
$totalTests += $result['totalTests'];
|
||||
$passedTests += $result['passedTests'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate percentage score
|
||||
$score = $totalTests > 0 ? ($passedTests / $totalTests) * 100 : 0;
|
||||
$score = round($score, 2); // Round to 2 decimal places
|
||||
|
||||
// If we already processed this user, only keep the higher score
|
||||
if (isset($processedUsers[$userId])) {
|
||||
if ($score > $processedUsers[$userId]['score']) {
|
||||
$processedUsers[$userId] = [
|
||||
'user_id' => $userId,
|
||||
'user_name' => $submission->user_name,
|
||||
'attempts' => $submission->attempts,
|
||||
'score' => $score,
|
||||
'total_tests' => $totalTests,
|
||||
'passed_tests' => $passedTests,
|
||||
'submission_date' => $submission->updated_at,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$processedUsers[$userId] = [
|
||||
'user_id' => $userId,
|
||||
'user_name' => $submission->user_name,
|
||||
'attempts' => $submission->attempts,
|
||||
'score' => $score,
|
||||
'total_tests' => $totalTests,
|
||||
'passed_tests' => $passedTests,
|
||||
'submission_date' => $submission->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to array and sort by score (descending)
|
||||
$rankings = array_values($processedUsers);
|
||||
usort($rankings, function($a, $b) {
|
||||
if ($a['score'] == $b['score']) {
|
||||
// If scores are equal, sort by submission date (earlier is better)
|
||||
return strtotime($a['submission_date']) - strtotime($b['submission_date']);
|
||||
}
|
||||
return $b['score'] - $a['score'];
|
||||
});
|
||||
|
||||
// Add rank information
|
||||
$rank = 1;
|
||||
foreach ($rankings as $key => $value) {
|
||||
$rankings[$key]['rank'] = $rank++;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'rankings' => $rankings
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects for the ranking dropdown
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getProjects()
|
||||
{
|
||||
$projects = DB::table('projects')
|
||||
->select('id', 'title') // Alias 'title' as 'name' to match frontend expectations
|
||||
->orderBy('title')
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
'projects' => $projects
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export ranking data to CSV
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function exportRanking(Request $request)
|
||||
{
|
||||
$projectId = $request->input('project_id');
|
||||
|
||||
if (!$projectId) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Project ID is required'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Get project name
|
||||
$project = DB::table('projects')
|
||||
->where('id', $projectId)
|
||||
->first();
|
||||
|
||||
if (!$project) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Project not found'
|
||||
], 404);
|
||||
}
|
||||
|
||||
// Get rankings data by calling the existing method
|
||||
$rankingResponse = $this->getRankingByProject($request);
|
||||
$content = json_decode($rankingResponse->getContent(), true);
|
||||
|
||||
if (!isset($content['data']['rankings'])) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'No ranking data available'
|
||||
], 404);
|
||||
}
|
||||
|
||||
$rankings = $content['data']['rankings'];
|
||||
$filename = 'ranking_' . \Illuminate\Support\Str::slug($project->title) . '_' . date('Y-m-d') . '.csv';
|
||||
|
||||
// Create CSV headers
|
||||
$headers = [
|
||||
"Content-type" => "text/csv",
|
||||
"Content-Disposition" => "attachment; filename=$filename",
|
||||
"Pragma" => "no-cache",
|
||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
||||
"Expires" => "0"
|
||||
];
|
||||
|
||||
// Create the CSV file
|
||||
$callback = function() use ($rankings) {
|
||||
$file = fopen('php://output', 'w');
|
||||
|
||||
// Add CSV header row
|
||||
fputcsv($file, ['Rank', 'Name', 'Attempts', 'Score (%)', 'Passed Tests', 'Total Tests', 'Submission Date']);
|
||||
|
||||
// Add data rows
|
||||
foreach ($rankings as $ranking) {
|
||||
fputcsv($file, [
|
||||
$ranking['rank'],
|
||||
$ranking['user_name'],
|
||||
$ranking['attempts'],
|
||||
$ranking['score'],
|
||||
$ranking['passed_tests'],
|
||||
$ranking['total_tests'],
|
||||
$ranking['submission_date']
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
};
|
||||
|
||||
return response()->stream($callback, 200, $headers);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ class TeacherMiddleware
|
|||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
|
||||
if ( Auth::user()->teacher !== "teacher" ) {
|
||||
if ( Auth::user()->role !== "teacher" ) {
|
||||
|
||||
|
||||
abort(403, "Unauthorized action.");
|
||||
}
|
||||
|
|
|
|||
41
package-lock.json
generated
41
package-lock.json
generated
|
|
@ -4,6 +4,11 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.14.9",
|
||||
"select2": "^4.1.0-rc.0",
|
||||
"tailwindcss": "^4.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1.1.2",
|
||||
"laravel-vite-plugin": "^0.7.5",
|
||||
|
|
@ -362,6 +367,30 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz",
|
||||
"integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/alpinejs": {
|
||||
"version": "3.14.9",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.9.tgz",
|
||||
"integrity": "sha512-gqSOhTEyryU9FhviNqiHBHzgjkvtukq9tevew29fTj+ofZtfsYriw4zPirHHOAy9bw8QoL3WGhyk7QqCh5AYlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "~3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -608,6 +637,12 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/select2": {
|
||||
"version": "4.1.0-rc.0",
|
||||
"resolved": "https://registry.npmjs.org/select2/-/select2-4.1.0-rc.0.tgz",
|
||||
"integrity": "sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
|
|
@ -617,6 +652,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.7",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz",
|
||||
"integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
||||
|
|
|
|||
|
|
@ -9,5 +9,10 @@
|
|||
"axios": "^1.1.2",
|
||||
"laravel-vite-plugin": "^0.7.5",
|
||||
"vite": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.14.9",
|
||||
"select2": "^4.1.0-rc.0",
|
||||
"tailwindcss": "^4.1.7"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="/nodejs" class="btn btn-primary">Start Learning</a>
|
||||
<a href="/nodejs/dashboard" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
413
resources/views/dashboard_teacher.blade.php
Normal file
413
resources/views/dashboard_teacher.blade.php
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
|
||||
|
||||
|
||||
<title>iCLOP</title>
|
||||
<link rel="icon" href={{asset("./images/logo.png")}} type="image/png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg" style="background-color: #FEFEFE;">
|
||||
<div class="container-fluid">
|
||||
<!-- <a class="navbar-brand" href="#">Navbar</a> -->
|
||||
<img src={{asset("./images/logo.png")}} alt="logo" width="104" height="65">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<div class="mx-auto">
|
||||
<ul class="navbar-nav mb-2 mb-lg-0 justify-content-center">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Dashboard Teacher</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Tutorials</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Contact Us</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<p style="margin-top: 10px; margin-right: 10px;">{{auth()->user()->name}}
|
||||
<img src="{{ asset('./images/Group.png') }}" alt="Group" style="height: 50px; margin-right: 10px;">
|
||||
<i class="fas fa-chevron-down" style="color: #0079FF;"></i>
|
||||
<div class="dropdown-content" id="dropdownContent">
|
||||
<form id="logout-form" action="{{ route('logoutt') }}" method="POST">
|
||||
@csrf
|
||||
<a href="#" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Logout</a>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
<!-- <button class="btn btn-primary custom-button-sign-up" onclick="window.location.href='register.html'">Sign Up</button> -->
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@auth
|
||||
|
||||
<!-- CONTENT -->
|
||||
<div class="container" style="margin-top: 70px; justify-content: center; align-items: center;">
|
||||
<p style="font-size: 22px;">Choose your<br><span style="font-size: 35px; font-weight: 600; color: #34364A;">Learning Materials</span></p>
|
||||
|
||||
<!-- CARD 1 -->
|
||||
<!-- <div class="row" style="margin-top: 45px; display: flex; justify-content: center; align-items: center;"> -->
|
||||
<div class="row" style="margin-top: 45px;">
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src={{asset("./images/cards/Android.png")}} class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Android programming
|
||||
with Java and Kotlin</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src={{asset("./images/book.png")}} style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="/android23/topic" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src={{asset("./images/cards/Flutter.png")}} class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Mobile programming with Flutter</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src={{asset("./images/book.png")}} style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="/flutter/start" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height:375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src={{asset("./images/cards/Node.js.png")}} class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Web application with Node.JS</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src={{asset("./images/book.png ")}} style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>{{ $topicCount ?? '0' }} learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('dashboard.nodejs.teacher') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
<!-- CARD 2 -->
|
||||
<!-- <div class="row" style="margin-top: 45px; display: flex; justify-content: center; align-items: center;"> -->
|
||||
<div class="row" style="margin-top: 45px;">
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/Python.png")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Python programming</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/MySQL.png")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">SQL Querying with MySQL</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/PostgreSQL.png")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">SQL Querying with PostgreSQL</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
<!-- CARD 3 -->
|
||||
<!-- <div class="row" style="margin-top: 45px; display: flex; justify-content: center; align-items: center;"> -->
|
||||
<div class="row" style="margin-top: 45px;">
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/Network.png ")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Network programming with Java</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/Unity.png")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Game programming with Unity</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/Data analytic.png ")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Data Analytics with Python</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('learning_student') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 45px;">
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/DB.png ")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Database with PHP Programming</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('welcome') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/React.jpg")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Learn React JS</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>6 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('react_welcome') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="card p-0" style="width: 305px; height: 375px; margin-left: 25px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<img src="{{asset("./images/cards/DB.png ")}}" class="card-img-top" style="width: auto; height: 200px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">Database Programming with PHP</h5>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<img src="{{asset("./images/book.png ")}}" style="width: 13px; height: 16px;">
|
||||
</div>
|
||||
<div class="col">
|
||||
<p>18 learning topics</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: auto;">
|
||||
<a href="{{ route('welcome') }}" class="btn btn-primary">Start Learning</a>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<div class="container text-align" style="margin-top: 100px; background-color: #FAFAFA; padding-right: 50px; padding-left: 50px; height: 300px;">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="container" style="margin-top: 40px;">
|
||||
<img src="{{asset("./images/logo.png ")}}" alt="rocket" style="height: 60px;">
|
||||
<p style="font-size: 16px; color: #636363;">Intelligent Computer Assisted<br>Programming Learning Platform.</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<i class="fab fa-instagram fa-lg" style="padding-right: 2px; color: #636363;"></i>
|
||||
<i class="fab fa-github fa-lg" style="padding-right: 2px; color: #636363;"></i>
|
||||
<i class="fab fa-linkedin fa-lg" style="padding-right: 2px; color: #636363;"></i>
|
||||
<i class="fab fa-youtube fa-lg" style="padding-right: 2px; color: #636363;"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="container">
|
||||
<p style="font-size: 22px; font-weight: 600; color: #34364A; margin-top: 40px; margin-left: 55px;">Company</p>
|
||||
<p style="font-size: 16px; color: #636363; margin-left: 55px;">Privacy Policy</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="container">
|
||||
|
||||
<p style="font-size: 22px; font-weight: 600; color: #34364A; margin-top: 40px;">Contact Info</p>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<i class="fas fa-map-marker-alt fa-lg" style="color: #636363; margin-top: 5px;"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p style="font-size: 16px; color: #636363;">Jl. Candi Mendut, RT.02/RW.08, Mojolangu, Kec. Lowokwaru, Kota Malang, Jawa Timur 65142</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row align-items-start">
|
||||
<div class="col-1">
|
||||
<i class="fas fa-envelope" style="color: #636363; margin-top: 5px;"></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p style="font-size: 16px; color: #636363;">qulis@polinema.ac.id (Email)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<p style="font-size: 16px; color: #636363; text-align: center; margin-top: 16px; margin-bottom: 16px;">© 2023 iCLOP. All rights reserved</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
</body>
|
||||
<script src="script.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#dropdownContainer").click(function() {
|
||||
$("#dropdownContainer").toggleClass("active");
|
||||
});
|
||||
$("#dropdownContent").click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
$(document).click(function() {
|
||||
$("#dropdownContainer").removeClass("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
transition: 0.3s;
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.dropdown-content a {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.dropdown-content a:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
.dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.dropdown.active .dropdown-content {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</html>
|
||||
341
resources/views/nodejs/dashboard-teacher/index.blade.php
Normal file
341
resources/views/nodejs/dashboard-teacher/index.blade.php
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Teacher Dashboard') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 pb-12">
|
||||
<!-- Summary Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-16">
|
||||
<!-- Total Projects -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-indigo-500 bg-opacity-75">
|
||||
<i class="fas fa-project-diagram text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Projects</p>
|
||||
<p class="text-lg font-semibold">{{ $totalProyek }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Total Students -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-blue-500 bg-opacity-75">
|
||||
<i class="fas fa-users text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Students</p>
|
||||
<p class="text-lg font-semibold">{{ $totalSiswa }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Total Submissions -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-green-500 bg-opacity-75">
|
||||
<i class="fas fa-file-code text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Submissions</p>
|
||||
<p class="text-lg font-semibold">{{ $totalSubmisi }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Completed Submissions -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-purple-500 bg-opacity-75">
|
||||
<i class="fas fa-check-circle text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Completed Submissions</p>
|
||||
<p class="text-lg font-semibold">{{ $submissionSelesai }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Failed Submissions -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-purple-500 bg-opacity-75">
|
||||
<i class="fas fa-times-circle text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Failed Submissions</p>
|
||||
<p class="text-lg font-semibold">{{ $submissionGagal }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Average Submissions -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-yellow-500 bg-opacity-75">
|
||||
<i class="fas fa-calculator text-white text-2xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Average Submissions Attempts</p>
|
||||
<p class="text-lg font-semibold">{{ $ratarataPecobaan }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts Row -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-8 mt-4">
|
||||
<!-- Submission Status Chart -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Submission Status</h3>
|
||||
<div class="relative h-64">
|
||||
<canvas id="submissionStatusChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Distribution Chart -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Projects with vs without Submissions</h3>
|
||||
<div class="relative h-64">
|
||||
<canvas id="projectDistributionChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4 mb-8 mt-4">
|
||||
<!-- Student Participation Rate -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Student Participation</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Active Students</p>
|
||||
<p class="text-2xl font-bold">{{ $siswaSubmisi }} / {{ $totalSiswa }}</p>
|
||||
</div>
|
||||
<div class="relative w-24 h-24">
|
||||
<canvas id="participationChart"></canvas>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-lg font-bold text-gray-900 dark:text-gray-100">{{ $tingkatPartisipasiSiswa }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Success Rate -->
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Submission Success Rate</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Completed Submissions</p>
|
||||
<p class="text-2xl font-bold">{{ $submissionSelesai }} / {{ $totalSubmisi }}</p>
|
||||
</div>
|
||||
<div class="relative w-24 h-24">
|
||||
<canvas id="successRateChart"></canvas>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-lg font-bold text-gray-900 dark:text-gray-100">{{ $tingkatKeberhasilan ?? 0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Detailed Statistics -->
|
||||
<div class="grid grid-cols-1 gap-4 mb-8 mt-4 w-full">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg w-full">
|
||||
<div class="p-6 w-full">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Detailed Statistics</h3>
|
||||
<div class="overflow-x-auto w-full">
|
||||
<table class="w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-700 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Metric</th>
|
||||
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-700 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Value</th>
|
||||
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-700 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">Projects with Submissions</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $proyekDenganSubmisi }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Number of projects that have at least one submission</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">Projects without Submissions</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $proyekTanpaSubmisi }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Number of projects that have no submissions yet</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">Processing Submissions</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $submisiDalamProses }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Number of submissions currently being processed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">Pending Submissions</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $submisiTertunda }}</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Number of submissions awaiting action</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
// Submission Status Chart
|
||||
const submissionStatusCtx = document.getElementById('submissionStatusChart').getContext('2d');
|
||||
const submissionStatusChart = new Chart(submissionStatusCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Completed', 'Processing', 'Pending', 'Failed'], // Tambah label Failed
|
||||
datasets: [{
|
||||
label: 'Submission Status',
|
||||
data: [{{ $submissionSelesai }}, {{ $submisiDalamProses }}, {{ $submisiTertunda }}, {{ $submissionGagal }}], // Tambah data failed
|
||||
backgroundColor: [
|
||||
'rgba(72, 187, 120, 0.7)', // Green for completed
|
||||
'rgba(66, 153, 225, 0.7)', // Blue for processing
|
||||
'rgba(237, 137, 54, 0.7)', // Orange for pending
|
||||
'rgba(220, 53, 69, 0.7)' // Red for failed
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(72, 187, 120, 1)',
|
||||
'rgba(66, 153, 225, 1)',
|
||||
'rgba(237, 137, 54, 1)',
|
||||
'rgba(220, 53, 69, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
precision: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Project Distribution Chart
|
||||
const projectDistributionCtx = document.getElementById('projectDistributionChart').getContext('2d');
|
||||
const projectDistributionChart = new Chart(projectDistributionCtx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Projects with Submissions', 'Projects without Submissions'],
|
||||
datasets: [{
|
||||
data: [{{ $proyekDenganSubmisi }}, {{ $proyekTanpaSubmisi }}],
|
||||
backgroundColor: [
|
||||
'rgba(79, 209, 197, 0.7)', // Teal
|
||||
'rgba(159, 122, 234, 0.7)' // Purple
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(79, 209, 197, 1)',
|
||||
'rgba(159, 122, 234, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
});
|
||||
|
||||
// Participation Rate Chart
|
||||
const participationCtx = document.getElementById('participationChart').getContext('2d');
|
||||
const participationChart = new Chart(participationCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Active', 'Inactive'],
|
||||
datasets: [{
|
||||
data: [{{ $siswaSubmisi }}, {{ $totalSiswa - $siswaSubmisi }}],
|
||||
backgroundColor: [
|
||||
'rgba(104, 211, 145, 0.7)', // Green
|
||||
'rgba(226, 232, 240, 0.7)' // Light gray
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(104, 211, 145, 1)',
|
||||
'rgba(226, 232, 240, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
cutout: '70%',
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Success Rate Chart
|
||||
const successRateCtx = document.getElementById('successRateChart').getContext('2d');
|
||||
const successRateChart = new Chart(successRateCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Completed', 'Incomplete'],
|
||||
datasets: [{
|
||||
data: [{{ $submissionSelesai }}, {{ $totalSubmisi - $submissionSelesai }}],
|
||||
backgroundColor: [
|
||||
'rgba(72, 187, 120, 0.7)', // Green
|
||||
'rgba(226, 232, 240, 0.7)' // Light gray
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(72, 187, 120, 1)',
|
||||
'rgba(226, 232, 240, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
cutout: '70%',
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
</x-app-layout>
|
||||
|
|
@ -1,76 +1,94 @@
|
|||
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<!-- Left side: Logo and Navigation Links -->
|
||||
<div class="flex items-center">
|
||||
<!-- Logo -->
|
||||
<div class="shrink-0 flex items-center">
|
||||
<a href="{{ route('dashboard.nodejs') }}">
|
||||
<a href="#">
|
||||
<x-application-logo class="block h-9 w-auto fill-current text-gray-800" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
|
||||
<x-nav-link :href="route('dashboard.nodejs')" :active="request()->routeIs('dashboard*')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('projects')" :active="request()->routeIs('projects*')">
|
||||
{{ __('Projects') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('submissions')" :active="request()->routeIs('submissions*')">
|
||||
{{ __('Submissions') }}
|
||||
</x-nav-link>
|
||||
@if (Auth::user()->role === 'student')
|
||||
<x-nav-link :href="route('dashboard.nodejs')" :active="request()->routeIs('dashboard-student')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('projects')" :active="request()->routeIs('projects.student')">
|
||||
{{ __('Projects') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('submissions')" :active="request()->routeIs('submissions.student')">
|
||||
{{ __('Submissions') }}
|
||||
</x-nav-link>
|
||||
@elseif (Auth::user()->role === 'teacher')
|
||||
<x-nav-link :href="route('dashboard.nodejs.teacher')" :active="request()->routeIs('dashboard-teacher')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('nodejs.teacher.rank')" :active="request()->routeIs('nodejs.teacher.rank')">
|
||||
{{ __('Rank') }}
|
||||
</x-nav-link>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ml-6">
|
||||
<!-- Right side: User Dropdown -->
|
||||
<div class="hidden sm:flex sm:items-center ml-auto">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button
|
||||
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ Auth::user()->name }}</div>
|
||||
|
||||
<div class="ml-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
{{-- Profile Link (Bisa ditampilkan untuk semua role) --}}
|
||||
<x-dropdown-link :href="route('profile.edit')">
|
||||
{{ __('Profile') }}
|
||||
</x-dropdown-link>
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="GET" action="{{ route('dashboard-student') }}">
|
||||
@csrf
|
||||
|
||||
<x-dropdown-link :href="route('dashboard-student')" onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
{{-- Authentication / Logout Berdasarkan Role --}}
|
||||
@if (Auth::user()->role === 'student')
|
||||
<form method="GET" action="{{ route('dashboard-student') }}">
|
||||
@csrf
|
||||
<x-dropdown-link href="{{ route('dashboard-student') }}" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
@elseif (Auth::user()->role === 'teacher')
|
||||
<form method="GET" action="{{ route('dashboard-teacher') }}">
|
||||
@csrf
|
||||
<x-dropdown-link href="{{ route('dashboard-teacher') }}" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
@endif
|
||||
</x-slot>
|
||||
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- Hamburger -->
|
||||
<!-- Hamburger (Mobile) -->
|
||||
<div class="-mr-2 flex items-center sm:hidden">
|
||||
<button @click="open = ! open"
|
||||
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
||||
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex"
|
||||
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16" />
|
||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16" />
|
||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden"
|
||||
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -80,15 +98,24 @@ class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark
|
|||
<!-- Responsive Navigation Menu -->
|
||||
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
|
||||
<div class="pt-2 pb-3 space-y-1">
|
||||
<x-responsive-nav-link :href="route('dashboard.nodejs')" :active="request()->routeIs('dashboard*')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('projects')" :active="request()->routeIs('projects*')">
|
||||
{{ __('Projects') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('submissions')" :active="request()->routeIs('submissions*')">
|
||||
{{ __('Submissions') }}
|
||||
</x-responsive-nav-link>
|
||||
@if (Auth::user()->role === 'student')
|
||||
<x-responsive-nav-link :href="route('dashboard.nodejs')" :active="request()->routeIs('dashboard*')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('projects')" :active="request()->routeIs('projects*')">
|
||||
{{ __('Projects') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('submissions')" :active="request()->routeIs('submissions*')">
|
||||
{{ __('Submissions') }}
|
||||
</x-responsive-nav-link>
|
||||
@elseif (Auth::user()->role === 'teacher')
|
||||
<x-responsive-nav-link :href="route('dashboard.nodejs.teacher')" :active="request()->routeIs('dashboard*')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('nodejs.teacher.rank')" :active="request()->routeIs('nodejs.teacher.rank')">
|
||||
{{ __('Rank') }}
|
||||
</x-responsive-nav-link>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Responsive Settings Options -->
|
||||
|
|
@ -103,15 +130,21 @@ class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark
|
|||
{{ __('Profile') }}
|
||||
</x-responsive-nav-link>
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="GET" action="{{ route('dashboard-student') }}">
|
||||
@csrf
|
||||
|
||||
<x-responsive-nav-link :href="route('dashboard-student')" onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-responsive-nav-link>
|
||||
</form>
|
||||
@if (Auth::user()->role === 'student')
|
||||
<form method="GET" action="{{ route('dashboard-student') }}">
|
||||
@csrf
|
||||
<x-responsive-nav-link :href="route('dashboard-student')" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-responsive-nav-link>
|
||||
</form>
|
||||
@elseif (Auth::user()->role === 'teacher')
|
||||
<form method="GET" action="{{ route('dashboard-teacher') }}">
|
||||
@csrf
|
||||
<x-responsive-nav-link :href="route('dashboard-teacher')" onclick="event.preventDefault(); this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-responsive-nav-link>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
212
resources/views/nodejs/rank-teacher/index.blade.php
Normal file
212
resources/views/nodejs/rank-teacher/index.blade.php
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Student Rankings') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="w-full mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class=" dark:bg-slate-800 overflow-hidden shadow-xl sm:rounded-lg border dark:border-gray-700">
|
||||
<!-- Header with project selector -->
|
||||
<div class="flex flex-col md:flex-row justify-between items-center dark:bg-slate-900 p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<span class="text-xl font-bold text-gray-800 dark:text-white mb-4 md:mb-0">Student Rankings</span>
|
||||
<div class="w-full md:w-96">
|
||||
<select id="project-selector" class= "bg-gray-50 border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-secondary-500 focus:border-secondary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-secondary-500 dark:focus:border-secondary-500">
|
||||
<option value="">Select Project</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<!-- Loading indicator -->
|
||||
<div id="loading" class="text-center my-8 hidden">
|
||||
<div class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
|
||||
<p class="mt-4 text-gray-600 dark:text-gray-400">Loading data...</p>
|
||||
</div>
|
||||
|
||||
<!-- No project selected message -->
|
||||
<div id="no-project-selected" class="text-center my-12">
|
||||
<svg class="mx-auto h-16 w-16 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
<p class="mt-4 text-lg font-medium text-gray-600 dark:text-gray-400">Please select a project to view rankings</p>
|
||||
</div>
|
||||
|
||||
<!-- Rankings data container -->
|
||||
<div id="ranking-data" class="hidden">
|
||||
<!-- Export button -->
|
||||
<div class="flex justify-end mb-5">
|
||||
<button id="export-csv" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors flex items-center shadow-md">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
Export to CSV
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Rankings table -->
|
||||
<div class="overflow-x-auto dark:bg-slate-800 rounded-lg shadow-md">
|
||||
<table class="w-full table-fixed divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700 text-gray-500 dark:text-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="w-1/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Rank</th>
|
||||
<th scope="col" class="w-3/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Name</th>
|
||||
<th scope="col" class="w-1/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Attempts</th>
|
||||
<th scope="col" class="w-1/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Score</th>
|
||||
<th scope="col" class="w-1/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Passed</th>
|
||||
<th scope="col" class="w-1/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Total</th>
|
||||
<th scope="col" class="w-2/12 px-6 py-4 text-left text-xs font-bold text-gray-600 dark:text-white uppercase tracking-wider">Last Submission</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="ranking-table-body" class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<!-- Rankings will be populated here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No data message -->
|
||||
<div id="no-data" class="text-center my-12 hidden">
|
||||
<svg class="mx-auto h-16 w-16 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M12 14h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<p class="mt-4 text-lg font-medium text-gray-600 dark:text-gray-400">No submissions found for this project</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Load projects for dropdown
|
||||
loadProjects();
|
||||
|
||||
// Project selection change event
|
||||
$('#project-selector').change(function() {
|
||||
const projectId = $(this).val();
|
||||
if (projectId) {
|
||||
loadRankings(projectId);
|
||||
} else {
|
||||
$('#ranking-data').hide();
|
||||
$('#no-project-selected').show();
|
||||
$('#no-data').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Export to CSV button click event
|
||||
$('#export-csv').click(function() {
|
||||
const projectId = $('#project-selector').val();
|
||||
if (projectId) {
|
||||
window.location.href = `{{ route('nodejs.teacher.rank.export') }}?project_id=${projectId}`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function loadProjects() {
|
||||
$.ajax({
|
||||
url: "{{ route('nodejs.teacher.rank.projects') }}",
|
||||
type: "GET",
|
||||
beforeSend: function() {
|
||||
$('#loading').show();
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
const projects = response.data.projects;
|
||||
let options = '<option value="">Pilih Proyek</option>';
|
||||
|
||||
projects.forEach(project => {
|
||||
options += `<option value="${project.id}">${project.title}</option>`;
|
||||
});
|
||||
|
||||
$('#project-selector').html(options);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error('Error loading projects', xhr);
|
||||
alert('Gagal memuat proyek. Silakan coba lagi.');
|
||||
},
|
||||
complete: function() {
|
||||
$('#loading').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadRankings(projectId) {
|
||||
$.ajax({
|
||||
url: "{{ route('nodejs.teacher.rank.by-project') }}",
|
||||
type: "GET",
|
||||
data: {
|
||||
project_id: projectId
|
||||
},
|
||||
beforeSend: function() {
|
||||
$('#ranking-data').hide();
|
||||
$('#no-project-selected').hide();
|
||||
$('#no-data').hide();
|
||||
$('#loading').show();
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
const rankings = response.data.rankings;
|
||||
|
||||
if (rankings.length > 0) {
|
||||
populateRankingsTable(rankings);
|
||||
$('#ranking-data').show();
|
||||
} else {
|
||||
$('#no-data').show();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error('Error loading rankings', xhr);
|
||||
alert('Gagal memuat peringkat. Silakan coba lagi.');
|
||||
},
|
||||
complete: function() {
|
||||
$('#loading').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function populateRankingsTable(rankings) {
|
||||
let tableRows = '';
|
||||
|
||||
rankings.forEach(student => {
|
||||
const submissionDate = new Date(student.submission_date).toLocaleString();
|
||||
|
||||
// Adding special class for top 3 performers with improved contrast
|
||||
let rowClass = '';
|
||||
if (student.rank <= 3) {
|
||||
rowClass = 'top-performer';
|
||||
}
|
||||
|
||||
tableRows += `
|
||||
<tr class="${rowClass}">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
||||
${student.rank <= 3 ?
|
||||
`<span class="inline-flex items-center justify-center w-6 h-6 rounded-full ${
|
||||
student.rank === 1 ? 'bg-yellow-400 gold-medal' :
|
||||
student.rank === 2 ? 'bg-gray-300 silver-medal' :
|
||||
'bg-amber-600 bronze-medal'} text-white font-bold">
|
||||
${student.rank}
|
||||
</span>` :
|
||||
student.rank
|
||||
}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">${student.user_name}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">${student.attempts}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white font-medium">${student.score}%</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">${student.passed_tests}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">${student.total_tests}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">${submissionDate}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
$('#ranking-table-body').html(tableRows);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
</x-app-layout>
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
use App\Http\Controllers\NodeJS\Student\SubmissionController;
|
||||
use App\Http\Controllers\NodeJS\Student\WelcomeController;
|
||||
use App\Http\Controllers\NodeJS\Student\ProfileController;
|
||||
use App\Http\Controllers\NodeJS\Teacher\DashboardController as TeacherDashboardController;
|
||||
use App\Http\Controllers\NodeJS\Teacher\RankController;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
|
@ -14,11 +16,14 @@
|
|||
|
||||
Route::middleware('auth')->group(function () {
|
||||
// Dashboard
|
||||
Route::get('/dashboard/teacher', [TeacherDashboardController::class, 'index'])->name('dashboard.nodejs.teacher');
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard.nodejs');
|
||||
|
||||
// Profile
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||
|
||||
// Projects
|
||||
Route::prefix('projects')->controller(ProjectController::class)->group(function () {
|
||||
Route::get('/', 'index')->name('projects');
|
||||
|
|
@ -26,6 +31,7 @@
|
|||
Route::get('/project/{project_id}/download', 'download')->name('projects.download');
|
||||
Route::get('pdf', 'showPDF')->name('projects.pdf');
|
||||
});
|
||||
|
||||
// Submissions
|
||||
Route::prefix('submissions')->group(function () {
|
||||
// show all the submission based on the projects
|
||||
|
|
@ -57,5 +63,17 @@
|
|||
// update source code based on the submission id
|
||||
Route::post('/update/submission', [SubmissionController::class, 'update'])->name('submissions.update');
|
||||
});
|
||||
|
||||
// Teacher Rankings
|
||||
Route::prefix('teacher/rank')->controller(RankController::class)->group(function () {
|
||||
// Display ranking page
|
||||
Route::get('/', 'index')->name('nodejs.teacher.rank');
|
||||
// Get ranking data for a specific project
|
||||
Route::get('/by-project', 'getRankingByProject')->name('nodejs.teacher.rank.by-project');
|
||||
// Get all projects for the ranking dropdown
|
||||
Route::get('/projects', 'getProjects')->name('nodejs.teacher.rank.projects');
|
||||
// Export ranking data to CSV
|
||||
Route::get('/export', 'exportRanking')->name('nodejs.teacher.rank.export');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\AuthController;
|
||||
use App\Http\Controllers\DataController;
|
||||
use App\Http\Controllers\NodeJS\NodeJSController;
|
||||
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use App\Http\Controllers\SocialController;
|
||||
|
|
@ -50,13 +51,13 @@
|
|||
// Route::group(["prefix" => 'test', 'middleware' => ['login'], 'as' => 'test.'], function(){
|
||||
|
||||
|
||||
Route::get('/dashboard-student', function () {
|
||||
return view('dashboard_student');
|
||||
})->name('dashboard-student')->middleware('auth');
|
||||
Route::get('/dashboard_teacher', [NodeJSController::class, 'dashboardTeacher'])
|
||||
->name('dashboard-teacher')
|
||||
->middleware('auth');
|
||||
|
||||
Route::get('/dashboard_teacher', function () {
|
||||
return view('dashboard_student');
|
||||
})->name('dashboard-teacher')->middleware('auth');
|
||||
Route::get('/dashboard-student', [NodeJSController::class, 'dashboardStudent'])
|
||||
->name('dashboard-student')
|
||||
->middleware('auth');
|
||||
|
||||
Route::get('/learning-student', function () {
|
||||
return view('learning_student');
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user