Files
citrus-cms/resources/views/admin/master.blade.php
T

617 lines
22 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if (getisset("ajax")) {
?>
@include("admin-ajax.{$_GET['ajax']}")
<?php
exit();
} ?>
<?php if (getisset("ajax2")) { //blade ajax system
?>
@if(getisset("cache"))
@includeCache("{$_GET['ajax2']}")
@else
@include("{$_GET['ajax2']}")
@endif
<?php
exit();
} ?>
@php
$permissions = userPermissions();
@endphp
<?php $u = u(); ?>
<!DOCTYPE HTML>
<html lang="{{App::getLocale()}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>{!! strip_tags($__env->yieldContent('title', '')) !!}</title>
<link rel="manifest" href="/assets/manifest.json">
<meta name="author" content="Truncgil Technology">
<meta property="og:title" content="">
<meta property="og:site_name" content="https://www.truncgil.com.tr/">
<meta property="og:description" content="">
<meta property="og:type" content="app">
<meta property="og:url" content>
<meta property="og:image" content>
<div class="header-zone">
<link rel="apple-touch-icon" sizes="180x180" href="{{url("assets/favicon.png")}}">
<link rel="shortcut icon" href="{{url("assets/favicon.png")}}" type="image/png">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="theme-color" content="#fff" />
<meta name="description" content="{!! strip_tags($__env->yieldContent('title', '')) !!}">
<!-- Favicon -->
<link rel="icon" href="/assets/favicon.png" type="image/png">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(function (registration) {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(function (error) {
console.log('Service Worker registration failed:', error);
});
}
</script>
@include("admin.inc.plugins")
@include("admin.inc.citrus-design")
@yield('scripts')
</div>
</head>
<body>
@guest
@yield("content")
@else
<div id="page-container" @if(isset($contents))
class="sidebar-o enable-page-overlay side-scroll page-header-fixed page-header-glass side-trans-enabled sidebar-inverse"
@endif>
<aside id="side-overlay">
<div class="content-header content-header-fullrow">
<div class="content-header-section align-parent">
<button type="button" class="btn btn-circle btn-dual-secondary align-v-r" data-toggle="layout"
data-action="side_overlay_close">
<i class="fa fa-times text-danger"></i>
</button>
<div class="content-header-item">
<a class="align-middle text-primary-dark font-w600" href="#">
{{ Auth::user()->name }} {{ Auth::user()->surname }}
</a>
</div>
</div>
</div>
@include("admin.inc.side-overlay")
</aside>
@if(isset($contents))
<nav id="sidebar">
<div class="sidebar-content">
<div class="content-side content-side-full content-side-user px-10 align-parent">
<!-- Visible only in mini mode -->
<div class="sidebar-mini-visible-b align-v animated fadeIn">
<a href="{{url("admin")}}">
<img class="img-fluid" src="{{ url("assets/citrus-logos/citrus-yatay.svg") }}" alt="">
</a>
</div>
<!-- END Visible only in mini mode -->
<!-- Visible only in normal mode -->
<div class="sidebar-mini-hidden-b text-center">
<a class="img-link" href="{{url("admin")}}">
<img class="img-fluid" src="{{ url("assets/citrus-logos/citrus-yatay.svg") }}" alt="">
</a>
<a class="text-center text-dual-primary-dark font-size-xs font-w600"
href="#">{{ Auth::user()->name }} {{ Auth::user()->surname }}</a>
<div class="text-center badge badge-success">{{ Auth::user()->level }}</div>
<div class="text-center badge badge-info">{{ Auth::user()->subcontructer }}</div>
</div>
<!-- END Visible only in normal mode -->
</div>
@include("admin.inc.menu")
</div>
</nav>
@include("admin.inc.header")
@endif
<div class="main-container">
<div class="">
@if (View::hasSection('title'))
<div class="bg-image d-none">
<div class="bg-white-op-90">
<div class="content content-full content-top">
<h1 class="text-center">@yield("title")<br /> </h1>
<div class="text-center d-none">@yield("desc")</div>
</div>
</div>
</div>
@endif
<div class="content-ajax" style="margin-top:70px">
@yield("content")
</div>
</div>
</div>
<div class="clearfix"></div>
@include('admin.inc.feedback-modal')
<footer id="page-footer" class="t-center" style=" background: gray;
color: white;
padding: 5px 10px;
position: fixed;
bottom: 0px;
right: 0px;
width: 100%;
text-align: right;
font-size: 12px;">
<div class="footer-status"></div>
</footer>
</div>
@endguest
<!--
<link rel="stylesheet" href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" />
<script type="text/javascript" src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
-->
@include("admin.inc.script")
<style type="text/css">
.hidden {
display: none !important;
}
.visible {
display: block !important;
}
.hidden-upload {
display: none;
}
.table-responsive {
/* background: url(back.png) white center center / contain no-repeat !important; */
/* background-attachment: fixed !important; */
background-size: 20% !important;
background-position: center !important;
}
.dz-filename {
white-space: normal !important;
height: 74px;
}
</style>
<div class="modal fade" id="modal-popin" tabindex="-1" role="dialog" aria-labelledby="modal-popin"
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-popin" role="document">
<div class="modal-content">
<div class="block block-themed block-transparent mb-0">
<div class="block-header bg-primary-dark">
<h3 class="block-title"></h3>
<div class="block-options">
<button type="button" class="btn-block-option" data-dismiss="modal" aria-label="Close">
<i class="si si-close"></i>
</button>
</div>
</div>
<div class="block-content">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-alt-secondary" data-dismiss="modal">{{__('Hayır')}}</button>
<a href="#" class="btn btn-alt-success tamam" data-dismiss="modal">
<i class="fa fa-check"></i> {{__('Evet')}}
</a>
</div>
</div>
</div>
</div>
<!-- Notification System CSS -->
<style>
.notification-dropdown .notification-badge {
min-width: 18px;
height: 18px;
line-height: 14px;
}
.notification-menu {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.notification-list {
max-height: 400px;
overflow-y: auto;
}
.notification-item {
display: flex;
align-items: flex-start;
padding: 14px 18px;
border-bottom: 1px solid rgba(17, 25, 40, 0.08);
cursor: pointer;
position: relative;
background: #ffffff;
transition: background 0.2s ease, box-shadow 0.2s ease;
}
.notification-item:hover {
background: #e8f1ff;
}
.notification-item.unread {
background: #eef6ff;
}
.notification-item.unread::after {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background: #3a7bd5;
position: absolute;
top: 18px;
right: 18px;
box-shadow: 0 0 0 2px rgba(58, 123, 213, 0.15);
}
.notification-item:last-child {
border-bottom: none;
}
.notification-item .icon {
width: 36px;
height: 36px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(58, 123, 213, 0.1);
color: #3a7bd5;
margin-right: 12px;
font-size: 16px;
flex-shrink: 0;
}
.notification-item .icon i {
line-height: 1;
}
.notification-item .content {
flex: 1;
min-width: 0;
}
.notification-item .content .title {
font-size: 14px;
font-weight: 600;
color: #111928;
margin-bottom: 4px;
}
.notification-item .content .message {
font-size: 13px;
color: #4a5568;
margin-bottom: 6px;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.notification-item .content .meta {
font-size: 12px;
color: #6c757d;
display: flex;
align-items: center;
gap: 6px;
}
.notification-item .content .meta i {
font-size: 11px;
opacity: 0.6;
}
.notification-empty {
padding: 48px 24px;
text-align: center;
color: #6c757d;
}
.notification-empty i {
font-size: 36px;
margin-bottom: 12px;
color: rgba(58, 123, 213, 0.55);
}
</style>
<!-- Notification System JavaScript -->
<script>
// Performance: Debounce function to limit API calls
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Performance: Cache for notification count to avoid repeated API calls
let notificationCountCache = null;
let notificationCountCacheTime = 0;
const CACHE_DURATION = 30000; // 30 seconds
// Load notifications for dropdown
function loadNotifications() {
$.ajax({
url: '/api/notifications/recent',
method: 'GET',
success: function (response) {
displayNotifications(response.notifications);
updateNotificationCount(response.unread_count);
},
error: function () {
$('#notification-list').html(`
<li class="notification-placeholder">
<i class="fa fa-exclamation-triangle text-danger"></i>
<div class="font-weight-bold">{{e2("Unable to load notifications")}}</div>
<div class="small">{{e2("Please try again shortly")}}</div>
</li>
`);
$('#notification-unread-indicator').text('{{e2("Error")}}');
}
});
}
function getNotificationIcon(notificationCode, isRead) {
if (!isRead) {
return 'fa-bell';
}
if (!notificationCode) return 'fa-bell';
const code = notificationCode.toLowerCase();
if (code.includes('success') || code.includes('complete') || code.includes('created')) return 'fa-check-circle';
if (code.includes('error') || code.includes('fail')) return 'fa-exclamation-circle';
if (code.includes('warning') || code.includes('alert')) return 'fa-exclamation-triangle';
if (code.includes('info') || code.includes('update')) return 'fa-info-circle';
if (code.includes('test') || code.includes('inspection')) return 'fa-clipboard-check';
if (code.includes('document') || code.includes('file')) return 'fa-file-alt';
if (code.includes('user') || code.includes('account')) return 'fa-user';
if (code.includes('system') || code.includes('maintenance')) return 'fa-cog';
return 'fa-bell';
}
// Display notifications in dropdown
function displayNotifications(notifications) {
const listContainer = $('#notification-list');
const defaultTitle = '{{e2("Notification")}}';
const defaultMessage = '{{e2("No additional details")}}';
if (!notifications || notifications.length === 0) {
listContainer.html(`
<li class="notification-placeholder">
<i class="fa fa-bell-slash"></i>
<div class="font-weight-bold">{{e2("No notifications")}}</div>
<div class="small">{{e2("You are all caught up")}}</div>
</li>
`);
return;
}
let html = '';
notifications.forEach(function (notif) {
const isUnread = !notif.is_read;
const unreadClass = isUnread ? 'unread' : '';
const timeAgo = formatTimeAgo(notif.created_at);
const link = notif.link || '#';
const safeTitle = notif.title ? escapeHtml(notif.title) : defaultTitle;
const safeMessage = notif.message ? escapeHtml(notif.message) : defaultMessage;
const iconClass = getNotificationIcon(notif.notification_code, !isUnread);
const truncatedMessage = safeMessage.length > 100
? safeMessage.substring(0, 100) + '...'
: safeMessage;
html += `
<li class="notification-item ${unreadClass}"
data-id="${notif.id}"
onclick="handleNotificationClick(${notif.id}, '${escapeHtml(link)}')"
title="${safeMessage}">
<div class="icon">
<i class="fa ${iconClass}"></i>
</div>
<div class="content">
<div class="title">${safeTitle}</div>
<div class="message">${truncatedMessage}</div>
<div class="meta">
<i class="fa fa-clock"></i>
${timeAgo}
</div>
</div>
</li>
`;
});
listContainer.html(html);
}
// Handle notification click
function handleNotificationClick(notificationId, link) {
// Mark as read and wait for response before navigation
$.ajax({
url: '/api/notifications/mark-read/' + notificationId,
method: 'POST',
data: { _token: '{{ csrf_token() }}' },
async: false, // Wait for mark-read to complete
success: function (response) {
if (response.success) {
updateNotificationCount(response.unread_count);
$('[data-id="' + notificationId + '"]').removeClass('unread');
}
}
});
// Navigate to link if provided
if (link && link !== '#') {
window.location.href = link;
}
}
// Mark all notifications as read
function markAllAsRead() {
$.ajax({
url: '/api/notifications/mark-all-read',
method: 'POST',
data: { _token: '{{ csrf_token() }}' },
success: function (response) {
if (response.success) {
updateNotificationCount(0);
$('.notification-item').removeClass('unread');
Swal.fire({
icon: 'success',
title: '{{e2("Success")}}',
text: '{{e2("All notifications marked as read")}}',
timer: 2000,
showConfirmButton: false
});
}
}
});
}
// Update notification count badge
function updateNotificationCount(count) {
notificationCountCache = count;
notificationCountCacheTime = Date.now();
const badge = $('#notification-count');
const indicator = $('#notification-unread-indicator');
if (count > 0) {
const displayCount = count > 99 ? '99+' : count;
badge.text(displayCount).show();
if (indicator.length) {
indicator.text(`${displayCount} {{e2("new")}}`).removeClass('badge-secondary').addClass('badge-primary');
}
} else {
badge.hide();
if (indicator.length) {
indicator.text('{{e2("All caught up")}}').removeClass('badge-primary').addClass('badge-success');
}
}
}
// Fetch notification count (with caching)
function fetchNotificationCount() {
// Use cache if valid
if (notificationCountCache !== null &&
(Date.now() - notificationCountCacheTime) < CACHE_DURATION) {
updateNotificationCount(notificationCountCache);
return;
}
// Performance: Only fetch count, not full notifications
$.ajax({
url: '/api/notifications/unread-count',
method: 'GET',
success: function (response) {
updateNotificationCount(response.unread_count);
}
});
}
// Format time ago
function formatTimeAgo(dateString) {
const date = new Date(dateString);
const now = new Date();
const seconds = Math.floor((now - date) / 1000);
if (seconds < 60) return '{{e2("Just now")}}';
if (seconds < 3600) return Math.floor(seconds / 60) + ' {{e2("min ago")}}';
if (seconds < 86400) return Math.floor(seconds / 3600) + ' {{e2("h ago")}}';
if (seconds < 604800) return Math.floor(seconds / 86400) + ' {{e2("d ago")}}';
return date.toLocaleDateString();
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function (m) { return map[m]; });
}
// Initialize notification system on page load
$(document).ready(function () {
// Fetch initial count
fetchNotificationCount();
// Poll for new notifications every 15 seconds (for testing - can be increased to 60s in production)
setInterval(fetchNotificationCount, 15000);
// Prevent dropdown from closing when clicking inside
$('.notification-menu').on('click', function (e) {
e.stopPropagation();
});
});
</script>
<div id="screenshotOverlay" style="
display:none;
position:fixed;
inset:0;
background:rgba(0,0,0,.7);
z-index:9999;
padding:20px;
overflow:auto;">
<div style="background:#fff; padding:10px; max-width:90%; margin:0 auto;">
<h5 style="margin-bottom:10px">Crop your screenshot</h5>
<div id="overlayPreview"></div>
<button id="useCropped" class="btn btn-sm btn-primary mt-2">Use this & open feedback</button>
<button id="cancelCrop" class="btn btn-sm btn-secondary mt-2">Cancel</button>
</div>
</div>
</body>
</html>
<style type="text/css">
.cke_button__easyimageupload {
display: none !important;
}
</style>