academy/resources/views/StudentProfile.blade.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