materi tugas siswa
This commit is contained in:
parent
bc5a63ade9
commit
c6e706c4e1
|
|
@ -6,18 +6,15 @@
|
|||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
// Sesuaikan nama model-model ini dengan yang kamu punya
|
||||
use App\Models\Tugas;
|
||||
use App\Models\TugasSiswa; // tabel pengumpulan tugas
|
||||
use App\Models\PengumpulanTugas;
|
||||
use App\Models\Challenge;
|
||||
use App\Models\ChallengeSiswa; // tabel progress challenge siswa
|
||||
use App\Models\PesertaChallenge;
|
||||
use App\Models\Leaderboard;
|
||||
use App\Models\Siswa;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
/**
|
||||
* Pastikan hanya siswa yang sudah login yang bisa akses.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:siswa');
|
||||
|
|
@ -29,72 +26,83 @@ public function index()
|
|||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
// =============================================
|
||||
// 1. TUGAS — ambil tugas yang belum dikumpulkan
|
||||
// dan deadline-nya belum lewat, urutkan by deadline
|
||||
// 1. TUGAS — yang belum dikumpulkan siswa ini
|
||||
// dan deadline belum lewat
|
||||
// =============================================
|
||||
$tugasRaw = Tugas::with('mataPelajaran') // eager load relasi mapel
|
||||
->whereDoesntHave('pengumpulan', function ($q) use ($siswa) {
|
||||
// tugas yang BELUM dikumpulkan oleh siswa ini
|
||||
$q->where('siswa_id', $siswa->id);
|
||||
})
|
||||
|
||||
// id_tugas yang sudah dikumpulkan siswa ini
|
||||
$sudahDikumpulkan = PengumpulanTugas::where('id_siswa', $siswa->id_siswa)
|
||||
->pluck('id_tugas');
|
||||
|
||||
// Tugas untuk kelas siswa yang belum dikumpulkan
|
||||
$tugasRaw = Tugas::with(['mengajar.mapel'])
|
||||
->whereNotIn('id_tugas', $sudahDikumpulkan)
|
||||
->where('deadline', '>=', Carbon::now())
|
||||
->whereHas('mengajar', function ($q) use ($siswa) {
|
||||
$q->where('id_kelas', $siswa->id_kelas);
|
||||
})
|
||||
->orderBy('deadline', 'asc')
|
||||
->take(5) // tampilkan maks 5 tugas di dashboard
|
||||
->take(5)
|
||||
->get();
|
||||
|
||||
// Kelompokkan tugas berdasarkan tanggal deadline
|
||||
// Kelompokkan per tanggal deadline
|
||||
$tugasList = [];
|
||||
foreach ($tugasRaw as $tugas) {
|
||||
$tgl = Carbon::parse($tugas->deadline)
|
||||
->locale('id')
|
||||
->isoFormat('dddd, D MMMM YYYY'); // contoh: "Sabtu, 10 Mei 2025"
|
||||
->isoFormat('dddd, D MMMM YYYY');
|
||||
|
||||
$namaMapel = optional(optional($tugas->mengajar)->mapel)->nama_mapel ?? '-';
|
||||
|
||||
$tugasList[$tgl][] = [
|
||||
'jam' => Carbon::parse($tugas->deadline)->format('H.i'),
|
||||
'nama' => $tugas->judul,
|
||||
// sesuaikan nama kolom dengan model kamu
|
||||
'mapel' => 'Belum · ' . ($tugas->mataPelajaran->nama ?? '-'),
|
||||
'nama' => $tugas->judul_tugas,
|
||||
'mapel' => 'Belum · ' . $namaMapel,
|
||||
];
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 2. CHALLENGE MINGGUAN
|
||||
// =============================================
|
||||
$startMingguIni = Carbon::now()->startOfWeek();
|
||||
$endMingguIni = Carbon::now()->endOfWeek();
|
||||
$startMinggu = Carbon::now()->startOfWeek();
|
||||
$endMinggu = Carbon::now()->endOfWeek();
|
||||
|
||||
// Total challenge aktif minggu ini
|
||||
$challengeTotal = Challenge::whereBetween('created_at', [$startMingguIni, $endMingguIni])
|
||||
->where('is_active', true)
|
||||
// Challenge untuk kelas siswa yang masih aktif minggu ini
|
||||
$challengeTotal = Challenge::whereHas('kelas', function ($q) use ($siswa) {
|
||||
$q->where('challenge_kelas.id_kelas', $siswa->id_kelas);
|
||||
})
|
||||
->where('tenggat_waktu', '>=', Carbon::now())
|
||||
->whereBetween('created_at', [$startMinggu, $endMinggu])
|
||||
->count();
|
||||
|
||||
// Challenge yang sudah diselesaikan siswa minggu ini
|
||||
$challengeDone = ChallengeSiswa::where('siswa_id', $siswa->id)
|
||||
// Challenge yang sudah selesai dikerjakan siswa minggu ini
|
||||
$challengeDone = PesertaChallenge::where('id_siswa', $siswa->id_siswa)
|
||||
->where('status', 'selesai')
|
||||
->whereBetween('created_at', [$startMingguIni, $endMingguIni])
|
||||
->whereBetween('waktu_submit', [$startMinggu, $endMinggu])
|
||||
->count();
|
||||
|
||||
// =============================================
|
||||
// 3. TUGAS SELESAI MINGGU INI (untuk speech bubble mascot)
|
||||
// 3. TUGAS SELESAI MINGGU INI (mascot speech bubble)
|
||||
// =============================================
|
||||
$tugasSelesai = TugasSiswa::where('siswa_id', $siswa->id)
|
||||
->whereBetween('created_at', [$startMingguIni, $endMingguIni])
|
||||
$tugasSelesai = PengumpulanTugas::where('id_siswa', $siswa->id_siswa)
|
||||
->whereIn('status', ['dikumpulkan', 'terlambat'])
|
||||
->whereBetween('tanggal_submit', [$startMinggu, $endMinggu])
|
||||
->count();
|
||||
|
||||
// =============================================
|
||||
// 4. LEADERBOARD — top 3 siswa berdasarkan total EXP
|
||||
// Asumsi: kolom 'exp' ada di tabel siswa
|
||||
// Sesuaikan jika EXP disimpan di tabel lain
|
||||
// 4. LEADERBOARD — top 3 kelas siswa yang login
|
||||
// =============================================
|
||||
$leaderboardRaw = Siswa::orderBy('exp', 'desc')
|
||||
$leaderboardRaw = Leaderboard::with('siswa')
|
||||
->where('id_kelas', $siswa->id_kelas)
|
||||
->orderBy('total_exp', 'desc')
|
||||
->take(3)
|
||||
->get();
|
||||
|
||||
$leaderboard = $leaderboardRaw->map(function ($item, $index) {
|
||||
return [
|
||||
'rank' => $index + 1,
|
||||
'nama' => $item->nama,
|
||||
'exp' => $item->exp,
|
||||
'nama' => optional($item->siswa)->nama ?? '-',
|
||||
'exp' => $item->total_exp,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Siswa;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Models\Mengajar;
|
||||
use App\Models\Materi;
|
||||
|
||||
class MateriController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:siswa');
|
||||
}
|
||||
|
||||
/**
|
||||
* Halaman daftar mata pelajaran siswa
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
// Ambil semua mapel yang diajarkan di kelas siswa ini
|
||||
$mapelList = Mengajar::with(['mapel', 'guru'])
|
||||
->where('id_kelas', $siswa->id_kelas)
|
||||
->get()
|
||||
->map(function ($mengajar) {
|
||||
return [
|
||||
'id_mengajar' => $mengajar->id_mengajar,
|
||||
'nama_mapel' => optional($mengajar->mapel)->nama_mapel ?? '-',
|
||||
'nama_guru' => optional($mengajar->guru)->nama ?? '-',
|
||||
'jumlah_materi' => Materi::where('id_mengajar', $mengajar->id_mengajar)->count(),
|
||||
];
|
||||
});
|
||||
|
||||
return view('siswa.materi.index', compact('mapelList'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Halaman daftar materi per mata pelajaran
|
||||
*/
|
||||
public function show($id_mengajar)
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
// Pastikan mengajar ini memang untuk kelas siswa
|
||||
$mengajar = Mengajar::with(['mapel', 'guru'])
|
||||
->where('id_mengajar', $id_mengajar)
|
||||
->where('id_kelas', $siswa->id_kelas)
|
||||
->firstOrFail();
|
||||
|
||||
$materiList = Materi::where('id_mengajar', $id_mengajar)
|
||||
->orderBy('tanggal_upload', 'desc')
|
||||
->get();
|
||||
|
||||
return view('siswa.materi.show', compact('mengajar', 'materiList'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Siswa;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Tugas;
|
||||
use App\Models\PengumpulanTugas;
|
||||
use App\Models\Mengajar;
|
||||
|
||||
class TugasController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth:siswa');
|
||||
}
|
||||
|
||||
/**
|
||||
* Daftar semua tugas untuk kelas siswa
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
// Ambil semua tugas untuk kelas siswa
|
||||
$semuaTugas = Tugas::with(['mengajar.mapel', 'mengajar.guru'])
|
||||
->whereHas('mengajar', function ($q) use ($siswa) {
|
||||
$q->where('id_kelas', $siswa->id_kelas);
|
||||
})
|
||||
->orderBy('deadline', 'asc')
|
||||
->get();
|
||||
|
||||
// Tandai status tiap tugas untuk siswa ini
|
||||
$tugasList = $semuaTugas->map(function ($tugas) use ($siswa) {
|
||||
$pengumpulan = PengumpulanTugas::where('id_tugas', $tugas->id_tugas)
|
||||
->where('id_siswa', $siswa->id_siswa)
|
||||
->first();
|
||||
|
||||
$now = Carbon::now();
|
||||
$deadline = Carbon::parse($tugas->deadline);
|
||||
|
||||
if ($pengumpulan) {
|
||||
$status = $pengumpulan->status; // 'dikumpulkan' atau 'terlambat'
|
||||
} else {
|
||||
$status = $now->greaterThan($deadline) ? 'terlambat' : 'belum';
|
||||
}
|
||||
|
||||
return [
|
||||
'id_tugas' => $tugas->id_tugas,
|
||||
'judul' => $tugas->judul_tugas,
|
||||
'keterangan' => $tugas->keterangan,
|
||||
'deadline' => $deadline,
|
||||
'nama_mapel' => optional(optional($tugas->mengajar)->mapel)->nama_mapel ?? '-',
|
||||
'nama_guru' => optional(optional($tugas->mengajar)->guru)->nama ?? '-',
|
||||
'status' => $status,
|
||||
'sudah_kumpul'=> !is_null($pengumpulan),
|
||||
'lampiran' => $pengumpulan?->lampiran_tugas,
|
||||
'exp' => $pengumpulan?->exp ?? 0,
|
||||
];
|
||||
});
|
||||
|
||||
// Kelompokkan: belum & pending vs sudah dikumpulkan
|
||||
$tugasBelum = $tugasList->filter(fn($t) => !$t['sudah_kumpul'] && $t['status'] !== 'terlambat');
|
||||
$tugasTerlambat = $tugasList->filter(fn($t) => !$t['sudah_kumpul'] && $t['status'] === 'terlambat');
|
||||
$tugasSelesai = $tugasList->filter(fn($t) => $t['sudah_kumpul']);
|
||||
|
||||
return view('siswa.tugas.index', compact('tugasBelum', 'tugasTerlambat', 'tugasSelesai'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail tugas + form submit
|
||||
*/
|
||||
public function show($id_tugas)
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
$tugas = Tugas::with(['mengajar.mapel', 'mengajar.guru'])
|
||||
->whereHas('mengajar', function ($q) use ($siswa) {
|
||||
$q->where('id_kelas', $siswa->id_kelas);
|
||||
})
|
||||
->where('id_tugas', $id_tugas)
|
||||
->firstOrFail();
|
||||
|
||||
$pengumpulan = PengumpulanTugas::where('id_tugas', $id_tugas)
|
||||
->where('id_siswa', $siswa->id_siswa)
|
||||
->first();
|
||||
|
||||
$sudahKumpul = !is_null($pengumpulan);
|
||||
$terlambat = Carbon::now()->greaterThan(Carbon::parse($tugas->deadline));
|
||||
|
||||
return view('siswa.tugas.show', compact('tugas', 'pengumpulan', 'sudahKumpul', 'terlambat'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit / upload jawaban tugas
|
||||
*/
|
||||
public function submit(Request $request, $id_tugas)
|
||||
{
|
||||
$siswa = Auth::guard('siswa')->user();
|
||||
|
||||
$request->validate([
|
||||
'lampiran_tugas' => 'required|file|mimes:pdf,doc,docx,jpg,jpeg,png|max:5120',
|
||||
], [
|
||||
'lampiran_tugas.required' => 'File jawaban wajib diupload.',
|
||||
'lampiran_tugas.mimes' => 'Format file harus pdf, doc, docx, jpg, atau png.',
|
||||
'lampiran_tugas.max' => 'Ukuran file maksimal 5MB.',
|
||||
]);
|
||||
|
||||
// Cek tugas valid untuk kelas siswa
|
||||
$tugas = Tugas::whereHas('mengajar', function ($q) use ($siswa) {
|
||||
$q->where('id_kelas', $siswa->id_kelas);
|
||||
})
|
||||
->where('id_tugas', $id_tugas)
|
||||
->firstOrFail();
|
||||
|
||||
// Cek sudah dikumpulkan belum
|
||||
$sudahAda = PengumpulanTugas::where('id_tugas', $id_tugas)
|
||||
->where('id_siswa', $siswa->id_siswa)
|
||||
->exists();
|
||||
|
||||
if ($sudahAda) {
|
||||
return back()->with('error', 'Kamu sudah mengumpulkan tugas ini.');
|
||||
}
|
||||
|
||||
// Upload file
|
||||
$file = $request->file('lampiran_tugas');
|
||||
$filename = 'tugas_' . $siswa->id_siswa . '_' . $id_tugas . '_' . time() . '.' . $file->getClientOriginalExtension();
|
||||
$path = $file->storeAs('pengumpulan_tugas', $filename, 'public');
|
||||
|
||||
// Tentukan status: terlambat atau dikumpulkan
|
||||
$now = Carbon::now();
|
||||
$deadline = Carbon::parse($tugas->deadline);
|
||||
$status = $now->greaterThan($deadline) ? 'terlambat' : 'dikumpulkan';
|
||||
|
||||
PengumpulanTugas::create([
|
||||
'id_tugas' => $id_tugas,
|
||||
'id_siswa' => $siswa->id_siswa,
|
||||
'lampiran_tugas' => $path,
|
||||
'tanggal_submit' => $now,
|
||||
'exp' => 0, // exp diberikan guru saat menilai
|
||||
'status' => $status,
|
||||
]);
|
||||
|
||||
$pesan = $status === 'terlambat'
|
||||
? 'Tugas berhasil dikumpulkan (terlambat).'
|
||||
: 'Tugas berhasil dikumpulkan! 🎉';
|
||||
|
||||
return redirect()->route('siswa.tugas.index')->with('success', $pesan);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ class Materi extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'materis';
|
||||
protected $table = 'materis';
|
||||
protected $primaryKey = 'id_materi';
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -19,4 +19,13 @@ class Materi extends Model
|
|||
'deskripsi',
|
||||
'tanggal_upload',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tanggal_upload' => 'datetime',
|
||||
];
|
||||
|
||||
public function mengajar()
|
||||
{
|
||||
return $this->belongsTo(Mengajar::class, 'id_mengajar', 'id_mengajar');
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ class Tugas extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'tugas';
|
||||
protected $table = 'tugas';
|
||||
protected $primaryKey = 'id_tugas';
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -18,4 +18,24 @@ class Tugas extends Model
|
|||
'keterangan',
|
||||
'deadline',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'deadline' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tugas dibuat melalui relasi mengajar (guru - mapel - kelas)
|
||||
*/
|
||||
public function mengajar()
|
||||
{
|
||||
return $this->belongsTo(Mengajar::class, 'id_mengajar', 'id_mengajar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Satu tugas bisa punya banyak pengumpulan dari siswa
|
||||
*/
|
||||
public function pengumpulanTugas()
|
||||
{
|
||||
return $this->hasMany(PengumpulanTugas::class, 'id_tugas', 'id_tugas');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@extends('layouts.siswa.app')
|
||||
@extends('siswa.layouts.app')
|
||||
|
||||
@section('title', 'Dashboard Siswa')
|
||||
|
||||
|
|
@ -319,7 +319,7 @@
|
|||
<div class="dash-card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Tugas</h2>
|
||||
<a href="{{ route('siswa.tugas') }}" class="card-link">LIHAT SEMUA</a>
|
||||
<a href="#" class="card-link">LIHAT SEMUA</a>
|
||||
</div>
|
||||
|
||||
@forelse($tugasList as $tanggal => $items)
|
||||
|
|
@ -381,7 +381,7 @@
|
|||
</div>
|
||||
|
||||
<div class="challenge-footer">
|
||||
<a href="{{ route('siswa.challenge') }}" class="card-link">LIHAT SEMUA</a>
|
||||
<a href="#" class="card-link">LIHAT SEMUA</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -408,7 +408,7 @@
|
|||
<div class="dash-card" style="grid-column: 1 / -1;">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Leaderboard</h2>
|
||||
<a href="{{ route('siswa.leaderboard') }}" class="card-link">LIHAT SEMUA</a>
|
||||
<a href="#" class="card-link">LIHAT SEMUA</a>
|
||||
</div>
|
||||
|
||||
@forelse($leaderboard as $lb)
|
||||
|
|
|
|||
|
|
@ -218,13 +218,13 @@ class="sidebar-link {{ request()->routeIs('siswa.dashboard') ? 'active' : '' }}"
|
|||
<span>Dashboard</span>
|
||||
</a>
|
||||
|
||||
<a href="#"
|
||||
<a href="{{ route('siswa.materi.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.materi*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/mapel.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Materi</span>
|
||||
</a>
|
||||
|
||||
<a href="#"
|
||||
<a href="{{ route('siswa.tugas.index') }}"
|
||||
class="sidebar-link {{ request()->routeIs('siswa.tugas*') ? 'active' : '' }}">
|
||||
<img src="{{ asset('images/icon/sidebar/siswa.png') }}" class="sidebar-icon" alt="">
|
||||
<span>Tugas</span>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
@extends('layouts.siswa.app')
|
||||
|
||||
@section('title', 'Materi')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mapel-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.mapel-card {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
border-top: 4px solid #2b8ef3;
|
||||
}
|
||||
|
||||
.mapel-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(43,142,243,0.15);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.mapel-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #e6f0ff;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.mapel-nama {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mapel-guru {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mapel-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 4px 10px;
|
||||
border-radius: 99px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.empty-state .empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
<h1 class="page-title">📚 Mata Pelajaran</h1>
|
||||
|
||||
@if($mapelList->isEmpty())
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p>Belum ada mata pelajaran untuk kelasmu.</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="mapel-grid">
|
||||
@foreach($mapelList as $mapel)
|
||||
<a href="{{ route('siswa.materi.show', $mapel['id_mengajar']) }}" class="mapel-card">
|
||||
<div class="mapel-icon">📖</div>
|
||||
<div>
|
||||
<p class="mapel-nama">{{ $mapel['nama_mapel'] }}</p>
|
||||
<p class="mapel-guru">{{ $mapel['nama_guru'] }}</p>
|
||||
</div>
|
||||
<div class="mapel-badge">
|
||||
📄 {{ $mapel['jumlah_materi'] }} Materi
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
@extends('layouts.siswa.app')
|
||||
|
||||
@section('title', 'Materi - ' . optional(optional($mengajar)->mapel)->nama_mapel)
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #2b8ef3;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
|
||||
.mapel-header {
|
||||
background: linear-gradient(135deg, #2b8ef3, #60a5fa);
|
||||
border-radius: 20px;
|
||||
padding: 24px 28px;
|
||||
color: white;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.mapel-header-icon {
|
||||
font-size: 36px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mapel-header-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.mapel-header-sub {
|
||||
font-size: 14px;
|
||||
opacity: 0.85;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.materi-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.materi-card {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 20px 24px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.materi-card:hover {
|
||||
box-shadow: 0 4px 20px rgba(43,142,243,0.12);
|
||||
}
|
||||
|
||||
.materi-file-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.materi-file-icon.pdf { background: #fee2e2; }
|
||||
.materi-file-icon.doc { background: #dbeafe; }
|
||||
.materi-file-icon.img { background: #dcfce7; }
|
||||
.materi-file-icon.other { background: #f1f5f9; }
|
||||
|
||||
.materi-info { flex: 1; }
|
||||
|
||||
.materi-judul {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.materi-meta {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.materi-desc {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
|
||||
.btn-download {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: background 0.2s;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-download:hover {
|
||||
background: #1a7ae0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-no-file {
|
||||
background: #f1f5f9;
|
||||
color: #94a3b8;
|
||||
border-radius: 10px;
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.empty-state .empty-icon { font-size: 48px; margin-bottom: 12px; }
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
<a href="{{ route('siswa.materi.index') }}" class="back-link">← Kembali ke Mata Pelajaran</a>
|
||||
|
||||
<div class="mapel-header">
|
||||
<div class="mapel-header-icon">📖</div>
|
||||
<div>
|
||||
<p class="mapel-header-title">{{ optional($mengajar->mapel)->nama_mapel ?? '-' }}</p>
|
||||
<p class="mapel-header-sub">Guru: {{ optional($mengajar->guru)->nama ?? '-' }} • {{ $materiList->count() }} Materi</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($materiList->isEmpty())
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p>Belum ada materi yang diupload untuk mata pelajaran ini.</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="materi-list">
|
||||
@foreach($materiList as $materi)
|
||||
@php
|
||||
$ext = $materi->lampiran_materi
|
||||
? strtolower(pathinfo($materi->lampiran_materi, PATHINFO_EXTENSION))
|
||||
: null;
|
||||
|
||||
$iconClass = match(true) {
|
||||
in_array($ext, ['pdf']) => 'pdf',
|
||||
in_array($ext, ['doc','docx']) => 'doc',
|
||||
in_array($ext, ['jpg','jpeg','png']) => 'img',
|
||||
default => 'other',
|
||||
};
|
||||
|
||||
$iconEmoji = match($iconClass) {
|
||||
'pdf' => '📄',
|
||||
'doc' => '📝',
|
||||
'img' => '🖼️',
|
||||
default => '📎',
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div class="materi-card">
|
||||
<div class="materi-file-icon {{ $iconClass }}">{{ $iconEmoji }}</div>
|
||||
|
||||
<div class="materi-info">
|
||||
<p class="materi-judul">{{ $materi->judul_materi }}</p>
|
||||
<p class="materi-meta">
|
||||
Diupload {{ \Carbon\Carbon::parse($materi->tanggal_upload)->locale('id')->isoFormat('D MMMM YYYY') }}
|
||||
</p>
|
||||
@if($materi->deskripsi)
|
||||
<p class="materi-desc">{{ $materi->deskripsi }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($materi->lampiran_materi)
|
||||
<a href="{{ asset('storage/' . $materi->lampiran_materi) }}"
|
||||
target="_blank"
|
||||
class="btn-download">
|
||||
⬇️ Unduh
|
||||
</a>
|
||||
@else
|
||||
<span class="btn-no-file">Tidak ada file</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
@extends('layouts.siswa.app')
|
||||
|
||||
@section('title', 'Tugas')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* TABS */
|
||||
.tab-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
border-bottom: 3px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
transition: all 0.2s;
|
||||
border-radius: 8px 8px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
color: #2b8ef3;
|
||||
border-bottom-color: #2b8ef3;
|
||||
}
|
||||
|
||||
.tab-btn:hover:not(.active) { color: #475569; }
|
||||
|
||||
.tab-count {
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
padding: 2px 8px;
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.tab-btn.active .tab-count {
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* TAB PANELS */
|
||||
.tab-panel { display: none; }
|
||||
.tab-panel.active { display: block; }
|
||||
|
||||
/* TUGAS CARD */
|
||||
.tugas-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.tugas-card {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 20px 24px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: box-shadow 0.2s, transform 0.2s;
|
||||
border-left: 4px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.tugas-card:hover {
|
||||
box-shadow: 0 4px 20px rgba(43,142,243,0.12);
|
||||
transform: translateX(4px);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tugas-card.status-belum { border-left-color: #2b8ef3; }
|
||||
.tugas-card.status-terlambat { border-left-color: #ef4444; }
|
||||
.tugas-card.status-selesai { border-left-color: #22c55e; }
|
||||
|
||||
.tugas-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-belum .tugas-icon { background: #e6f0ff; }
|
||||
.status-terlambat .tugas-icon { background: #fee2e2; }
|
||||
.status-selesai .tugas-icon { background: #dcfce7; }
|
||||
|
||||
.tugas-info { flex: 1; }
|
||||
|
||||
.tugas-judul {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.tugas-meta {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tugas-right {
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.deadline-label {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.deadline-val {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
padding: 3px 10px;
|
||||
border-radius: 99px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.badge-belum { background: #e6f0ff; color: #2b8ef3; }
|
||||
.badge-terlambat { background: #fee2e2; color: #ef4444; }
|
||||
.badge-selesai { background: #dcfce7; color: #16a34a; }
|
||||
|
||||
.exp-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background: #fef9c3;
|
||||
color: #b45309;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
padding: 3px 10px;
|
||||
border-radius: 99px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.empty-icon { font-size: 48px; margin-bottom: 12px; }
|
||||
|
||||
/* ALERT */
|
||||
.alert-success {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
border-radius: 12px;
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border-radius: 12px;
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
<h1 class="page-title">📋 Tugas</h1>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert-success">✅ {{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert-error">❌ {{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
{{-- TABS --}}
|
||||
<div class="tab-nav">
|
||||
<button class="tab-btn active" onclick="switchTab('belum', this)">
|
||||
Belum Dikerjakan
|
||||
<span class="tab-count">{{ $tugasBelum->count() }}</span>
|
||||
</button>
|
||||
<button class="tab-btn" onclick="switchTab('terlambat', this)">
|
||||
Terlambat
|
||||
<span class="tab-count">{{ $tugasTerlambat->count() }}</span>
|
||||
</button>
|
||||
<button class="tab-btn" onclick="switchTab('selesai', this)">
|
||||
Sudah Dikumpulkan
|
||||
<span class="tab-count">{{ $tugasSelesai->count() }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- TAB: BELUM --}}
|
||||
<div id="tab-belum" class="tab-panel active">
|
||||
@if($tugasBelum->isEmpty())
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🎉</div>
|
||||
<p>Semua tugas sudah dikerjakan!</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="tugas-list">
|
||||
@foreach($tugasBelum as $tugas)
|
||||
<a href="{{ route('siswa.tugas.show', $tugas['id_tugas']) }}"
|
||||
class="tugas-card status-belum">
|
||||
<div class="tugas-icon">📋</div>
|
||||
<div class="tugas-info">
|
||||
<p class="tugas-judul">{{ $tugas['judul'] }}</p>
|
||||
<p class="tugas-meta">{{ $tugas['nama_mapel'] }} • {{ $tugas['nama_guru'] }}</p>
|
||||
</div>
|
||||
<div class="tugas-right">
|
||||
<p class="deadline-label">Deadline</p>
|
||||
<p class="deadline-val">{{ $tugas['deadline']->format('d M Y H:i') }}</p>
|
||||
<span class="status-badge badge-belum">Belum</span>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- TAB: TERLAMBAT --}}
|
||||
<div id="tab-terlambat" class="tab-panel">
|
||||
@if($tugasTerlambat->isEmpty())
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">✅</div>
|
||||
<p>Tidak ada tugas terlambat!</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="tugas-list">
|
||||
@foreach($tugasTerlambat as $tugas)
|
||||
<a href="{{ route('siswa.tugas.show', $tugas['id_tugas']) }}"
|
||||
class="tugas-card status-terlambat">
|
||||
<div class="tugas-icon">⏰</div>
|
||||
<div class="tugas-info">
|
||||
<p class="tugas-judul">{{ $tugas['judul'] }}</p>
|
||||
<p class="tugas-meta">{{ $tugas['nama_mapel'] }} • {{ $tugas['nama_guru'] }}</p>
|
||||
</div>
|
||||
<div class="tugas-right">
|
||||
<p class="deadline-label">Deadline lewat</p>
|
||||
<p class="deadline-val">{{ $tugas['deadline']->format('d M Y H:i') }}</p>
|
||||
<span class="status-badge badge-terlambat">Terlambat</span>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- TAB: SELESAI --}}
|
||||
<div id="tab-selesai" class="tab-panel">
|
||||
@if($tugasSelesai->isEmpty())
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p>Belum ada tugas yang dikumpulkan.</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="tugas-list">
|
||||
@foreach($tugasSelesai as $tugas)
|
||||
<a href="{{ route('siswa.tugas.show', $tugas['id_tugas']) }}"
|
||||
class="tugas-card status-selesai">
|
||||
<div class="tugas-icon">✅</div>
|
||||
<div class="tugas-info">
|
||||
<p class="tugas-judul">{{ $tugas['judul'] }}</p>
|
||||
<p class="tugas-meta">{{ $tugas['nama_mapel'] }} • {{ $tugas['nama_guru'] }}</p>
|
||||
</div>
|
||||
<div class="tugas-right">
|
||||
<span class="status-badge badge-selesai">Dikumpulkan</span>
|
||||
@if($tugas['exp'] > 0)
|
||||
<br>
|
||||
<span class="exp-badge">⭐ {{ $tugas['exp'] }} EXP</span>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function switchTab(name, btn) {
|
||||
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('tab-' + name).classList.add('active');
|
||||
btn.classList.add('active');
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
|
|
@ -0,0 +1,409 @@
|
|||
@extends('layouts.siswa.app')
|
||||
|
||||
@section('title', $tugas->judul_tugas)
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #2b8ef3;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
|
||||
.tugas-header {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
padding: 28px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||
margin-bottom: 20px;
|
||||
border-left: 5px solid #2b8ef3;
|
||||
}
|
||||
|
||||
.tugas-header.terlambat { border-left-color: #ef4444; }
|
||||
.tugas-header.selesai { border-left-color: #22c55e; }
|
||||
|
||||
.tugas-judul {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.tugas-meta-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.meta-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
padding: 6px 12px;
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.meta-chip.deadline-chip {
|
||||
background: #fee2e2;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.meta-chip.deadline-chip.aman {
|
||||
background: #e6f0ff;
|
||||
color: #2b8ef3;
|
||||
}
|
||||
|
||||
.keterangan-box {
|
||||
background: #f8fafc;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
line-height: 1.7;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* SUBMIT FORM */
|
||||
.submit-card {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
padding: 28px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.submit-title {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed #cbd5e1;
|
||||
border-radius: 14px;
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.upload-area:hover, .upload-area.drag-over {
|
||||
border-color: #2b8ef3;
|
||||
background: #f0f7ff;
|
||||
}
|
||||
|
||||
.upload-area input[type="file"] {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-icon { font-size: 36px; margin-bottom: 10px; }
|
||||
|
||||
.upload-text {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.file-preview {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background: #f0f7ff;
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.file-preview.show { display: flex; }
|
||||
|
||||
.file-preview-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.file-preview-size {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
background: #2b8ef3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn-submit:hover { background: #1a7ae0; }
|
||||
.btn-submit:disabled {
|
||||
background: #cbd5e1;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* SUDAH KUMPUL */
|
||||
.sudah-kumpul-card {
|
||||
background: #dcfce7;
|
||||
border-radius: 20px;
|
||||
padding: 28px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sudah-kumpul-icon { font-size: 40px; margin-bottom: 10px; }
|
||||
|
||||
.sudah-kumpul-title {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #166534;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.sudah-kumpul-sub {
|
||||
font-size: 14px;
|
||||
color: #16a34a;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.exp-display {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #fef9c3;
|
||||
color: #b45309;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
padding: 6px 16px;
|
||||
border-radius: 99px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.terlambat-warning {
|
||||
background: #fff7ed;
|
||||
border: 1px solid #fed7aa;
|
||||
border-radius: 14px;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
font-size: 13px;
|
||||
color: #9a3412;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
border-radius: 12px;
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border-radius: 12px;
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
|
||||
<a href="{{ route('siswa.tugas.index') }}" class="back-link">← Kembali ke Daftar Tugas</a>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert-success">✅ {{ session('success') }}</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert-error">❌ {{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
{{-- DETAIL TUGAS --}}
|
||||
@php
|
||||
$headerClass = $sudahKumpul ? 'selesai' : ($terlambat ? 'terlambat' : '');
|
||||
$deadlineClass = $terlambat ? '' : 'aman';
|
||||
@endphp
|
||||
|
||||
<div class="tugas-header {{ $headerClass }}">
|
||||
<h1 class="tugas-judul">{{ $tugas->judul_tugas }}</h1>
|
||||
|
||||
<div class="tugas-meta-row">
|
||||
<span class="meta-chip">
|
||||
📚 {{ optional(optional($tugas->mengajar)->mapel)->nama_mapel ?? '-' }}
|
||||
</span>
|
||||
<span class="meta-chip">
|
||||
👨🏫 {{ optional(optional($tugas->mengajar)->guru)->nama ?? '-' }}
|
||||
</span>
|
||||
<span class="meta-chip deadline-chip {{ $deadlineClass }}">
|
||||
⏰ Deadline: {{ \Carbon\Carbon::parse($tugas->deadline)->format('d M Y, H:i') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if($tugas->keterangan)
|
||||
<div class="keterangan-box">
|
||||
{!! nl2br(e($tugas->keterangan)) !!}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- WARNING TERLAMBAT --}}
|
||||
@if($terlambat && !$sudahKumpul)
|
||||
<div class="terlambat-warning">
|
||||
<span style="font-size:20px">⚠️</span>
|
||||
<div>
|
||||
<strong>Deadline sudah lewat!</strong><br>
|
||||
Kamu masih bisa mengumpulkan tugas ini, tapi akan ditandai sebagai <strong>terlambat</strong>.
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- SUDAH DIKUMPULKAN --}}
|
||||
@if($sudahKumpul)
|
||||
<div class="sudah-kumpul-card">
|
||||
<div class="sudah-kumpul-icon">🎉</div>
|
||||
<p class="sudah-kumpul-title">Tugas sudah dikumpulkan!</p>
|
||||
<p class="sudah-kumpul-sub">
|
||||
Dikumpulkan pada {{ \Carbon\Carbon::parse($pengumpulan->tanggal_submit)->format('d M Y, H:i') }}
|
||||
• Status: <strong>{{ ucfirst($pengumpulan->status) }}</strong>
|
||||
</p>
|
||||
@if($pengumpulan->exp > 0)
|
||||
<div class="exp-display">⭐ {{ $pengumpulan->exp }} EXP didapat</div>
|
||||
@else
|
||||
<p style="font-size:13px;color:#16a34a;margin-top:8px">
|
||||
EXP akan diberikan setelah guru menilai tugasmu.
|
||||
</p>
|
||||
@endif
|
||||
|
||||
@if($pengumpulan->lampiran_tugas)
|
||||
<a href="{{ asset('storage/' . $pengumpulan->lampiran_tugas) }}"
|
||||
target="_blank"
|
||||
style="display:inline-flex;align-items:center;gap:6px;margin-top:14px;
|
||||
background:#fff;color:#2b8ef3;padding:8px 18px;border-radius:10px;
|
||||
font-weight:600;font-size:13px;text-decoration:none;">
|
||||
📎 Lihat File yang Dikumpulkan
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- FORM SUBMIT --}}
|
||||
@else
|
||||
<div class="submit-card">
|
||||
<p class="submit-title">📤 Upload Jawaban</p>
|
||||
|
||||
<form action="{{ route('siswa.tugas.submit', $tugas->id_tugas) }}"
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
id="submitForm">
|
||||
@csrf
|
||||
|
||||
@error('lampiran_tugas')
|
||||
<div class="alert-error" style="margin-bottom:16px">❌ {{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
<div class="upload-area" id="uploadArea">
|
||||
<input type="file"
|
||||
name="lampiran_tugas"
|
||||
id="fileInput"
|
||||
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
|
||||
required>
|
||||
<div class="upload-icon">☁️</div>
|
||||
<p class="upload-text"><strong>Klik untuk pilih file</strong> atau drag & drop di sini</p>
|
||||
<p class="upload-hint">PDF, DOC, DOCX, JPG, PNG • Maks. 5MB</p>
|
||||
</div>
|
||||
|
||||
<div class="file-preview" id="filePreview">
|
||||
<span style="font-size:22px">📎</span>
|
||||
<span class="file-preview-name" id="fileName">-</span>
|
||||
<span class="file-preview-size" id="fileSize">-</span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit" id="submitBtn" disabled>
|
||||
Kumpulkan Tugas
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const filePreview = document.getElementById('filePreview');
|
||||
const fileName = document.getElementById('fileName');
|
||||
const fileSize = document.getElementById('fileSize');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function () {
|
||||
if (this.files && this.files[0]) {
|
||||
const file = this.files[0];
|
||||
const size = (file.size / 1024 / 1024).toFixed(2);
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = size + ' MB';
|
||||
filePreview.classList.add('show');
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Drag & drop highlight
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('drag-over');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', () => {
|
||||
uploadArea.classList.remove('drag-over');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('drag-over');
|
||||
if (e.dataTransfer.files[0]) {
|
||||
fileInput.files = e.dataTransfer.files;
|
||||
fileInput.dispatchEvent(new Event('change'));
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
Loading…
Reference in New Issue