Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/resources/lang/en/logmanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@
'delete_error_message' => 'The log file has NOT been deleted.',
'delete_confirmation_message' => 'The log file was deleted.',
'log_file_doesnt_exist' => "The log file doesn't exist.",
'link_copied_to_clipboard' => 'Link copied to clipboard',
'link_copy_failed' => 'Failed to copy link',

];
103 changes: 96 additions & 7 deletions src/resources/views/log_item.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,20 @@
<div id="accordion" role="tablist" aria-multiselectable="true">
@forelse($logs as $key => $log)
<div class="card mb-0 pb-0">
<div class="card-header bg-{{ $log['level_class'] }}" role="tab" id="heading{{ $key }}">
<a role="button" data-toggle="collapse" data-bs-toggle="collapse" data-parent="#accordion" href="#collapse{{ $key }}" aria-expanded="true" aria-controls="collapse{{ $key }}" class="text-white">
<div class="card-header bg-{{ $log['level_class'] }} d-flex align-items-center" role="tab" id="heading{{ $key }}">
<a href="#heading{{ $key }}" class="log-anchor text-white me-2 mr-2 border border-white rounded-circle text-center text-decoration-none flex-shrink-0" style="width: 30px; height: 30px; line-height: 28px;">
<i class="la la-link"></i>
</a>
<a role="button" data-toggle="collapse" data-bs-toggle="collapse" data-parent="#accordion" href="#collapse{{ $key }}" aria-expanded="false" aria-controls="collapse{{ $key }}" class="text-white flex-grow-1 collapsed">
<i class="la la-{{ $log['level_img'] }}"></i>
<span>[{{ $log['date'] }}]</span>
{{ Str::limit($log['text'], 150) }}
</a>
</div>
<div id="collapse{{ $key }}" class="panel-collapse collapse p-3" role="tabpanel" aria-labelledby="heading{{ $key }}">
<div id="collapse{{ $key }}" class="panel-collapse collapse p-3" role="tabpanel" aria-labelledby="heading{{ $key }}" data-parent="#accordion" data-bs-parent="#accordion">
<div class="panel-body">
<p>{{$log['text']}}</p>
<pre class="p-0" ><code class="php">{{ trim($log['stack']) }}</code></pre>
<pre class="p-0" ><code>{{ trim($log['stack']) }}</code></pre>
</div>
</div>
</div>
Expand All @@ -49,7 +52,93 @@
@endsection

@section('after_scripts')
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<script>
function openAccordionFromHash() {
if (window.location.hash && window.location.hash.startsWith('#heading')) {
const heading = document.querySelector(window.location.hash);
if (heading) {
const trigger = heading.querySelector('a[data-toggle="collapse"], a[data-bs-toggle="collapse"]');

if (trigger) {
const targetId = trigger.getAttribute('href') || trigger.getAttribute('data-bs-target');
const collapseEl = document.querySelector(targetId);
const isExpanded = collapseEl && (collapseEl.classList.contains('show') || collapseEl.classList.contains('in'));

if (!isExpanded) {
trigger.click();

try {
if (collapseEl && !collapseEl.classList.contains('show')) {
const bsCollapse = bootstrap.Collapse.getInstance(collapseEl) || new bootstrap.Collapse(collapseEl, { toggle: false });
bsCollapse.show();
}
} catch (e) {
//
}
}
}

setTimeout(() => {
const elementPosition = heading.getBoundingClientRect().top + window.scrollY;
const offsetPosition = elementPosition - 80;

window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}, 250);
}
}
}

document.addEventListener('DOMContentLoaded', () => {
openAccordionFromHash();

window.addEventListener('hashchange', () => {
openAccordionFromHash();
});


function showNotification(type, text) {
new Noty({
text: text,
type: type
}).show();
}

async function copyToClipboard(text) {
try {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
showNotification('success', "{{ trans('backpack::logmanager.link_copied_to_clipboard') }}");
} else {
throw new Error('Clipboard API unavailable');
}
} catch (err) {
// Fallback
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
showNotification('success', "{{ trans('backpack::logmanager.link_copied_to_clipboard') }}");
} catch (fallbackErr) {
showNotification('error', "{{ trans('backpack::logmanager.link_copy_failed') }}");
}
document.body.removeChild(textArea);
}
}

document.querySelectorAll('.log-anchor').forEach(anchor => {
anchor.addEventListener('click', function(e) {
const url = window.location.origin + window.location.pathname + this.getAttribute('href');
copyToClipboard(url);
});
});
});
</script>
@endsection