penambahan sertifikat
This commit is contained in:
parent
51a5631b74
commit
40f7a8e3c2
@ -137,7 +137,7 @@ class PlayerController extends Controller
|
|||||||
if ($certificate->count() == 0) {
|
if ($certificate->count() == 0) {
|
||||||
$certificate_data['user_id'] = auth()->user()->id;
|
$certificate_data['user_id'] = auth()->user()->id;
|
||||||
$certificate_data['course_id'] = $request->course_id;
|
$certificate_data['course_id'] = $request->course_id;
|
||||||
$certificate_data['identifier'] = random(12);
|
$certificate_data['identifier'] = $this->generateIdentifier(12);
|
||||||
$certificate_data['created_at'] = date('Y-m-d H:i:s');
|
$certificate_data['created_at'] = date('Y-m-d H:i:s');
|
||||||
Certificate::insert($certificate_data);
|
Certificate::insert($certificate_data);
|
||||||
}
|
}
|
||||||
@ -146,6 +146,22 @@ class PlayerController extends Controller
|
|||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function generateIdentifier($length = 12)
|
||||||
|
{
|
||||||
|
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
$charactersLength = strlen($characters);
|
||||||
|
$randomString = '';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $randomString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function prepend_watermark()
|
public function prepend_watermark()
|
||||||
{
|
{
|
||||||
return view('course_player.watermark');
|
return view('course_player.watermark');
|
||||||
|
|||||||
@ -60,6 +60,8 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
$page_data['certificate'] = $certificate->first();
|
$page_data['certificate'] = $certificate->first();
|
||||||
$page_data['qrcode'] = $qrcode;
|
$page_data['qrcode'] = $qrcode;
|
||||||
|
|
||||||
|
// dd($qr_code_content_value);
|
||||||
return view('curriculum.certificate.download', $page_data);
|
return view('curriculum.certificate.download', $page_data);
|
||||||
} else {
|
} else {
|
||||||
return redirect(route('home'))->with('error', get_phrase('Certificate not found at this url'));
|
return redirect(route('home'))->with('error', get_phrase('Certificate not found at this url'));
|
||||||
|
|||||||
@ -208,7 +208,7 @@ class QuizController extends Controller
|
|||||||
Certificate::create([
|
Certificate::create([
|
||||||
'user_id' => $student_id,
|
'user_id' => $student_id,
|
||||||
'course_id' => $course_id,
|
'course_id' => $course_id,
|
||||||
'identifier' => substr(md5(uniqid(mt_rand(), true)), 0, 10), // Generate random ID
|
'identifier' => $this->generateIdentifier(12),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,6 +216,18 @@ class QuizController extends Controller
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function generateIdentifier($length = 12)
|
||||||
|
{
|
||||||
|
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
$charactersLength = strlen($characters);
|
||||||
|
$randomString = '';
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $randomString;
|
||||||
|
}
|
||||||
|
|
||||||
public function load_result(Request $request)
|
public function load_result(Request $request)
|
||||||
{
|
{
|
||||||
$page_data['quiz'] = Lesson::where('id', $request->quiz_id)->first();
|
$page_data['quiz'] = Lesson::where('id', $request->quiz_id)->first();
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 228 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@ -15,20 +15,31 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{{ asset('assets/backend/css/style.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ asset('assets/backend/css/style.css') }}">
|
||||||
|
|
||||||
|
{{-- Font Awesome untuk ikon tambahan --}}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ asset('assets/backend/js/jquery-3.7.1.min.js') }}"></script>
|
<script type="text/javascript" src="{{ asset('assets/backend/js/jquery-3.7.1.min.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ asset('assets/global/jquery-ui-1.13.2/jquery-ui.min.js') }}"></script>
|
<script type="text/javascript" src="{{ asset('assets/global/jquery-ui-1.13.2/jquery-ui.min.js') }}"></script>
|
||||||
<script src="{{ asset('assets/backend/vendors/bootstrap/bootstrap.bundle.min.js') }}"></script>
|
<script src="{{ asset('assets/backend/vendors/bootstrap/bootstrap.bundle.min.js') }}"></script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Inter', 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable {
|
.draggable {
|
||||||
border: 2px dashed rgb(255, 255, 255);
|
border: 2px dashed rgba(255, 255, 255, 0.8);
|
||||||
cursor: move;
|
cursor: move;
|
||||||
background-color: #15b57e33;
|
background-color: #15b57e33;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable:hover {
|
||||||
|
border-color: #15b57e;
|
||||||
|
box-shadow: 0 6px 12px rgba(21, 181, 126, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden-position:not(.certificate-layout-module) {
|
.hidden-position:not(.certificate-layout-module) {
|
||||||
@ -48,17 +59,20 @@
|
|||||||
|
|
||||||
.certificate-layout-module {
|
.certificate-layout-module {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: -300px;
|
right: -350px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
width: 300px;
|
width: 350px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
transition: right 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
box-shadow: -5px 0 25px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar.open {
|
.sidebar.open {
|
||||||
@ -67,26 +81,49 @@
|
|||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 15px;
|
||||||
|
background: rgba(0, 17, 81, 1);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-toggle {
|
.sidebar-toggle {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 10px;
|
top: 20px;
|
||||||
right: 10px;
|
right: 20px;
|
||||||
z-index: 150;
|
z-index: 150;
|
||||||
|
background: rgba(0, 17, 81, 1);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-item {
|
.remove-item {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -20px;
|
top: -10px;
|
||||||
right: -17px;
|
right: -10px;
|
||||||
background-color: white;
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 2px;
|
padding: 4px;
|
||||||
height: 20px;
|
height: 24px;
|
||||||
width: 20px;
|
width: 24px;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-item:hover {
|
||||||
|
background-color: #c82333;
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
i:not(.fas, .fa, .fab) {
|
i:not(.fas, .fa, .fab) {
|
||||||
@ -96,23 +133,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dotted-background {
|
.dotted-background {
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
background-image: radial-gradient(circle, #afafaf 1px, transparent 1px);
|
background-image: radial-gradient(circle, #afafaf 1px, transparent 1px);
|
||||||
background-size: 10px 10px;
|
background-size: 20px 20px;
|
||||||
/* Adjust size of dots as needed */
|
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-color: #e4e5ff;
|
background-color: #f0f2f5;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-left: 10px;
|
padding: 30px;
|
||||||
padding-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
@ -120,97 +152,401 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-body {
|
.sidebar-body {
|
||||||
height: 100%;
|
height: calc(100% - 60px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-family-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-family-option:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-family-option.active {
|
||||||
|
background-color: #e8eeff;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-family-preview {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
margin: 2px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control, .form-select {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
padding: 10px 15px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus, .form-select:focus {
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ol-btn-light-primary {
|
||||||
|
background-color: #e8eeff;
|
||||||
|
color: #667eea;
|
||||||
|
border: 1px solid #c2d0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ol-btn-light-primary:hover {
|
||||||
|
background-color: #d5deff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ol-btn-primary {
|
||||||
|
background: rgba(0, 17, 81, 1);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ol-btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-style-options {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-style-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
background: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-style-btn:hover {
|
||||||
|
border-color: #667eea;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-style-btn.active {
|
||||||
|
background-color: #667eea;
|
||||||
|
color: white;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-preview {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-weight-slider {
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-decoration-options {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-decoration-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
background: white;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-decoration-btn:hover {
|
||||||
|
border-color: #667eea;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-decoration-btn.active {
|
||||||
|
background-color: #667eea;
|
||||||
|
color: white;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-option-group {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-option-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a onclick="$('.sidebar').addClass('open')" href="#" class="sidebar-toggle">
|
<a onclick="$('.sidebar').addClass('open')" href="#" class="sidebar-toggle">
|
||||||
<svg width="40px" height="40px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<i class="fas fa-sliders-h" style="color: white; font-size: 20px;"></i>
|
||||||
<g id="SVGRepo_bgCarrier" stroke-width="0" />
|
|
||||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
|
|
||||||
<g id="SVGRepo_iconCarrier">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 5C3.44772 5 3 5.44772 3 6C3 6.55228 3.44772 7 4 7H20C20.5523 7 21 6.55228 21 6C21 5.44772 20.5523 5 20 5H4ZM7 12C7 11.4477 7.44772 11 8 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H8C7.44772 13 7 12.5523 7 12ZM13 18C13 17.4477 13.4477 17 14 17H20C20.5523 17 21 17.4477 21 18C21 18.5523 20.5523 19 20 19H14C13.4477 19 13 18.5523 13 18Z" fill="#000000" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="sidebar open">
|
<div class="sidebar open">
|
||||||
<div class="sidebar-header border-bottom d-flex align-items-center">
|
<div class="sidebar-header border-bottom d-flex align-items-center">
|
||||||
<a class="btn" href="#" onclick="$('.sidebar').removeClass('open')">
|
<a class="btn text-white" href="#" onclick="$('.sidebar').removeClass('open')">
|
||||||
<i class="fi-rr-cross-small"></i>
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</a>
|
||||||
|
<span class="ms-2 fw-bold">{{ get_phrase('Certificate Designer') }}</span>
|
||||||
|
<a class="ms-auto btn btn-sm btn-outline-light" href="{{ route('admin.certificate.settings') }}">
|
||||||
|
<i class="fas fa-arrow-left me-1"></i>{{ get_phrase('Back') }}
|
||||||
</a>
|
</a>
|
||||||
{{ get_phrase('Certificate elements') }}
|
|
||||||
<a class="ms-auto" href="{{ route('admin.certificate.settings') }}">{{ get_phrase('Back') }}</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sidebar-body">
|
<div class="sidebar-body">
|
||||||
<div class="card border-0 m-2">
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
<div class="card-body pt-0">
|
<div class="card-body">
|
||||||
<h6 class="card-title mt-3">{{ get_phrase('Available Variable Data') }}</h6>
|
<h6 class="card-title d-flex align-items-center">
|
||||||
<span class="badge bg-secondary rounded-1">{course_duration}</span>
|
<i class="fas fa-code me-2"></i>{{ get_phrase('Available Variables') }}
|
||||||
<span class="badge bg-secondary rounded-1">{instructor_name}</span>
|
</h6>
|
||||||
<span class="badge bg-secondary rounded-1">{student_name}</span>
|
<div class="d-flex flex-wrap">
|
||||||
<span class="badge bg-secondary rounded-1">{course_title}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_duration}')">{course_duration}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{number_of_lesson}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{certificate_id}')">{certificate_id}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{qr_code}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{instructor_name}')">{instructor_name}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{course_completion_date}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{student_name}')">{student_name}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{certificate_download_date}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_title}')">{course_title}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{course_level}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{number_of_lesson}')">{number_of_lesson}</span>
|
||||||
<span class="badge bg-secondary rounded-1">{course_language}</span>
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{qr_code}')">{qr_code}</span>
|
||||||
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_completion_date}')">{course_completion_date}</span>
|
||||||
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{certificate_download_date}')">{certificate_download_date}</span>
|
||||||
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_level}')">{course_level}</span>
|
||||||
|
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_language}')">{course_language}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card border-0 m-2" id="custom_elem_form">
|
|
||||||
<div class="card-body pt-2">
|
<div class="card border-0 shadow-sm" id="custom_elem_form">
|
||||||
<h6 class="card-title">{{ get_phrase('Add a new element') }}</h6>
|
<div class="card-body">
|
||||||
|
<h6 class="card-title d-flex align-items-center">
|
||||||
|
<i class="fas fa-plus-circle me-2"></i>{{ get_phrase('Add New Element') }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<div class="mb-3">
|
<div class="font-option-group">
|
||||||
<label for="certificate_element_content" class="form-label">{{ get_phrase('Enter Text with variable data') }}</label>
|
<div class="font-option-title">
|
||||||
<textarea name="certificate_element_content" placeholder="{{ get_phrase('Total Lesson') }}:{number_of_lesson}" id="certificate_element_content" rows="3" class="form-control"></textarea>
|
<i class="fas fa-font"></i>{{ get_phrase('Text Content') }}
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="certificate_element_content" class="form-label">{{ get_phrase('Enter Text with variables') }}</label>
|
||||||
|
<textarea name="certificate_element_content"
|
||||||
|
placeholder="{{ get_phrase('Example: This certifies that {student_name} has completed {course_title}') }}"
|
||||||
|
id="certificate_element_content"
|
||||||
|
rows="3"
|
||||||
|
class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="font_family_auto" class="form-label">{{ get_phrase('Choice a font-family') }}</label><br>
|
<div class="font-option-group">
|
||||||
<input type="radio" name="font_family" value="auto" id="font_family_auto" checked> <label for="font_family_auto">{{ get_phrase('Auto') }}</label><br>
|
<div class="font-option-title">
|
||||||
<input type="radio" name="font_family" value="Pinyon Script" id="font_family_pinyon_script"> <label for="font_family_pinyon_script">{{ get_phrase('Pinyon Script') }}</label><br>
|
<i class="fas fa-palette"></i>{{ get_phrase('Font Styling') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="font_family" class="form-label">{{ get_phrase('Font Family') }}</label>
|
||||||
|
<div id="font-family-options">
|
||||||
|
<!-- Font options will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{ get_phrase('Font Weight') }}</label>
|
||||||
|
<input type="range"
|
||||||
|
class="form-range font-weight-slider"
|
||||||
|
id="font_weight"
|
||||||
|
min="100"
|
||||||
|
max="900"
|
||||||
|
step="100"
|
||||||
|
value="400">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<small>Thin</small>
|
||||||
|
<small>Normal</small>
|
||||||
|
<small>Bold</small>
|
||||||
|
<small>Black</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{ get_phrase('Font Style') }}</label>
|
||||||
|
<div class="font-style-options">
|
||||||
|
<button type="button" class="font-style-btn" data-style="normal">
|
||||||
|
<i class="fas fa-font"></i> Normal
|
||||||
|
</button>
|
||||||
|
<button type="button" class="font-style-btn" data-style="italic">
|
||||||
|
<i class="fas fa-italic"></i> Italic
|
||||||
|
</button>
|
||||||
|
<button type="button" class="font-style-btn" data-style="oblique">
|
||||||
|
<i class="fas fa-slash"></i> Oblique
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{ get_phrase('Text Decoration') }}</label>
|
||||||
|
<div class="text-decoration-options">
|
||||||
|
<button type="button" class="text-decoration-btn" data-decoration="none">
|
||||||
|
None
|
||||||
|
</button>
|
||||||
|
<button type="button" class="text-decoration-btn" data-decoration="underline">
|
||||||
|
<u>Underline</u>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="text-decoration-btn" data-decoration="overline">
|
||||||
|
Overline
|
||||||
|
</button>
|
||||||
|
<button type="button" class="text-decoration-btn" data-decoration="line-through">
|
||||||
|
<s>Strike</s>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{ get_phrase('Text Color') }}</label>
|
||||||
|
<div class="color-picker-container">
|
||||||
|
<input type="color"
|
||||||
|
id="text_color"
|
||||||
|
value="#000000"
|
||||||
|
style="position: absolute; opacity: 0; width: 30px; height: 30px; cursor: pointer;">
|
||||||
|
<div class="color-preview" id="color_preview" style="background-color: #000000;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="font_size" class="form-label">{{ get_phrase('Font Size') }}: <span id="font_size_value">16</span>px</label>
|
||||||
|
<input type="range"
|
||||||
|
class="form-range"
|
||||||
|
id="font_size"
|
||||||
|
min="8"
|
||||||
|
max="72"
|
||||||
|
step="1"
|
||||||
|
value="16">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label for="font_size" class="form-label">{{ get_phrase('Font Size') }}</label><br>
|
<div class="font-option-group">
|
||||||
<input type="number" name="font_size" value="16" id="font_size" class="form-control" required>
|
<div class="font-option-title">
|
||||||
|
<i class="fas fa-text-height"></i>{{ get_phrase('Text Alignment') }}
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-outline-secondary flex-fill" onclick="setTextAlignment('left')">
|
||||||
|
<i class="fas fa-align-left"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary flex-fill" onclick="setTextAlignment('center')">
|
||||||
|
<i class="fas fa-align-center"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary flex-fill" onclick="setTextAlignment('right')">
|
||||||
|
<i class="fas fa-align-right"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary flex-fill" onclick="setTextAlignment('justify')">
|
||||||
|
<i class="fas fa-align-justify"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<button type="button" class="btn ol-btn-light-primary w-100" onclick="addElemToCertificate()">{{ get_phrase('Add') }}</button>
|
<div class="mb-4">
|
||||||
|
<button type="button" class="btn ol-btn-light-primary w-100" onclick="addElemToCertificate()">
|
||||||
|
<i class="fas fa-plus me-2"></i>{{ get_phrase('Add Element') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5">
|
|
||||||
<button type="button" class="btn ol-btn-primary w-100" onclick="saveTemplate()">{{ get_phrase('Save Template') }}</button>
|
<div class="mb-4">
|
||||||
|
<button type="button" class="btn ol-btn-primary w-100" onclick="saveTemplate()">
|
||||||
|
<i class="fas fa-save me-2"></i>{{ get_phrase('Save Template') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="certificate_builder_content" class="builder dotted-background">
|
<div id="certificate_builder_content" class="builder dotted-background">
|
||||||
|
|
||||||
{{-- Common style for page builder start --}}
|
{{-- Common style for page builder start --}}
|
||||||
<style>
|
<style>
|
||||||
|
/* Import Google Fonts for certificate styling */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Pinyon+Script&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Italianno&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Italianno&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Pinyon+Script&display=swap%27');
|
@import url('https://fonts.googleapis.com/css2?family=Miss+Fajardose&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Miss+Fajardose&display=swap%27');
|
@import url('https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;500;600;700&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@100;300;400;700;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@300;400;700;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700;800;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+Pro:wght@200;300;400;600;700;900&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;500;600;700&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Tangerine:wght@400;700&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Alex+Brush&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Sacramento&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Parisienne&display=swap');
|
||||||
</style>
|
</style>
|
||||||
{{-- Common style for page builder END --}}
|
{{-- Common style for page builder END --}}
|
||||||
|
|
||||||
@if (get_settings('certificate_builder_content'))
|
@if (get_settings('certificate_builder_content'))
|
||||||
@php
|
@php
|
||||||
$htmlContent = get_settings('certificate_builder_content');
|
$htmlContent = get_settings('certificate_builder_content');
|
||||||
|
|
||||||
// Use regex to update the src attribute of the <img> tag with the class 'certificate-template'.
|
|
||||||
$newSrc = get_image(get_settings('certificate_template'));
|
$newSrc = get_image(get_settings('certificate_template'));
|
||||||
|
|
||||||
$certificate_builder_content = preg_replace('/(<img[^>]*class=["\']certificate-template["\'][^>]*src=["\'])([^"\']*)(["\'])/i', '${1}' . $newSrc . '${3}', $htmlContent);
|
$certificate_builder_content = preg_replace('/(<img[^>]*class=["\']certificate-template["\'][^>]*src=["\'])([^"\']*)(["\'])/i', '${1}' . $newSrc . '${3}', $htmlContent);
|
||||||
@endphp
|
@endphp
|
||||||
{!! $certificate_builder_content !!}
|
{!! $certificate_builder_content !!}
|
||||||
@ -220,9 +556,140 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// Available fonts with categories
|
||||||
|
const fontOptions = [
|
||||||
|
{ name: 'Auto', value: 'auto', category: 'default' },
|
||||||
|
{ name: 'Inter', value: 'Inter, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Pinyon Script', value: 'Pinyon Script, cursive', category: 'elegant' },
|
||||||
|
{ name: 'Italianno', value: 'Italianno, cursive', category: 'script' },
|
||||||
|
{ name: 'Miss Fajardose', value: 'Miss Fajardose, cursive', category: 'script' },
|
||||||
|
{ name: 'Great Vibes', value: 'Great Vibes, cursive', category: 'script' },
|
||||||
|
{ name: 'Dancing Script', value: 'Dancing Script, cursive', category: 'script' },
|
||||||
|
{ name: 'Playfair Display', value: 'Playfair Display, serif', category: 'serif' },
|
||||||
|
{ name: 'Montserrat', value: 'Montserrat, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Roboto', value: 'Roboto, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Lato', value: 'Lato, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Open Sans', value: 'Open Sans, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Merriweather', value: 'Merriweather, serif', category: 'serif' },
|
||||||
|
{ name: 'Cinzel', value: 'Cinzel, serif', category: 'decorative' },
|
||||||
|
{ name: 'Raleway', value: 'Raleway, sans-serif', category: 'modern' },
|
||||||
|
{ name: 'Source Serif Pro', value: 'Source Serif Pro, serif', category: 'serif' },
|
||||||
|
{ name: 'Cormorant Garamond', value: 'Cormorant Garamond, serif', category: 'serif' },
|
||||||
|
{ name: 'Tangerine', value: 'Tangerine, cursive', category: 'script' },
|
||||||
|
{ name: 'Alex Brush', value: 'Alex Brush, cursive', category: 'script' },
|
||||||
|
{ name: 'Sacramento', value: 'Sacramento, cursive', category: 'script' },
|
||||||
|
{ name: 'Parisienne', value: 'Parisienne, cursive', category: 'script' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Current styling options
|
||||||
|
let currentFontStyle = 'normal';
|
||||||
|
let currentTextDecoration = 'none';
|
||||||
|
let currentTextAlign = 'left';
|
||||||
|
let currentTextColor = '#000000';
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
initialize();
|
||||||
|
populateFontOptions();
|
||||||
|
setupEventListeners();
|
||||||
|
});
|
||||||
|
|
||||||
|
function populateFontOptions() {
|
||||||
|
const container = $('#font-family-options');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
// Group fonts by category
|
||||||
|
const fontsByCategory = {};
|
||||||
|
fontOptions.forEach(font => {
|
||||||
|
if (!fontsByCategory[font.category]) {
|
||||||
|
fontsByCategory[font.category] = [];
|
||||||
|
}
|
||||||
|
fontsByCategory[font.category].push(font);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create options for each category
|
||||||
|
Object.keys(fontsByCategory).forEach(category => {
|
||||||
|
const categoryTitle = category.charAt(0).toUpperCase() + category.slice(1);
|
||||||
|
const categoryDiv = $(`<div class="mb-3"><small class="text-muted">${categoryTitle} Fonts</small></div>`);
|
||||||
|
|
||||||
|
fontsByCategory[category].forEach(font => {
|
||||||
|
const isActive = font.value === 'auto' ? 'active' : '';
|
||||||
|
const option = $(`
|
||||||
|
<div class="font-family-option ${isActive}" data-value="${font.value}">
|
||||||
|
<input type="radio" name="font_family" value="${font.value}"
|
||||||
|
id="font_family_${font.value.replace(/\s+/g, '_')}"
|
||||||
|
${font.value === 'auto' ? 'checked' : ''}>
|
||||||
|
<label for="font_family_${font.value.replace(/\s+/g, '_')}" class="ms-2 flex-grow-1">${font.name}</label>
|
||||||
|
<span class="font-family-preview" style="font-family: ${font.value}">Aa</span>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
categoryDiv.append(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.append(categoryDiv);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add click event to font options
|
||||||
|
$('.font-family-option').click(function() {
|
||||||
|
$('.font-family-option').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
$(this).find('input[type="radio"]').prop('checked', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupEventListeners() {
|
||||||
|
// Font size display
|
||||||
|
$('#font_size').on('input', function() {
|
||||||
|
$('#font_size_value').text($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Font style buttons
|
||||||
|
$('.font-style-btn').click(function() {
|
||||||
|
$('.font-style-btn').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
currentFontStyle = $(this).data('style');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Text decoration buttons
|
||||||
|
$('.text-decoration-btn').click(function() {
|
||||||
|
$('.text-decoration-btn').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
currentTextDecoration = $(this).data('decoration');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Color picker
|
||||||
|
$('#text_color').on('input', function() {
|
||||||
|
$('#color_preview').css('background-color', $(this).val());
|
||||||
|
currentTextColor = $(this).val();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize first button as active
|
||||||
|
$('.font-style-btn[data-style="normal"]').addClass('active');
|
||||||
|
$('.text-decoration-btn[data-decoration="none"]').addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addVariableToText(variable) {
|
||||||
|
const textarea = $('#certificate_element_content');
|
||||||
|
const currentValue = textarea.val();
|
||||||
|
const cursorPos = textarea[0].selectionStart;
|
||||||
|
|
||||||
|
const newValue = currentValue.substring(0, cursorPos) +
|
||||||
|
variable +
|
||||||
|
currentValue.substring(cursorPos);
|
||||||
|
|
||||||
|
textarea.val(newValue);
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTextAlignment(alignment) {
|
||||||
|
currentTextAlign = alignment;
|
||||||
|
$('.btn-outline-secondary').removeClass('active');
|
||||||
|
$(`button[onclick="setTextAlignment('${alignment}')"]`).addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
function saveTemplate() {
|
function saveTemplate() {
|
||||||
var certificate_builder_content = $('#certificate_builder_content').html();
|
var certificate_builder_content = $('#certificate_builder_content').html();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -237,50 +704,93 @@
|
|||||||
success: function(response) {
|
success: function(response) {
|
||||||
$(location).attr('href', response);
|
$(location).attr('href', response);
|
||||||
console.log(response)
|
console.log(response)
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
console.error('Error saving template:', xhr);
|
||||||
|
alert('Error saving template. Please try again.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addElemToCertificate() {
|
function addElemToCertificate() {
|
||||||
var font_family = $("input[type='radio'][name='font_family']:checked").val();
|
var font_family = $("input[type='radio'][name='font_family']:checked").val();
|
||||||
console.log(font_family);
|
|
||||||
var font_size = $("#font_size").val();
|
var font_size = $("#font_size").val();
|
||||||
|
var font_weight = $("#font_weight").val();
|
||||||
var certificate_element_content = $('#certificate_element_content').val();
|
var certificate_element_content = $('#certificate_element_content').val();
|
||||||
var certificateElem = `<div class="draggable resizeable-canvas" style="padding: 5px !important; position: absolute; font-size: ${font_size}px; top: 10px; left: 10px; width: min-content; font-family: '${font_family}';">
|
|
||||||
${certificate_element_content}
|
// Generate unique ID for the element
|
||||||
<i class="remove-item fi-rr-cross-circle cursor-pointer" onclick="$(this).parent().remove()">
|
var elementId = 'elem_' + Date.now() + Math.floor(Math.random() * 1000);
|
||||||
</div>`;
|
|
||||||
|
var certificateElem = `
|
||||||
|
<div id="${elementId}"
|
||||||
|
class="draggable resizeable-canvas"
|
||||||
|
style="
|
||||||
|
padding: 10px !important;
|
||||||
|
position: absolute;
|
||||||
|
font-size: ${font_size}px;
|
||||||
|
font-weight: ${font_weight};
|
||||||
|
font-style: ${currentFontStyle};
|
||||||
|
text-decoration: ${currentTextDecoration};
|
||||||
|
text-align: ${currentTextAlign};
|
||||||
|
color: ${currentTextColor};
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
min-width: 100px;
|
||||||
|
min-height: 40px;
|
||||||
|
font-family: ${font_family};
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 5;
|
||||||
|
">
|
||||||
|
${certificate_element_content}
|
||||||
|
<div class="remove-item" onclick="$(this).parent().remove(); positionTracking(this.parentNode)">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
if (certificate_element_content != '') {
|
if (certificate_element_content.trim() !== '') {
|
||||||
$('#certificate-layout-module').append(certificateElem);
|
$('#certificate-layout-module').append(certificateElem);
|
||||||
|
|
||||||
|
// Reset form
|
||||||
$('#certificate_element_content').val('');
|
$('#certificate_element_content').val('');
|
||||||
$("#font_size").val(16);
|
$("#font_size").val(16);
|
||||||
$("#font_family_auto").attr('checked', 'checked');
|
$("#font_size_value").text('16');
|
||||||
|
$("#font_weight").val(400);
|
||||||
|
currentTextColor = '#000000';
|
||||||
|
$('#color_preview').css('background-color', '#000000');
|
||||||
|
$('#text_color').val('#000000');
|
||||||
|
|
||||||
|
// Reset to default font
|
||||||
|
$('.font-family-option').removeClass('active');
|
||||||
|
$('.font-family-option[data-value="auto"]').addClass('active').find('input').prop('checked', true);
|
||||||
|
|
||||||
initialize();
|
initialize();
|
||||||
|
} else {
|
||||||
|
alert('Please enter some text content.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
$(".draggable").draggable();
|
$(".draggable").draggable({
|
||||||
|
containment: "#certificate-layout-module",
|
||||||
|
cursor: "move",
|
||||||
|
scroll: false
|
||||||
|
});
|
||||||
|
|
||||||
$(".resizeable-canvas").resizable({
|
$(".resizeable-canvas").resizable({
|
||||||
resize: function(event, ui) {
|
resize: function(event, ui) {
|
||||||
// When resizing starts, temporarily disable dragging
|
|
||||||
$(".draggable").draggable("disable");
|
$(".draggable").draggable("disable");
|
||||||
positionTracking(this);
|
positionTracking(this);
|
||||||
},
|
},
|
||||||
stop: function(event, ui) {
|
stop: function(event, ui) {
|
||||||
$(".draggable").draggable("enable");
|
$(".draggable").draggable("enable");
|
||||||
positionTracking(this);
|
positionTracking(this);
|
||||||
}
|
},
|
||||||
|
handles: "all",
|
||||||
|
minHeight: 30,
|
||||||
|
minWidth: 50
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".draggable").on("dragstop", function(e, pos) {
|
||||||
$(".draggable").on("dragend", function(e, pos) {}).on("dragstart", function(e, pos) {}).on("dragstop", function(e, pos) {
|
|
||||||
positionTracking(this);
|
positionTracking(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -302,15 +812,20 @@
|
|||||||
layoutCanvasBottom > itemBottom
|
layoutCanvasBottom > itemBottom
|
||||||
) {
|
) {
|
||||||
$(e).removeClass('hidden-position');
|
$(e).removeClass('hidden-position');
|
||||||
|
$(e).css('border-color', 'rgba(21, 181, 126, 0.5)');
|
||||||
} else {
|
} else {
|
||||||
$(e).addClass('hidden-position');
|
$(e).addClass('hidden-position');
|
||||||
|
$(e).css('border-color', '#ff6b6b');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var draggableItems = document.getElementsByClassName("draggable");
|
var draggableItems = document.getElementsByClassName("draggable");
|
||||||
for (var i = 0; i < draggableItems.length; i++) {
|
for (var i = 0; i < draggableItems.length; i++) {
|
||||||
var itemOffset = $(draggableItems.item(i)).offset();
|
var item = $(draggableItems.item(i));
|
||||||
var itemRight = $(draggableItems.item(i)).width() + itemOffset.left;
|
if (item.attr('id') == 'certificate-layout-module') continue;
|
||||||
var itemBottom = $(draggableItems.item(i)).height() + itemOffset.top;
|
|
||||||
|
var itemOffset = item.offset();
|
||||||
|
var itemRight = item.width() + itemOffset.left;
|
||||||
|
var itemBottom = item.height() + itemOffset.top;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
layoutCanvasOffset.left < itemOffset.left &&
|
layoutCanvasOffset.left < itemOffset.left &&
|
||||||
@ -318,9 +833,11 @@
|
|||||||
layoutCanvasRight > itemRight &&
|
layoutCanvasRight > itemRight &&
|
||||||
layoutCanvasBottom > itemBottom
|
layoutCanvasBottom > itemBottom
|
||||||
) {
|
) {
|
||||||
$(draggableItems.item(i)).removeClass('hidden-position');
|
item.removeClass('hidden-position');
|
||||||
|
item.css('border-color', 'rgba(21, 181, 126, 0.5)');
|
||||||
} else {
|
} else {
|
||||||
$(draggableItems.item(i)).addClass('hidden-position');
|
item.addClass('hidden-position');
|
||||||
|
item.css('border-color', '#ff6b6b');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,5 +845,4 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
</html>
|
||||||
</html>
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<link rel="shortcut icon" href="{{ asset(get_frontend_settings('favicon')) }}" />
|
<link rel="shortcut icon" href="{{ asset(get_frontend_settings('favicon')) }}" />
|
||||||
<script src="{{ asset('assets/frontend/default/js/jquery-3.7.1.min.js') }}"></script>
|
<script src="{{ asset('assets/frontend/default/js/jquery-3.7.1.min.js') }}"></script>
|
||||||
<script src="{{ asset('assets/global/html2canvas/html2canvas.min.js') }}"></script>
|
<script src="{{ asset('assets/global/html2canvas/html2canvas.min.js') }}"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -26,31 +26,83 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.certificate-layout-module {
|
.certificate-layout-module {
|
||||||
left: unset !important;
|
left: unset !important;
|
||||||
top: unset !important;
|
top: unset !important;
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
margin-right: auto !important;
|
margin-right: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn {
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #ffffff;
|
||||||
|
background: linear-gradient(135deg, #3d9bff 0%, #0066cc 100%);
|
||||||
|
border: none;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(61, 155, 255, 0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn.secondary {
|
||||||
|
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
|
||||||
|
box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn.secondary:hover {
|
||||||
|
background: linear-gradient(135deg, #5a6268 0%, #3d4349 100%);
|
||||||
|
box-shadow: 0 6px 16px rgba(108, 117, 125, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:hover {
|
||||||
|
background: linear-gradient(135deg, #2d8bef 0%, #0055b3 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(61, 155, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2px 8px rgba(61, 155, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:focus {
|
||||||
|
outline: 2px solid rgba(61, 155, 255, 0.5);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive untuk mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
.download-btn {
|
.download-btn {
|
||||||
position: fixed;
|
bottom: 16px;
|
||||||
bottom: 10px;
|
right: 16px;
|
||||||
right: 10px;
|
padding: 10px 20px;
|
||||||
padding: 20px 32px;
|
font-size: 14px;
|
||||||
border-radius: 5px;
|
|
||||||
color: #3d9bff;
|
|
||||||
background-color: #d3e8ff;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.absolute-view{
|
.absolute-view{
|
||||||
background-color: #fff;
|
background-color: #e6e6e6;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -75,12 +127,37 @@
|
|||||||
{{-- Downloadable canvas --}}
|
{{-- Downloadable canvas --}}
|
||||||
<div class="captureCertificate" id="captureCertificate">
|
<div class="captureCertificate" id="captureCertificate">
|
||||||
@php
|
@php
|
||||||
|
$bulan_indonesia = [
|
||||||
|
1 => 'Januari',
|
||||||
|
2 => 'Februari',
|
||||||
|
3 => 'Maret',
|
||||||
|
4 => 'April',
|
||||||
|
5 => 'Mei',
|
||||||
|
6 => 'Juni',
|
||||||
|
7 => 'Juli',
|
||||||
|
8 => 'Agustus',
|
||||||
|
9 => 'September',
|
||||||
|
10 => 'Oktober',
|
||||||
|
11 => 'November',
|
||||||
|
12 => 'Desember'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Jika date_formatter() mengembalikan tanggal string
|
||||||
|
$completion_date = $certificate->created_at; // Ambil timestamp asli
|
||||||
|
|
||||||
|
// Konversi ke format Indonesia
|
||||||
|
$timestamp = strtotime($completion_date); // atau $completion_date jika sudah timestamp
|
||||||
|
$hari = date('j', $timestamp); // tanggal tanpa nol
|
||||||
|
$bulan_angka = (int)date('n', $timestamp); // bulan angka
|
||||||
|
$tahun = date('Y', $timestamp);
|
||||||
|
|
||||||
|
$course_completion_date = $hari . ' ' . $bulan_indonesia[$bulan_angka] . ' ' . $tahun;
|
||||||
$course_duration = $certificate->course->total_duration();
|
$course_duration = $certificate->course->total_duration();
|
||||||
$student_name = $certificate->user->name;
|
$student_name = $certificate->user->name;
|
||||||
$course_title = $certificate->course->title;
|
$course_title = $certificate->course->title;
|
||||||
$number_of_lesson = $certificate->course->lessons->count();
|
$number_of_lesson = $certificate->course->lessons->count();
|
||||||
$qr_code = $qrcode;
|
$qr_code = $qrcode;
|
||||||
$course_completion_date = date_formatter($certificate->created_at);
|
$certificate_id = $certificate->identifier;
|
||||||
$certificate_download_date = date('d M Y');
|
$certificate_download_date = date('d M Y');
|
||||||
$course_level = ucfirst($certificate->course->level);
|
$course_level = ucfirst($certificate->course->level);
|
||||||
$course_language = ucfirst($certificate->course->language);
|
$course_language = ucfirst($certificate->course->language);
|
||||||
@ -98,6 +175,7 @@
|
|||||||
$certificate_builder_content = str_replace('{number_of_lesson}', $number_of_lesson, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{number_of_lesson}', $number_of_lesson, $certificate_builder_content);
|
||||||
$certificate_builder_content = str_replace('{qr_code}', $qr_code, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{qr_code}', $qr_code, $certificate_builder_content);
|
||||||
$certificate_builder_content = str_replace('{course_completion_date}', $course_completion_date, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{course_completion_date}', $course_completion_date, $certificate_builder_content);
|
||||||
|
$certificate_builder_content = str_replace('{certificate_id}', $certificate_id, $certificate_builder_content);
|
||||||
$certificate_builder_content = str_replace('{certificate_download_date}', $certificate_download_date, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{certificate_download_date}', $certificate_download_date, $certificate_builder_content);
|
||||||
$certificate_builder_content = str_replace('{course_level}', $course_level, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{course_level}', $course_level, $certificate_builder_content);
|
||||||
$certificate_builder_content = str_replace('{course_language}', $course_language, $certificate_builder_content);
|
$certificate_builder_content = str_replace('{course_language}', $course_language, $certificate_builder_content);
|
||||||
@ -121,7 +199,19 @@
|
|||||||
{{-- Preview certificate end--}}
|
{{-- Preview certificate end--}}
|
||||||
|
|
||||||
|
|
||||||
<a class="download-btn" href="#" onclick="setTimeout(() => renderCanvasToImage(), 500);">{{ get_phrase('Download') }}</a>
|
<div class="download-wrapper">
|
||||||
|
<a class="download-btn" href="#"
|
||||||
|
onclick="setTimeout(() => renderCanvasToImage(), 500); return false;">
|
||||||
|
{{ get_phrase('Download Image') }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="download-btn secondary" href="#"
|
||||||
|
onclick="renderCanvasToPDF(); return false;">
|
||||||
|
{{ get_phrase('Download PDF') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
"use strict";
|
"use strict";
|
||||||
@ -176,6 +266,57 @@
|
|||||||
// Remove the link from the body
|
// Remove the link from the body
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
var certificate_builder_view_width = $('.certificate_builder_view').width();
|
||||||
|
var certificate_layout_module = $('.certificate_builder_view .certificate-layout-module').width();
|
||||||
|
var zoomScaleValue = ((certificate_builder_view_width / certificate_layout_module) * 100) - 8;
|
||||||
|
$('.certificate_builder_view .certificate-layout-module').css('zoom', zoomScaleValue + '%');
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderCanvasToPDF() {
|
||||||
|
const target = document.querySelector("#captureCertificate > div");
|
||||||
|
|
||||||
|
html2canvas(target, {
|
||||||
|
allowTaint: true,
|
||||||
|
useCORS: true,
|
||||||
|
scale: 2
|
||||||
|
}).then(canvas => {
|
||||||
|
|
||||||
|
const imgData = canvas.toDataURL("image/png");
|
||||||
|
|
||||||
|
// A4 Landscape
|
||||||
|
const pdf = new window.jspdf.jsPDF({
|
||||||
|
orientation: 'landscape',
|
||||||
|
unit: 'mm',
|
||||||
|
format: 'a4'
|
||||||
|
});
|
||||||
|
|
||||||
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||||
|
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||||
|
|
||||||
|
// Hitung rasio agar tidak terpotong
|
||||||
|
const imgWidth = pageWidth;
|
||||||
|
const imgHeight = canvas.height * imgWidth / canvas.width;
|
||||||
|
|
||||||
|
const positionY = (pageHeight - imgHeight) / 2;
|
||||||
|
|
||||||
|
pdf.addImage(
|
||||||
|
imgData,
|
||||||
|
'PNG',
|
||||||
|
0,
|
||||||
|
positionY,
|
||||||
|
imgWidth,
|
||||||
|
imgHeight,
|
||||||
|
undefined,
|
||||||
|
'FAST'
|
||||||
|
);
|
||||||
|
|
||||||
|
pdf.save("certificate.pdf");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user