penambahan sertifikat
This commit is contained in:
parent
51a5631b74
commit
40f7a8e3c2
@ -137,7 +137,7 @@ class PlayerController extends Controller
|
||||
if ($certificate->count() == 0) {
|
||||
$certificate_data['user_id'] = auth()->user()->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::insert($certificate_data);
|
||||
}
|
||||
@ -146,6 +146,22 @@ class PlayerController extends Controller
|
||||
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()
|
||||
{
|
||||
return view('course_player.watermark');
|
||||
|
||||
@ -60,6 +60,8 @@ class HomeController extends Controller
|
||||
|
||||
$page_data['certificate'] = $certificate->first();
|
||||
$page_data['qrcode'] = $qrcode;
|
||||
|
||||
// dd($qr_code_content_value);
|
||||
return view('curriculum.certificate.download', $page_data);
|
||||
} else {
|
||||
return redirect(route('home'))->with('error', get_phrase('Certificate not found at this url'));
|
||||
|
||||
@ -208,7 +208,7 @@ class QuizController extends Controller
|
||||
Certificate::create([
|
||||
'user_id' => $student_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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$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') }}">
|
||||
|
||||
{{-- 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/global/jquery-ui-1.13.2/jquery-ui.min.js') }}"></script>
|
||||
<script src="{{ asset('assets/backend/vendors/bootstrap/bootstrap.bundle.min.js') }}"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-family: 'Inter', 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.draggable {
|
||||
border: 2px dashed rgb(255, 255, 255);
|
||||
border: 2px dashed rgba(255, 255, 255, 0.8);
|
||||
cursor: move;
|
||||
background-color: #15b57e33;
|
||||
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) {
|
||||
@ -48,17 +59,20 @@
|
||||
|
||||
.certificate-layout-module {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: -300px;
|
||||
right: -350px;
|
||||
bottom: 0;
|
||||
z-index: 200;
|
||||
background-color: #ffffff;
|
||||
width: 300px;
|
||||
width: 350px;
|
||||
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 {
|
||||
@ -67,26 +81,49 @@
|
||||
|
||||
.sidebar-header {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
background: rgba(0, 17, 81, 1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
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 {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: -17px;
|
||||
background-color: white;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
padding: 2px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
font-size: 16px;
|
||||
padding: 4px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
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) {
|
||||
@ -96,23 +133,18 @@
|
||||
}
|
||||
|
||||
.dotted-background {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-image: radial-gradient(circle, #afafaf 1px, transparent 1px);
|
||||
background-size: 10px 10px;
|
||||
/* Adjust size of dots as needed */
|
||||
|
||||
background-size: 20px 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
background-color: #e4e5ff;
|
||||
background-color: #f0f2f5;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
@ -120,97 +152,401 @@
|
||||
}
|
||||
|
||||
.sidebar-body {
|
||||
height: 100%;
|
||||
height: calc(100% - 60px);
|
||||
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>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<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">
|
||||
<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>
|
||||
<i class="fas fa-sliders-h" style="color: white; font-size: 20px;"></i>
|
||||
</a>
|
||||
|
||||
<div class="sidebar open">
|
||||
<div class="sidebar-header border-bottom d-flex align-items-center">
|
||||
<a class="btn" href="#" onclick="$('.sidebar').removeClass('open')">
|
||||
<i class="fi-rr-cross-small"></i>
|
||||
<a class="btn text-white" href="#" onclick="$('.sidebar').removeClass('open')">
|
||||
<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>
|
||||
{{ get_phrase('Certificate elements') }}
|
||||
<a class="ms-auto" href="{{ route('admin.certificate.settings') }}">{{ get_phrase('Back') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-body">
|
||||
<div class="card border-0 m-2">
|
||||
<div class="card-body pt-0">
|
||||
<h6 class="card-title mt-3">{{ get_phrase('Available Variable Data') }}</h6>
|
||||
<span class="badge bg-secondary rounded-1">{course_duration}</span>
|
||||
<span class="badge bg-secondary rounded-1">{instructor_name}</span>
|
||||
<span class="badge bg-secondary rounded-1">{student_name}</span>
|
||||
<span class="badge bg-secondary rounded-1">{course_title}</span>
|
||||
<span class="badge bg-secondary rounded-1">{number_of_lesson}</span>
|
||||
<span class="badge bg-secondary rounded-1">{qr_code}</span>
|
||||
<span class="badge bg-secondary rounded-1">{course_completion_date}</span>
|
||||
<span class="badge bg-secondary rounded-1">{certificate_download_date}</span>
|
||||
<span class="badge bg-secondary rounded-1">{course_level}</span>
|
||||
<span class="badge bg-secondary rounded-1">{course_language}</span>
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title d-flex align-items-center">
|
||||
<i class="fas fa-code me-2"></i>{{ get_phrase('Available Variables') }}
|
||||
</h6>
|
||||
<div class="d-flex flex-wrap">
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_duration}')">{course_duration}</span>
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{certificate_id}')">{certificate_id}</span>
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{instructor_name}')">{instructor_name}</span>
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{student_name}')">{student_name}</span>
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{course_title}')">{course_title}</span>
|
||||
<span class="badge bg-primary cursor-pointer" onclick="addVariableToText('{number_of_lesson}')">{number_of_lesson}</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 class="card border-0 m-2" id="custom_elem_form">
|
||||
<div class="card-body pt-2">
|
||||
<h6 class="card-title">{{ get_phrase('Add a new element') }}</h6>
|
||||
|
||||
<div class="card border-0 shadow-sm" id="custom_elem_form">
|
||||
<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="#">
|
||||
<div class="mb-3">
|
||||
<label for="certificate_element_content" class="form-label">{{ get_phrase('Enter Text with variable data') }}</label>
|
||||
<textarea name="certificate_element_content" placeholder="{{ get_phrase('Total Lesson') }}:{number_of_lesson}" id="certificate_element_content" rows="3" class="form-control"></textarea>
|
||||
<div class="font-option-group">
|
||||
<div class="font-option-title">
|
||||
<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 class="mb-3">
|
||||
<label for="font_family_auto" class="form-label">{{ get_phrase('Choice a font-family') }}</label><br>
|
||||
<input type="radio" name="font_family" value="auto" id="font_family_auto" checked> <label for="font_family_auto">{{ get_phrase('Auto') }}</label><br>
|
||||
<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>
|
||||
|
||||
<div class="font-option-group">
|
||||
<div class="font-option-title">
|
||||
<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 class="mb-3">
|
||||
<label for="font_size" class="form-label">{{ get_phrase('Font Size') }}</label><br>
|
||||
<input type="number" name="font_size" value="16" id="font_size" class="form-control" required>
|
||||
|
||||
<div class="font-option-group">
|
||||
<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 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 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>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="certificate_builder_content" class="builder dotted-background">
|
||||
|
||||
{{-- Common style for page builder start --}}
|
||||
<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=Pinyon+Script&display=swap%27');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Miss+Fajardose&display=swap%27');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Miss+Fajardose&display=swap');
|
||||
@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>
|
||||
{{-- Common style for page builder END --}}
|
||||
|
||||
@if (get_settings('certificate_builder_content'))
|
||||
@php
|
||||
$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'));
|
||||
|
||||
$certificate_builder_content = preg_replace('/(<img[^>]*class=["\']certificate-template["\'][^>]*src=["\'])([^"\']*)(["\'])/i', '${1}' . $newSrc . '${3}', $htmlContent);
|
||||
@endphp
|
||||
{!! $certificate_builder_content !!}
|
||||
@ -220,9 +556,140 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script>
|
||||
"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() {
|
||||
var certificate_builder_content = $('#certificate_builder_content').html();
|
||||
$.ajax({
|
||||
@ -237,50 +704,93 @@
|
||||
success: function(response) {
|
||||
$(location).attr('href', response);
|
||||
console.log(response)
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error('Error saving template:', xhr);
|
||||
alert('Error saving template. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addElemToCertificate() {
|
||||
var font_family = $("input[type='radio'][name='font_family']:checked").val();
|
||||
console.log(font_family);
|
||||
var font_size = $("#font_size").val();
|
||||
var font_weight = $("#font_weight").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}
|
||||
<i class="remove-item fi-rr-cross-circle cursor-pointer" onclick="$(this).parent().remove()">
|
||||
</div>`;
|
||||
|
||||
// Generate unique ID for the element
|
||||
var elementId = 'elem_' + Date.now() + Math.floor(Math.random() * 1000);
|
||||
|
||||
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);
|
||||
|
||||
// Reset form
|
||||
$('#certificate_element_content').val('');
|
||||
$("#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();
|
||||
} else {
|
||||
alert('Please enter some text content.');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
initialize();
|
||||
});
|
||||
|
||||
function initialize() {
|
||||
$(".draggable").draggable();
|
||||
$(".draggable").draggable({
|
||||
containment: "#certificate-layout-module",
|
||||
cursor: "move",
|
||||
scroll: false
|
||||
});
|
||||
|
||||
$(".resizeable-canvas").resizable({
|
||||
resize: function(event, ui) {
|
||||
// When resizing starts, temporarily disable dragging
|
||||
$(".draggable").draggable("disable");
|
||||
positionTracking(this);
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
$(".draggable").draggable("enable");
|
||||
positionTracking(this);
|
||||
}
|
||||
},
|
||||
handles: "all",
|
||||
minHeight: 30,
|
||||
minWidth: 50
|
||||
});
|
||||
|
||||
|
||||
$(".draggable").on("dragend", function(e, pos) {}).on("dragstart", function(e, pos) {}).on("dragstop", function(e, pos) {
|
||||
$(".draggable").on("dragstop", function(e, pos) {
|
||||
positionTracking(this);
|
||||
});
|
||||
}
|
||||
@ -302,15 +812,20 @@
|
||||
layoutCanvasBottom > itemBottom
|
||||
) {
|
||||
$(e).removeClass('hidden-position');
|
||||
$(e).css('border-color', 'rgba(21, 181, 126, 0.5)');
|
||||
} else {
|
||||
$(e).addClass('hidden-position');
|
||||
$(e).css('border-color', '#ff6b6b');
|
||||
}
|
||||
} else {
|
||||
var draggableItems = document.getElementsByClassName("draggable");
|
||||
for (var i = 0; i < draggableItems.length; i++) {
|
||||
var itemOffset = $(draggableItems.item(i)).offset();
|
||||
var itemRight = $(draggableItems.item(i)).width() + itemOffset.left;
|
||||
var itemBottom = $(draggableItems.item(i)).height() + itemOffset.top;
|
||||
var item = $(draggableItems.item(i));
|
||||
if (item.attr('id') == 'certificate-layout-module') continue;
|
||||
|
||||
var itemOffset = item.offset();
|
||||
var itemRight = item.width() + itemOffset.left;
|
||||
var itemBottom = item.height() + itemOffset.top;
|
||||
|
||||
if (
|
||||
layoutCanvasOffset.left < itemOffset.left &&
|
||||
@ -318,9 +833,11 @@
|
||||
layoutCanvasRight > itemRight &&
|
||||
layoutCanvasBottom > itemBottom
|
||||
) {
|
||||
$(draggableItems.item(i)).removeClass('hidden-position');
|
||||
item.removeClass('hidden-position');
|
||||
item.css('border-color', 'rgba(21, 181, 126, 0.5)');
|
||||
} else {
|
||||
$(draggableItems.item(i)).addClass('hidden-position');
|
||||
item.addClass('hidden-position');
|
||||
item.css('border-color', '#ff6b6b');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -328,5 +845,4 @@
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@ -9,7 +9,7 @@
|
||||
<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/global/html2canvas/html2canvas.min.js') }}"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -26,31 +26,83 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.certificate-layout-module {
|
||||
left: unset !important;
|
||||
top: unset !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
.certificate-layout-module {
|
||||
left: unset !important;
|
||||
top: unset !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
svg {
|
||||
width: 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 {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
padding: 20px 32px;
|
||||
border-radius: 5px;
|
||||
color: #3d9bff;
|
||||
background-color: #d3e8ff;
|
||||
z-index: 100;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.absolute-view{
|
||||
background-color: #fff;
|
||||
background-color: #e6e6e6;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
@ -75,12 +127,37 @@
|
||||
{{-- Downloadable canvas --}}
|
||||
<div class="captureCertificate" id="captureCertificate">
|
||||
@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();
|
||||
$student_name = $certificate->user->name;
|
||||
$course_title = $certificate->course->title;
|
||||
$number_of_lesson = $certificate->course->lessons->count();
|
||||
$qr_code = $qrcode;
|
||||
$course_completion_date = date_formatter($certificate->created_at);
|
||||
$certificate_id = $certificate->identifier;
|
||||
$certificate_download_date = date('d M Y');
|
||||
$course_level = ucfirst($certificate->course->level);
|
||||
$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('{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('{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('{course_level}', $course_level, $certificate_builder_content);
|
||||
$certificate_builder_content = str_replace('{course_language}', $course_language, $certificate_builder_content);
|
||||
@ -121,7 +199,19 @@
|
||||
{{-- 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>
|
||||
"use strict";
|
||||
@ -176,6 +266,57 @@
|
||||
// Remove the link from the body
|
||||
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>
|
||||
</body>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user