563 lines
26 KiB
PHP
563 lines
26 KiB
PHP
@extends('layouts.backendTemplate')
|
|
|
|
@section('content')
|
|
|
|
<style>
|
|
.cdx-input.image-tool__caption {
|
|
display: none;
|
|
}
|
|
|
|
.block-wrapper {
|
|
margin-top: 10px;
|
|
border: 1px solid #ddd;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.invalid {
|
|
border-color: red;
|
|
}
|
|
|
|
/* Responsive Video Container */
|
|
.video-wrapper {
|
|
position: relative;
|
|
padding-bottom: 56.25%;
|
|
/* 16:9 aspect ratio */
|
|
height: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.video-wrapper iframe {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
}
|
|
</style>
|
|
|
|
<!--begin::Main-->
|
|
<div class="app-main flex-column flex-row-fluid" id="kt_app_main">
|
|
|
|
<div class="d-flex flex-column flex-column-fluid">
|
|
|
|
|
|
@include('uc.admin.breadcrumb', [
|
|
'title' => 'WHERE ELSE : Add Article',
|
|
'pageName' => 'Add',
|
|
'pageParent' => 'Article Management',
|
|
'pageParentLink' => url('where-else/article'),
|
|
])
|
|
|
|
<div id="kt_app_content" class="app-content flex-column-fluid">
|
|
<div id="kt_app_content_container" class="app-container container-xxl">
|
|
|
|
|
|
|
|
<form id="frmAdd" class="form d-flex flex-column flex-lg-row"
|
|
action="{{ url('where-else/insert-article') }}" method="post" enctype="multipart/form-data">
|
|
|
|
<input type="hidden" name="_method" value="POST">
|
|
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
|
|
|
|
|
<div class="d-flex flex-column gap-7 gap-lg-10 w-100 w-lg-300px mb-7 me-lg-10">
|
|
|
|
<div class="card card-flush py-4">
|
|
|
|
|
|
<div class="card-body text-center pt-0">
|
|
|
|
<style>
|
|
.image-input-placeholder {
|
|
background-image: url('{{ url('assets/media/svg/files/blank-image.svg') }}');
|
|
}
|
|
|
|
[data-bs-theme="dark"] .image-input-placeholder {
|
|
background-image: url('../media/svg/files/blank-image-dark.svg');
|
|
}
|
|
</style>
|
|
|
|
<div class="image-input image-input-empty image-input-outline image-input-placeholder mb-3"
|
|
data-kt-image-input="true">
|
|
|
|
<div class="image-input-wrapper w-150px h-150px"></div>
|
|
|
|
<label
|
|
class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
|
|
data-kt-image-input-action="change" data-bs-toggle="tooltip"
|
|
title="Change avatar">
|
|
|
|
<i class="bi bi-pencil-fill fs-7"></i>
|
|
|
|
<input type="file" name="avatar" accept=".png, .jpg, .jpeg" />
|
|
<input type="hidden" name="avatar_remove" />
|
|
|
|
</label>
|
|
|
|
<span
|
|
class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
|
|
data-kt-image-input-action="cancel" data-bs-toggle="tooltip"
|
|
title="Cancel avatar">
|
|
<i class="bi bi-x fs-2"></i>
|
|
</span>
|
|
|
|
<span
|
|
class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
|
|
data-kt-image-input-action="remove" data-bs-toggle="tooltip"
|
|
title="Remove avatar">
|
|
<i class="bi bi-x fs-2"></i>
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div class="text-muted fs-7">Set the profile picture. Only *.png, *.jpg and *.jpeg
|
|
image
|
|
files are accepted</div>
|
|
|
|
</div>
|
|
|
|
{{-- Active --}}
|
|
<div class="card-header" style="border: unset;">
|
|
<div class="required card-title">
|
|
<h2>Active</h2>
|
|
</div>
|
|
</div>
|
|
<div class="card-body pt-0">
|
|
|
|
<label class="form-check form-switch form-check-custom form-check-solid">
|
|
<input class="form-check-input" disabled readonly name="active" type="checkbox"
|
|
value="1" checked />
|
|
</label>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="d-flex flex-column flex-row-fluid gap-7 gap-lg-10">
|
|
|
|
<div class="card card-flush py-4">
|
|
|
|
|
|
@if (Session::has('messageSuccess'))
|
|
<div class="card-body">
|
|
@include('uc/messageSuccess')
|
|
</div>
|
|
@endif
|
|
|
|
@if (Session::has('messageFail'))
|
|
<div class="card-body">
|
|
@include('uc/messageFail')
|
|
</div>
|
|
@endif
|
|
|
|
<div class="card-header">
|
|
<div class="card-title">
|
|
<h2>Overview</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body pt-0">
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Sub Categories</label>
|
|
<div class="d-flex align-items-center mt-3">
|
|
@foreach ($subCategoriesView as $item)
|
|
<label
|
|
class="form-check form-check-custom form-check-inline form-check-solid me-5 is-invalid">
|
|
<input class="form-check-input" name="sub_categories[]" type="checkbox"
|
|
value="{{ $item->id }}">
|
|
<span class="fw-semibold ps-2 fs-6">
|
|
{{ $item->name . '(' . $item->name_en . ')' }}
|
|
</span>
|
|
</label>
|
|
@endforeach
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Date</label>
|
|
<div class="position-relative d-flex align-items-center">
|
|
<span class="svg-icon svg-icon-2 position-absolute mx-4">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
xmlns="http://www.w3.org/2000/svg">
|
|
<path opacity="0.3"
|
|
d="M21 22H3C2.4 22 2 21.6 2 21V5C2 4.4 2.4 4 3 4H21C21.6 4 22 4.4 22 5V21C22 21.6 21.6 22 21 22Z"
|
|
fill="currentColor" />
|
|
<path
|
|
d="M6 6C5.4 6 5 5.6 5 5V3C5 2.4 5.4 2 6 2C6.6 2 7 2.4 7 3V5C7 5.6 6.6 6 6 6ZM11 5V3C11 2.4 10.6 2 10 2C9.4 2 9 2.4 9 3V5C9 5.6 9.4 6 10 6C10.6 6 11 5.6 11 5ZM15 5V3C15 2.4 14.6 2 14 2C13.4 2 13 2.4 13 3V5C13 5.6 13.4 6 14 6C14.6 6 15 5.6 15 5ZM19 5V3C19 2.4 18.6 2 18 2C17.4 2 17 2.4 17 3V5C17 5.6 17.4 6 18 6C18.6 6 19 5.6 19 5Z"
|
|
fill="currentColor" />
|
|
<path
|
|
d="M8.8 13.1C9.2 13.1 9.5 13 9.7 12.8C9.9 12.6 10.1 12.3 10.1 11.9C10.1 11.6 10 11.3 9.8 11.1C9.6 10.9 9.3 10.8 9 10.8C8.8 10.8 8.59999 10.8 8.39999 10.9C8.19999 11 8.1 11.1 8 11.2C7.9 11.3 7.8 11.4 7.7 11.6C7.6 11.8 7.5 11.9 7.5 12.1C7.5 12.2 7.4 12.2 7.3 12.3C7.2 12.4 7.09999 12.4 6.89999 12.4C6.69999 12.4 6.6 12.3 6.5 12.2C6.4 12.1 6.3 11.9 6.3 11.7C6.3 11.5 6.4 11.3 6.5 11.1C6.6 10.9 6.8 10.7 7 10.5C7.2 10.3 7.49999 10.1 7.89999 10C8.29999 9.90003 8.60001 9.80003 9.10001 9.80003C9.50001 9.80003 9.80001 9.90003 10.1 10C10.4 10.1 10.7 10.3 10.9 10.4C11.1 10.5 11.3 10.8 11.4 11.1C11.5 11.4 11.6 11.6 11.6 11.9C11.6 12.3 11.5 12.6 11.3 12.9C11.1 13.2 10.9 13.5 10.6 13.7C10.9 13.9 11.2 14.1 11.4 14.3C11.6 14.5 11.8 14.7 11.9 15C12 15.3 12.1 15.5 12.1 15.8C12.1 16.2 12 16.5 11.9 16.8C11.8 17.1 11.5 17.4 11.3 17.7C11.1 18 10.7 18.2 10.3 18.3C9.9 18.4 9.5 18.5 9 18.5C8.5 18.5 8.1 18.4 7.7 18.2C7.3 18 7 17.8 6.8 17.6C6.6 17.4 6.4 17.1 6.3 16.8C6.2 16.5 6.10001 16.3 6.10001 16.1C6.10001 15.9 6.2 15.7 6.3 15.6C6.4 15.5 6.6 15.4 6.8 15.4C6.9 15.4 7.00001 15.4 7.10001 15.5C7.20001 15.6 7.3 15.6 7.3 15.7C7.5 16.2 7.7 16.6 8 16.9C8.3 17.2 8.6 17.3 9 17.3C9.2 17.3 9.5 17.2 9.7 17.1C9.9 17 10.1 16.8 10.3 16.6C10.5 16.4 10.5 16.1 10.5 15.8C10.5 15.3 10.4 15 10.1 14.7C9.80001 14.4 9.50001 14.3 9.10001 14.3C9.00001 14.3 8.9 14.3 8.7 14.3C8.5 14.3 8.39999 14.3 8.39999 14.3C8.19999 14.3 7.99999 14.2 7.89999 14.1C7.79999 14 7.7 13.8 7.7 13.7C7.7 13.5 7.79999 13.4 7.89999 13.2C7.99999 13 8.2 13 8.5 13H8.8V13.1ZM15.3 17.5V12.2C14.3 13 13.6 13.3 13.3 13.3C13.1 13.3 13 13.2 12.9 13.1C12.8 13 12.7 12.8 12.7 12.6C12.7 12.4 12.8 12.3 12.9 12.2C13 12.1 13.2 12 13.6 11.8C14.1 11.6 14.5 11.3 14.7 11.1C14.9 10.9 15.2 10.6 15.5 10.3C15.8 10 15.9 9.80003 15.9 9.70003C15.9 9.60003 16.1 9.60004 16.3 9.60004C16.5 9.60004 16.7 9.70003 16.8 9.80003C16.9 9.90003 17 10.2 17 10.5V17.2C17 18 16.7 18.4 16.2 18.4C16 18.4 15.8 18.3 15.6 18.2C15.4 18.1 15.3 17.8 15.3 17.5Z"
|
|
fill="currentColor" />
|
|
</svg>
|
|
</span>
|
|
<input class="form-control form-control-solid ps-12" placeholder="Date"
|
|
name="due_date" id="due_date" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Subject</label>
|
|
<input type="text" name="name" class="form-control mb-2 col-6"
|
|
value="{{ old('name') }}" />
|
|
</div>
|
|
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Subject (EN)</label>
|
|
<input type="text" name="name_en" class="form-control mb-2 col-6"
|
|
value="{{ old('name_en') }}" />
|
|
</div>
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Description</label>
|
|
<div class="form-control" id="description" name="description"></div>
|
|
<input type="hidden" id="description_input" name="description" />
|
|
</div>
|
|
|
|
|
|
<div class="mb-10 fv-row">
|
|
<label class="required form-label">Description (EN)</label>
|
|
<div class="form-control" id="description_en" name="description_en"></div>
|
|
<input type="hidden" id="description_input_en" name="description_en" />
|
|
|
|
</div>
|
|
|
|
<div hidden class="mb-10">
|
|
<label class="form-label required">File Video</label>
|
|
<input class="files-video" type="file" name="file_video[]" multiple
|
|
accept=".MP4, .MOV">
|
|
<div class="text-muted fs-7">Allowed Types .MP4, .MOV</div>
|
|
</div>
|
|
|
|
<div hidden class="mb-10">
|
|
<label class="form-label required">File Image</label>
|
|
<input class="files-image" type="file" name="file_image[]" multiple
|
|
accept=".jpg, .bmp, .jpeg, .png">
|
|
<div class="text-muted fs-7">Allowed Types .jpg, .bmp, .jpeg, .png</div>
|
|
</div>
|
|
|
|
<div hidden class="mb-10">
|
|
<label class="form-label required">File Sound</label>
|
|
<input class="files-sound" type="file" name="file_sound[]" multiple
|
|
accept=".m4a, .mp4, .mp3, .wav">
|
|
<div class="text-muted fs-7">Allowed Types .m4a, .mp4, .mp3, .wav</div>
|
|
</div>
|
|
|
|
<div hidden class="mb-10">
|
|
<label class="form-label required">File Documents</label>
|
|
<input class="files-document" type="file" name="file_document[]" multiple
|
|
accept=".pdf, .xls, .doc, .docx, .pptx, .csv, .txt">
|
|
<div class="text-muted fs-7">Allowed Types .pdf, .xls, .doc, .docx, .pptx, .csv,
|
|
.txt</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end">
|
|
|
|
<a href="{{ url('where-else/article') }}" id="kt_ecommerce_add_product_cancel"
|
|
class="btn btn-light me-5">Cancel</a>
|
|
|
|
<button id="btn_submit" class="btn btn-primary">
|
|
<span class="indicator-label">Save Changes</span>
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
@stop
|
|
|
|
|
|
@section('script')
|
|
|
|
<!-- Include Editor.js core -->
|
|
<script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
|
|
|
|
<!-- Include the plugins -->
|
|
<script src="https://cdn.jsdelivr.net/npm/@editorjs/header"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@editorjs/list"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@editorjs/image"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/editorjs-inline-image"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@editorjs/embed"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/editorjs-break-line"></script>
|
|
|
|
<script>
|
|
// Custom video tool for embedding videos
|
|
class VideoTool {
|
|
constructor({
|
|
data
|
|
}) {
|
|
this.data = data || {};
|
|
}
|
|
|
|
// Render input for video URL
|
|
render() {
|
|
const wrapper = document.createElement("div");
|
|
|
|
const input = document.createElement("input");
|
|
input.type = "text";
|
|
input.placeholder = "Enter video URL";
|
|
input.value = this.data.url || "";
|
|
input.style.width = "100%";
|
|
|
|
const video = document.createElement("video");
|
|
video.controls = true;
|
|
video.style.width = "100%";
|
|
video.style.marginTop = "10px";
|
|
video.src = this.data.url || "";
|
|
|
|
input.addEventListener("input", (event) => {
|
|
const value = event.target.value;
|
|
this.data.url = value;
|
|
video.src = value;
|
|
});
|
|
|
|
wrapper.appendChild(input);
|
|
wrapper.appendChild(video);
|
|
return wrapper;
|
|
}
|
|
|
|
// Save the video URL
|
|
save(blockContent) {
|
|
const input = blockContent.querySelector("input");
|
|
return {
|
|
url: input.value,
|
|
};
|
|
}
|
|
|
|
// Editor.js toolbox definition
|
|
static get toolbox() {
|
|
return {
|
|
title: "Video",
|
|
icon: '<svg width="20" height="20" viewBox="0 0 24 24"><path d="M10 8.64L15.27 12 10 15.36V8.64M10 3.14L21 12 10 20.86V3.14M3 4h4v16H3V4z"></path></svg>',
|
|
};
|
|
}
|
|
}
|
|
class YoutubeEmbed {
|
|
/**
|
|
* Define the toolbox settings for the Editor.js toolbar.
|
|
*/
|
|
static get toolbox() {
|
|
return {
|
|
title: "YouTube",
|
|
icon: '<svg width="18" height="18" viewBox="0 0 24 24"><path d="M19.615 3.184c-.403-1.516-1.589-2.693-3.09-3.091-2.719-.727-13.525-.727-16.244 0-1.514.398-2.687 1.57-3.09 3.091-.727 2.719-.727 13.525 0 16.244.403 1.514 1.576 2.693 3.09 3.09 2.719.727 13.525.727 16.244 0 1.516-.397 2.692-1.576 3.09-3.09.727-2.719.727-13.525 0-16.244zm-11.615 13.316v-8l8 4-8 4z"/></svg>',
|
|
};
|
|
}
|
|
|
|
constructor({
|
|
data,
|
|
config,
|
|
api,
|
|
readOnly
|
|
}) {
|
|
this.data = data || {};
|
|
this.readOnly = readOnly;
|
|
this.wrapper = null;
|
|
this.url = this.data.url || '';
|
|
this.isEdited = false;
|
|
}
|
|
|
|
/**
|
|
* Render the input field and YouTube preview.
|
|
*/
|
|
render() {
|
|
this.wrapper = document.createElement('div');
|
|
this.wrapper.classList.add('block-wrapper');
|
|
|
|
const input = document.createElement('input');
|
|
input.value = this.url;
|
|
input.placeholder = "Paste YouTube URL here...";
|
|
input.style.width = '100%';
|
|
|
|
this.wrapper.appendChild(input);
|
|
this._createIframe(this.url);
|
|
|
|
input.addEventListener('change', (event) => {
|
|
this.isEdited = true;
|
|
this.url = event.target.value;
|
|
this._createIframe(this.url);
|
|
});
|
|
|
|
return this.wrapper;
|
|
}
|
|
|
|
/**
|
|
* Create the iframe for the YouTube video.
|
|
*/
|
|
_createIframe(url) {
|
|
const videoId = this._extractVideoID(url);
|
|
if (!videoId) {
|
|
if (this.isEdited) {
|
|
this.wrapper.querySelector('input').classList.add('invalid');
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.wrapper.innerHTML = ''; // Clear the wrapper
|
|
|
|
const plyrContainer = document.createElement('div');
|
|
plyrContainer.classList.add('video-wrapper');
|
|
|
|
const iframe = document.createElement('iframe');
|
|
iframe.src = `https://www.youtube.com/embed/${videoId}`;
|
|
iframe.allowFullscreen = true;
|
|
|
|
plyrContainer.appendChild(iframe);
|
|
this.wrapper.appendChild(plyrContainer);
|
|
}
|
|
|
|
/**
|
|
* Extract the YouTube video ID from the URL.
|
|
*/
|
|
_extractVideoID(url) {
|
|
const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&?/]+)/;
|
|
const match = url.match(regex);
|
|
return match ? match[1] : null;
|
|
}
|
|
|
|
/**
|
|
* Return the saved data for this block.
|
|
*/
|
|
save(blockContent) {
|
|
return {
|
|
url: this.url,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Notify the core that this tool supports read-only mode.
|
|
*/
|
|
static get isReadOnlySupported() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
|
const baseUrlUpload = "{{ url('where-else/article-upload-image') }}";
|
|
const tools = {
|
|
header: {
|
|
class: Header,
|
|
inlineToolbar: ['link', 'bold', 'italic'],
|
|
config: {
|
|
placeholder: 'Enter a header',
|
|
levels: [2, 3, 4], // Set header levels
|
|
defaultLevel: 3,
|
|
},
|
|
},
|
|
breakLine: {
|
|
class: BreakLine,
|
|
inlineToolbar: true,
|
|
shortcut: 'CMD+SHIFT+ENTER',
|
|
},
|
|
youtube: YoutubeEmbed,
|
|
embed: {
|
|
class: Embed,
|
|
inlineToolbar: true,
|
|
config: {
|
|
services: {
|
|
youtube: true,
|
|
vimeo: true,
|
|
},
|
|
},
|
|
},
|
|
video: {
|
|
class: VideoTool,
|
|
inlineToolbar: true,
|
|
},
|
|
image: {
|
|
class: ImageTool,
|
|
config: {
|
|
endpoints: {
|
|
byFile: baseUrlUpload,
|
|
byUrl: baseUrlUpload,
|
|
},
|
|
field: 'image',
|
|
types: 'image/*',
|
|
inlineToolbar: true,
|
|
additionalRequestHeaders: {
|
|
'X-CSRF-TOKEN': csrfToken, // Include CSRF token here
|
|
},
|
|
},
|
|
},
|
|
inlineImage: {
|
|
class: InlineImage,
|
|
inlineToolbar: true,
|
|
config: {
|
|
embed: {
|
|
display: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
const editorDescription = new EditorJS({
|
|
holder: 'description',
|
|
tools: tools
|
|
});
|
|
const editorDescriptionEn = new EditorJS({
|
|
holder: 'description_en',
|
|
tools: tools,
|
|
});
|
|
</script>
|
|
|
|
<script type="text/javascript">
|
|
$(function() {
|
|
$('#due_date').flatpickr({
|
|
enableTime: false,
|
|
dateFormat: 'd-m-Y',
|
|
defaultDate: null, // Do not preselect any date
|
|
onReady: function(selectedDates, dateStr, instance) {
|
|
// Highlight today's date with a custom CSS class
|
|
const today = instance.todayDateElem;
|
|
today.classList.add('flatpickr-today-highlight'); // Add a custom class to today
|
|
}
|
|
});
|
|
|
|
$(document).on('click', '#btn_submit', function(e) {
|
|
e.preventDefault(); // Prevent default form submission
|
|
|
|
alertLoading("Loading..."); // Show loading alert (your custom function)
|
|
|
|
// Save both editor contents
|
|
Promise.all([editorDescription.save(), editorDescriptionEn.save()])
|
|
.then(([descriptionData, descriptionEnData]) => {
|
|
|
|
// Store the JSON output in the hidden input fields
|
|
$('#description_input').val(JSON.stringify(descriptionData));
|
|
$('#description_input_en').val(JSON.stringify(descriptionEnData));
|
|
|
|
// Submit the form after saving the data
|
|
$('#frmAdd').submit();
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error saving editor content:', error);
|
|
});
|
|
});
|
|
|
|
|
|
});
|
|
</script>
|
|
|
|
|
|
@endsection
|