791 lines
35 KiB
PHP
791 lines
35 KiB
PHP
@extends('layouts.studentportalnav')
|
|
|
|
@section('title', 'Student Profile')
|
|
|
|
@section('content')
|
|
|
|
<!-- Cropper.js CSS & JS -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js"></script>
|
|
|
|
<style>
|
|
:root {
|
|
--theme-navy: #1E233E;
|
|
--theme-navy-dark: #15182C;
|
|
--theme-red: #822424;
|
|
--theme-red-hover: #df2c3e;
|
|
--theme-yellow: #FFEA85;
|
|
--theme-bg: #F4F6F9;
|
|
--text-dark: #1E293B;
|
|
}
|
|
|
|
/* Page Header Hero */
|
|
.profile-hero {
|
|
background: linear-gradient(135deg, var(--theme-navy) 0%, #2A1F3B 50%, #4A1A24 100%);
|
|
border-radius: 20px;
|
|
padding: 32px 38px;
|
|
margin-bottom: 28px;
|
|
box-shadow: 0 12px 32px rgba(30, 35, 62, 0.16);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.profile-hero::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%;
|
|
right: -10%;
|
|
width: 350px;
|
|
height: 350px;
|
|
background: radial-gradient(circle, rgba(255, 234, 133, 0.15) 0%, rgba(255, 234, 133, 0) 70%);
|
|
border-radius: 50%;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Profile Summary Card */
|
|
.profile-summary-card {
|
|
background: #ffffff;
|
|
border-radius: 20px;
|
|
padding: 36px 28px;
|
|
border: 1px solid #E2E8F0;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.profile-avatar-wrapper {
|
|
position: relative;
|
|
margin-bottom: 22px;
|
|
}
|
|
|
|
.profile-avatar-box {
|
|
width: 140px;
|
|
height: 140px;
|
|
border-radius: 50%;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.profile-avatar-img {
|
|
width: 140px;
|
|
height: 140px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
border: 4px solid var(--theme-navy);
|
|
box-shadow: 0 10px 28px rgba(30, 35, 62, 0.18);
|
|
background: #ffffff;
|
|
}
|
|
|
|
.profile-avatar-placeholder {
|
|
width: 140px;
|
|
height: 140px;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg, #1E233E 0%, #15182C 100%);
|
|
color: var(--theme-yellow);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 4px solid var(--theme-yellow);
|
|
box-shadow: 0 10px 28px rgba(30, 35, 62, 0.22);
|
|
}
|
|
|
|
.avatar-initial-letter {
|
|
font-size: 52px;
|
|
line-height: 1;
|
|
font-weight: 800;
|
|
color: var(--theme-yellow);
|
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.student-name-title {
|
|
color: var(--theme-navy);
|
|
font-weight: 800;
|
|
font-size: 1.4rem;
|
|
margin-bottom: 12px;
|
|
line-height: 1.3;
|
|
letter-spacing: -0.3px;
|
|
text-transform: capitalize;
|
|
}
|
|
|
|
.student-id-pill {
|
|
background: var(--theme-navy);
|
|
color: var(--theme-yellow);
|
|
padding: 10px 24px;
|
|
border-radius: 50px;
|
|
font-size: 0.85rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.5px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
box-shadow: 0 4px 14px rgba(30, 35, 62, 0.18);
|
|
}
|
|
|
|
.btn-edit-profile {
|
|
background: var(--theme-red);
|
|
color: #ffffff;
|
|
border: none;
|
|
border-radius: 12px;
|
|
padding: 14px 28px;
|
|
font-weight: 700;
|
|
font-size: 0.92rem;
|
|
box-shadow: 0 6px 18px rgba(130, 36, 36, 0.25);
|
|
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
width: 100%;
|
|
}
|
|
|
|
.btn-edit-profile:hover {
|
|
background: var(--theme-red-hover);
|
|
color: #ffffff;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 24px rgba(130, 36, 36, 0.35);
|
|
}
|
|
|
|
/* Info Cards */
|
|
.info-card-modern {
|
|
background: #ffffff;
|
|
border-radius: 20px;
|
|
padding: 30px;
|
|
border: 1px solid #E2E8F0;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
height: 100%;
|
|
}
|
|
|
|
.info-card-title {
|
|
color: var(--theme-navy);
|
|
font-weight: 800;
|
|
font-size: 1.15rem;
|
|
border-bottom: 2px solid var(--theme-yellow);
|
|
padding-bottom: 12px;
|
|
margin-bottom: 22px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.info-data-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 14px 0;
|
|
border-bottom: 1px solid #F1F5F9;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.info-data-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.info-data-label {
|
|
color: #64748B;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.info-data-value {
|
|
font-weight: 700;
|
|
color: #0F172A;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
/* Course Box Box */
|
|
.course-card-dark {
|
|
background: linear-gradient(135deg, var(--theme-navy) 0%, var(--theme-navy-dark) 100%);
|
|
color: #ffffff;
|
|
padding: 30px;
|
|
border-radius: 20px;
|
|
box-shadow: 0 10px 30px rgba(30, 35, 62, 0.2);
|
|
height: 100%;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.course-card-dark::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -40%;
|
|
right: -20%;
|
|
width: 280px;
|
|
height: 280px;
|
|
background: radial-gradient(circle, rgba(255, 234, 133, 0.08) 0%, rgba(255, 234, 133, 0) 70%);
|
|
border-radius: 50%;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.course-card-title {
|
|
color: var(--theme-yellow);
|
|
font-weight: 800;
|
|
font-size: 1.15rem;
|
|
margin-bottom: 22px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
padding-bottom: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.course-item-row {
|
|
margin-bottom: 16px;
|
|
font-size: 0.92rem;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.course-item-row:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.course-item-row i {
|
|
color: var(--theme-yellow);
|
|
width: 24px;
|
|
font-size: 1.1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Cropper Circular Mask styling */
|
|
.cropper-view-box,
|
|
.cropper-face {
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.btn-emoji-picker {
|
|
transition: transform 0.2s ease, background-color 0.2s ease;
|
|
}
|
|
|
|
.btn-emoji-picker:hover {
|
|
transform: scale(1.2);
|
|
background-color: #E2E8F0 !important;
|
|
}
|
|
|
|
.btn-avatar-color {
|
|
cursor: pointer;
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.btn-avatar-color:hover {
|
|
transform: scale(1.25);
|
|
}
|
|
</style>
|
|
|
|
<div class="portal-content-container">
|
|
|
|
@if(session('success'))
|
|
<div class="alert alert-success alert-dismissible fade show rounded-4 border-0 shadow-sm mb-4" role="alert">
|
|
<i class="fa-solid fa-circle-check me-2"></i> {{ session('success') }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('error'))
|
|
<div class="alert alert-danger alert-dismissible fade show rounded-4 border-0 shadow-sm mb-4" role="alert">
|
|
<i class="fa-solid fa-triangle-exclamation me-2"></i> {{ session('error') }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Hero Header -->
|
|
<div class="profile-hero d-flex align-items-center justify-content-between flex-wrap gap-3">
|
|
<div class="position-relative" style="z-index: 1;">
|
|
<div class="d-flex align-items-center gap-2 mb-2">
|
|
<span class="badge bg-warning bg-opacity-20 text-warning border border-warning border-opacity-20 px-3 py-1.5 rounded-pill fw-semibold" style="font-size: 0.78rem;">
|
|
<i class="fa-solid fa-user-check me-1"></i> Verified Student Profile
|
|
</span>
|
|
</div>
|
|
<h2 class="fw-bold text-white mb-1" style="letter-spacing: -0.5px;">
|
|
<i class="fa-solid fa-user-graduate me-2" style="color: var(--theme-yellow);"></i>Student Profile
|
|
</h2>
|
|
<p class="text-white-50 mb-0 fs-6">
|
|
Manage your personal information, credentials, and academic details.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Profile Row -->
|
|
<div class="row g-4 mb-4">
|
|
<!-- Profile Summary Card -->
|
|
<div class="col-lg-4">
|
|
<div class="profile-summary-card">
|
|
<div class="profile-avatar-wrapper">
|
|
<div class="profile-avatar-box">
|
|
@if(!empty($student->image))
|
|
<img src="{{ asset($student->image) }}" class="profile-avatar-img" alt="{{ $student->full_name }}" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
|
@endif
|
|
<div class="profile-avatar-placeholder" style="{{ !empty($student->image) ? 'display: none;' : 'display: flex;' }}">
|
|
<span class="avatar-initial-letter">{{ strtoupper(substr($student->full_name ?? 'S', 0, 1)) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="student-name-title">{{ $student->full_name }}</h3>
|
|
|
|
<div class="mb-4">
|
|
<span class="student-id-pill">
|
|
<i class="fa-solid fa-id-badge text-warning"></i> Student ID : {{ $student->student_id }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="w-100 pt-3 border-top border-slate-100 mt-auto">
|
|
<button class="btn btn-edit-profile" data-bs-toggle="modal" data-bs-target="#editProfileModal">
|
|
<i class="fa-solid fa-pen-to-square me-2"></i> Edit Profile
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Personal Details -->
|
|
<div class="col-lg-8">
|
|
<div class="info-card-modern">
|
|
<h4 class="info-card-title">
|
|
<i class="fa-solid fa-id-card text-danger"></i> Personal Information
|
|
</h4>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-regular fa-user text-muted"></i> Full Name</span>
|
|
<span class="info-data-value text-capitalize">{{ $student->full_name }}</span>
|
|
</div>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-regular fa-address-card text-muted"></i> NIC / Passport</span>
|
|
<span class="info-data-value">{{ $student->nic_passport }}</span>
|
|
</div>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-regular fa-calendar text-muted"></i> Date of Birth</span>
|
|
<span class="info-data-value">
|
|
{{ !empty($student->date_of_birth) ? date('d F Y', strtotime($student->date_of_birth)) : 'N/A' }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-solid fa-venus-mars text-muted"></i> Gender</span>
|
|
<span class="info-data-value text-capitalize">{{ $student->gender }}</span>
|
|
</div>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-regular fa-envelope text-muted"></i> Email Address</span>
|
|
<span class="info-data-value text-primary">{{ $student->email }}</span>
|
|
</div>
|
|
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-solid fa-phone text-muted"></i> Phone Number</span>
|
|
<span class="info-data-value">{{ $student->phone }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Education & Course Details Row -->
|
|
<div class="row g-4 mb-5">
|
|
<!-- Educational Background -->
|
|
<div class="col-lg-6">
|
|
<div class="info-card-modern">
|
|
<h4 class="info-card-title">
|
|
<i class="fa-solid fa-graduation-cap text-danger"></i> Educational Background
|
|
</h4>
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-solid fa-certificate text-muted"></i> Qualification</span>
|
|
<span class="info-data-value">{{ $student->qualification }}</span>
|
|
</div>
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-solid fa-building-columns text-muted"></i> Institute</span>
|
|
<span class="info-data-value">{{ $student->institute }}</span>
|
|
</div>
|
|
<div class="info-data-row">
|
|
<span class="info-data-label"><i class="fa-regular fa-calendar-check text-muted"></i> Year Completed</span>
|
|
<span class="info-data-value">{{ $student->year_completed }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Course Details -->
|
|
<div class="col-lg-6">
|
|
<div class="course-card-dark">
|
|
<h4 class="course-card-title">
|
|
<i class="fa-solid fa-car-side"></i> Course Details
|
|
</h4>
|
|
|
|
<div class="course-item-row">
|
|
<i class="fa-solid fa-book"></i>
|
|
<div>
|
|
<span class="text-white-50 d-block small" style="font-size: 0.75rem;">Enrolled Course</span>
|
|
<strong class="text-white fs-6">{{ $student->course_name }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="course-item-row">
|
|
<i class="fa-solid fa-clock"></i>
|
|
<div>
|
|
<span class="text-white-50 d-block small" style="font-size: 0.75rem;">Duration</span>
|
|
<strong class="text-white">{{ $student->duration }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="course-item-row">
|
|
<i class="fa-solid fa-layer-group"></i>
|
|
<div>
|
|
<span class="text-white-50 d-block small" style="font-size: 0.75rem;">Current Semester</span>
|
|
<strong class="text-white">{{ $student->current_semester }}</strong>
|
|
</div>
|
|
</div>
|
|
<div class="course-item-row">
|
|
<i class="fa-solid fa-user-tie"></i>
|
|
<div>
|
|
<span class="text-white-50 d-block small" style="font-size: 0.75rem;">Instructor / Trainer</span>
|
|
<strong class="text-white">{{ $student->trainer_name }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Edit Profile Modal -->
|
|
<!-- ========================================== -->
|
|
<!-- 1. MAIN EDIT PROFILE MODAL -->
|
|
<!-- ========================================== -->
|
|
<div class="modal fade" id="editProfileModal" tabindex="-1" aria-labelledby="editProfileModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content text-white style-glass-modal border-0 shadow-lg" style="background: #11152B; border-radius: 20px;">
|
|
<div class="modal-header border-bottom border-secondary border-opacity-25 px-4 py-3">
|
|
<h5 class="modal-title fw-bold d-flex align-items-center" id="editProfileModalLabel">
|
|
<i class="bi bi-pencil-square text-primary me-2"></i> Edit Profile Information
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<form action="{{ route('student.profile.update') }}" method="POST" enctype="multipart/form-data" id="editProfileForm">
|
|
@csrf
|
|
@method('PUT')
|
|
<div class="modal-body p-4">
|
|
|
|
<!-- Profile Picture Management Section -->
|
|
<div class="card bg-dark bg-opacity-50 border border-secondary border-opacity-25 rounded-4 p-3 mb-4">
|
|
<div class="row align-items-center g-3">
|
|
<div class="col-auto">
|
|
<div class="position-relative">
|
|
<div id="modal_avatar_preview_wrapper" class="rounded-circle overflow-hidden d-flex align-items-center justify-content-center border border-2 border-primary" style="width: 90px; height: 90px; background: #1E233E;">
|
|
@if(isset($student->image) && $student->image)
|
|
<img id="modal_avatar_img" src="{{ asset($student->image) }}" alt="Profile Picture" class="w-100 h-100 object-fit-cover">
|
|
<div id="modal_avatar_placeholder_inner" class="d-flex align-items-center justify-content-center w-100 h-100" style="display: none !important;">
|
|
<span id="avatar_emoji_or_initial" class="fs-2 fw-bold text-white">{{ strtoupper(substr($student->full_name ?? 'S', 0, 1)) }}</span>
|
|
</div>
|
|
@else
|
|
<img id="modal_avatar_img" src="" alt="Profile Picture" class="w-100 h-100 object-fit-cover" style="display: none;">
|
|
<div id="modal_avatar_placeholder_inner" class="d-flex align-items-center justify-content-center w-100 h-100">
|
|
<span id="avatar_emoji_or_initial" class="fs-2 fw-bold text-white">{{ strtoupper(substr($student->full_name ?? 'S', 0, 1)) }}</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col">
|
|
<h6 class="fw-bold mb-1 text-white">Profile Picture</h6>
|
|
<p class="text-white-50 small mb-2">Upload a photo, create an emoji avatar, or remove picture.</p>
|
|
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<!-- Upload Photo Button -->
|
|
<label class="btn btn-sm btn-outline-light rounded-pill px-3 py-1 cursor-pointer">
|
|
<i class="bi bi-camera me-1"></i> Upload Photo
|
|
<input type="file" id="profile_image_file_input" accept="image/*" class="d-none">
|
|
</label>
|
|
|
|
<!-- Emoji Picker Dropdown -->
|
|
<div class="dropdown">
|
|
<button class="btn btn-sm btn-outline-info rounded-pill px-3 py-1 dropdown-toggle" type="button" id="emojiDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
|
<i class="bi bi-emoji-smile me-1"></i> Choose Emoji
|
|
</button>
|
|
<div class="dropdown-menu dropdown-menu-dark p-3 shadow-lg border-secondary" aria-labelledby="emojiDropdown" style="width: 260px; background: #1B203E;">
|
|
<p class="small text-white-50 mb-2">Select Avatar Emoji:</p>
|
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="👨💻">👨💻</button>
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="👩💻">👩💻</button>
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="🎓">🎓</button>
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="🚀">🚀</button>
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="🔥">🔥</button>
|
|
<button type="button" class="btn btn-dark btn-sm btn-emoji-picker fs-5" data-emoji="⚡">⚡</button>
|
|
</div>
|
|
<p class="small text-white-50 mb-2">Background Color:</p>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn rounded-circle p-2 btn-avatar-color border border-light" data-color="#1E233E" style="background-color: #1E233E; width: 24px; height: 24px;"></button>
|
|
<button type="button" class="btn rounded-circle p-2 btn-avatar-color border border-light" data-color="#0d6efd" style="background-color: #0d6efd; width: 24px; height: 24px;"></button>
|
|
<button type="button" class="btn rounded-circle p-2 btn-avatar-color border border-light" data-color="#198754" style="background-color: #198754; width: 24px; height: 24px;"></button>
|
|
<button type="button" class="btn rounded-circle p-2 btn-avatar-color border border-light" data-color="#dc3545" style="background-color: #dc3545; width: 24px; height: 24px;"></button>
|
|
<button type="button" class="btn rounded-circle p-2 btn-avatar-color border border-light" data-color="#6f42c1" style="background-color: #6f42c1; width: 24px; height: 24px;"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Remove Photo Button -->
|
|
<button type="button" class="btn btn-sm btn-outline-danger rounded-pill px-3 py-1" id="btnRemovePhoto">
|
|
<i class="bi bi-trash me-1"></i> Remove
|
|
</button>
|
|
</div>
|
|
<div id="remove_photo_status" class="small text-warning mt-2 d-none">
|
|
<i class="bi bi-exclamation-triangle me-1"></i> Photo will be removed upon saving.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hidden Inputs for Profile Image Handling -->
|
|
<input type="hidden" name="cropped_image_data" id="cropped_image_data">
|
|
<input type="hidden" name="remove_profile_image" id="remove_profile_image" value="0">
|
|
|
|
<!-- Personal Information Form Inputs -->
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label for="full_name" class="form-label text-white-50 small mb-1">Full Name</label>
|
|
<input type="text" class="form-control text-white border-secondary border-opacity-50" id="full_name" name="full_name" value="{{ old('full_name', $student->full_name ?? '') }}" style="background: #1B203E;" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="email" class="form-label text-white-50 small mb-1">Email Address</label>
|
|
<input type="email" class="form-control text-white border-secondary border-opacity-50" id="email" name="email" value="{{ old('email', $student->email ?? '') }}" style="background: #1B203E;" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="phone" class="form-label text-white-50 small mb-1">Phone Number</label>
|
|
<input type="text" class="form-control text-white border-secondary border-opacity-50" id="phone" name="phone" value="{{ old('phone', $student->phone ?? '') }}" style="background: #1B203E;">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="nic_passport" class="form-label text-white-50 small mb-1">NIC / Passport</label>
|
|
<input type="text" class="form-control text-white border-secondary border-opacity-50" id="nic_passport" name="nic_passport" value="{{ old('nic_passport', $student->nic_passport ?? '') }}" style="background: #1B203E;">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="date_of_birth" class="form-label text-white-50 small mb-1">Date of Birth</label>
|
|
<input type="date" class="form-control text-white border-secondary border-opacity-50" id="date_of_birth" name="date_of_birth" value="{{ old('date_of_birth', $student->date_of_birth ?? '') }}" style="background: #1B203E;">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="gender" class="form-label text-white-50 small mb-1">Gender</label>
|
|
<select class="form-select text-white border-secondary border-opacity-50" id="gender" name="gender" style="background: #1B203E;">
|
|
<option value="" class="bg-dark">Select Gender</option>
|
|
<option value="Male" {{ old('gender', $student->gender ?? '') == 'Male' ? 'selected' : '' }} class="bg-dark">Male</option>
|
|
<option value="Female" {{ old('gender', $student->gender ?? '') == 'Female' ? 'selected' : '' }} class="bg-dark">Female</option>
|
|
<option value="Other" {{ old('gender', $student->gender ?? '') == 'Other' ? 'selected' : '' }} class="bg-dark">Other</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="qualification" class="form-label text-white-50 small mb-1">Highest Qualification</label>
|
|
<input type="text" class="form-control text-white border-secondary border-opacity-50" id="qualification" name="qualification" value="{{ old('qualification', $student->qualification ?? '') }}" style="background: #1B203E;">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="institute" class="form-label text-white-50 small mb-1">Institute / University</label>
|
|
<input type="text" class="form-control text-white border-secondary border-opacity-50" id="institute" name="institute" value="{{ old('institute', $student->institute ?? '') }}" style="background: #1B203E;">
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer border-top border-secondary border-opacity-25 px-4 py-3">
|
|
<button type="button" class="btn btn-outline-light rounded-pill px-4" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary rounded-pill px-4">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========================================== -->
|
|
<!-- 2. SEPARATE CROPPER MODAL (High Z-Index) -->
|
|
<!-- ========================================== -->
|
|
<div class="modal fade" id="cropImageModal" tabindex="-1" aria-labelledby="cropImageModalLabel" aria-hidden="true" data-bs-backdrop="static" style="z-index: 1060;">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content text-white" style="background: #11152B; border-radius: 20px;">
|
|
<div class="modal-header border-bottom border-secondary border-opacity-25">
|
|
<h5 class="modal-title fw-bold" id="cropImageModalLabel">Crop Profile Image</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body p-3">
|
|
<div class="img-container overflow-hidden rounded-3" style="max-height: 380px; background: #000;">
|
|
<img id="cropper_image_target" src="" alt="Source Image" style="max-width: 100%; display: block;">
|
|
</div>
|
|
<div class="d-flex justify-content-center gap-2 mt-3">
|
|
<button type="button" class="btn btn-sm btn-dark border-secondary text-white" id="btnZoomIn"><i class="bi bi-zoom-in"></i> Zoom In</button>
|
|
<button type="button" class="btn btn-sm btn-dark border-secondary text-white" id="btnZoomOut"><i class="bi bi-zoom-out"></i> Zoom Out</button>
|
|
<button type="button" class="btn btn-sm btn-dark border-secondary text-white" id="btnRotateRight"><i class="bi bi-arrow-clockwise"></i> Rotate</button>
|
|
<button type="button" class="btn btn-sm btn-dark border-secondary text-white" id="btnResetCrop"><i class="bi bi-arrow-counterclockwise"></i> Reset</button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-top border-secondary border-opacity-25">
|
|
<button type="button" class="btn btn-outline-light rounded-pill px-3" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary rounded-pill px-4" id="btnApplyCrop">Crop & Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========================================== -->
|
|
<!-- 3. DEPENDENCIES & JAVASCRIPT LOGIC -->
|
|
<!-- ========================================== -->
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
let cropper = null;
|
|
let selectedBgColor = '#1E233E';
|
|
let currentEmoji = '';
|
|
|
|
const fileInput = document.getElementById('profile_image_file_input');
|
|
const cropperImgTarget = document.getElementById('cropper_image_target');
|
|
const croppedImageDataInput = document.getElementById('cropped_image_data');
|
|
const removeProfileImageInput = document.getElementById('remove_profile_image');
|
|
|
|
const modalAvatarImg = document.getElementById('modal_avatar_img');
|
|
const modalAvatarPlaceholder = document.getElementById('modal_avatar_placeholder_inner');
|
|
const removePhotoStatus = document.getElementById('remove_photo_status');
|
|
|
|
const cropImageModalEl = document.getElementById('cropImageModal');
|
|
const cropImageModal = new bootstrap.Modal(cropImageModalEl);
|
|
|
|
// Fix Backdrop Z-Index for Nested Modals
|
|
cropImageModalEl.addEventListener('show.bs.modal', function () {
|
|
setTimeout(() => {
|
|
const backdrops = document.querySelectorAll('.modal-backdrop');
|
|
if (backdrops.length > 1) {
|
|
backdrops[backdrops.length - 1].style.zIndex = '1055';
|
|
}
|
|
}, 10);
|
|
});
|
|
|
|
// 1. File Selection & Open Cropper Modal
|
|
fileInput.addEventListener('change', function (e) {
|
|
const files = e.target.files;
|
|
if (files && files.length > 0) {
|
|
const file = files[0];
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = function (event) {
|
|
cropperImgTarget.src = event.target.result;
|
|
cropImageModal.show();
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
});
|
|
|
|
// 2. Initialize Cropper on Modal Show
|
|
cropImageModalEl.addEventListener('shown.bs.modal', function () {
|
|
if (cropper) {
|
|
cropper.destroy();
|
|
}
|
|
cropper = new Cropper(cropperImgTarget, {
|
|
aspectRatio: 1,
|
|
viewMode: 1,
|
|
autoCropArea: 0.9,
|
|
responsive: true,
|
|
restore: false,
|
|
guides: true,
|
|
center: true,
|
|
highlight: false,
|
|
cropBoxMovable: true,
|
|
cropBoxResizable: true,
|
|
toggleDragModeOnDblclick: false,
|
|
});
|
|
});
|
|
|
|
// 3. Destroy Cropper on Modal Hide
|
|
cropImageModalEl.addEventListener('hidden.bs.modal', function () {
|
|
if (cropper) {
|
|
cropper.destroy();
|
|
cropper = null;
|
|
}
|
|
fileInput.value = '';
|
|
});
|
|
|
|
// 4. Cropper Controls
|
|
document.getElementById('btnZoomIn').addEventListener('click', () => cropper && cropper.zoom(0.1));
|
|
document.getElementById('btnZoomOut').addEventListener('click', () => cropper && cropper.zoom(-0.1));
|
|
document.getElementById('btnRotateRight').addEventListener('click', () => cropper && cropper.rotate(90));
|
|
document.getElementById('btnResetCrop').addEventListener('click', () => cropper && cropper.reset());
|
|
|
|
// 5. Apply Cropped Image
|
|
document.getElementById('btnApplyCrop').addEventListener('click', function () {
|
|
if (!cropper) return;
|
|
|
|
const canvas = cropper.getCroppedCanvas({
|
|
width: 300,
|
|
height: 300,
|
|
imageSmoothingEnabled: true,
|
|
imageSmoothingQuality: 'high',
|
|
});
|
|
|
|
const base64Image = canvas.toDataURL('image/jpeg', 0.9);
|
|
|
|
croppedImageDataInput.value = base64Image;
|
|
removeProfileImageInput.value = "0";
|
|
|
|
modalAvatarImg.src = base64Image;
|
|
modalAvatarImg.style.display = 'block';
|
|
if (modalAvatarPlaceholder) modalAvatarPlaceholder.style.display = 'none';
|
|
removePhotoStatus.classList.add('d-none');
|
|
|
|
cropImageModal.hide();
|
|
});
|
|
|
|
// 6. Remove Photo Action
|
|
document.getElementById('btnRemovePhoto').addEventListener('click', function () {
|
|
croppedImageDataInput.value = '';
|
|
removeProfileImageInput.value = "1";
|
|
|
|
modalAvatarImg.style.display = 'none';
|
|
modalAvatarImg.src = '';
|
|
if (modalAvatarPlaceholder) modalAvatarPlaceholder.style.display = 'flex';
|
|
removePhotoStatus.classList.remove('d-none');
|
|
});
|
|
|
|
// 7. Emoji Avatar Picker
|
|
document.querySelectorAll('.btn-emoji-picker').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
currentEmoji = this.getAttribute('data-emoji');
|
|
generateEmojiAvatar();
|
|
});
|
|
});
|
|
|
|
// 8. Avatar Background Color Picker
|
|
document.querySelectorAll('.btn-avatar-color').forEach(btn => {
|
|
btn.addEventListener('click', function () {
|
|
selectedBgColor = this.getAttribute('data-color');
|
|
generateEmojiAvatar();
|
|
});
|
|
});
|
|
|
|
// 9. Generate Canvas Based Emoji Avatar
|
|
function generateEmojiAvatar() {
|
|
if (!currentEmoji) return;
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = 300;
|
|
canvas.height = 300;
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
ctx.fillStyle = selectedBgColor;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.font = '140px sans-serif';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'middle';
|
|
ctx.fillText(currentEmoji, canvas.width / 2, canvas.height / 2 + 10);
|
|
|
|
const base64Image = canvas.toDataURL('image/png');
|
|
|
|
croppedImageDataInput.value = base64Image;
|
|
removeProfileImageInput.value = "0";
|
|
|
|
modalAvatarImg.src = base64Image;
|
|
modalAvatarImg.style.display = 'block';
|
|
if (modalAvatarPlaceholder) modalAvatarPlaceholder.style.display = 'none';
|
|
removePhotoStatus.classList.add('d-none');
|
|
}
|
|
});
|
|
</script>
|
|
@endsection |