Initial release

This commit is contained in:
Ludovic CANDELLIER
2020-03-22 21:30:03 +01:00
commit baf8e13c25
193 changed files with 8170 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Unauthorized'))
@section('code', '401')
@section('message', __('Unauthorized'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Forbidden'))
@section('code', '403')
@section('message', __($exception->getMessage() ?: 'Forbidden'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Not Found'))
@section('code', '404')
@section('message', __('Not Found'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Page Expired'))
@section('code', '419')
@section('message', __('Page Expired'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Too Many Requests'))
@section('code', '429')
@section('message', __('Too Many Requests'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Server Error'))
@section('code', '500')
@section('message', __('Server Error'))

View File

@@ -0,0 +1,5 @@
@extends('errors::minimal')
@section('title', __('Service Unavailable'))
@section('code', '503')
@section('message', __($exception->getMessage() ?: 'Service Unavailable'))

View File

@@ -0,0 +1,486 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>@yield('title')</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<style>
html {
line-height: 1.15;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
header,
nav,
section {
display: block;
}
figcaption,
main {
display: block;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
strong {
font-weight: inherit;
}
strong {
font-weight: bolder;
}
code {
font-family: monospace, monospace;
font-size: 1em;
}
dfn {
font-style: italic;
}
svg:not(:root) {
overflow: hidden;
}
button,
input {
font-family: sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button,
input {
overflow: visible;
}
button {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
legend {
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
[type="checkbox"],
[type="radio"] {
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
menu {
display: block;
}
canvas {
display: inline-block;
}
template {
display: none;
}
[hidden] {
display: none;
}
html {
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: sans-serif;
}
*,
*::before,
*::after {
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
p {
margin: 0;
}
button {
background: transparent;
padding: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
*,
*::before,
*::after {
border-width: 0;
border-style: solid;
border-color: #dae1e7;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
border-radius: 0;
}
button,
input {
font-family: inherit;
}
input::-webkit-input-placeholder {
color: inherit;
opacity: .5;
}
input:-ms-input-placeholder {
color: inherit;
opacity: .5;
}
input::-ms-input-placeholder {
color: inherit;
opacity: .5;
}
input::placeholder {
color: inherit;
opacity: .5;
}
button,
[role=button] {
cursor: pointer;
}
.bg-transparent {
background-color: transparent;
}
.bg-white {
background-color: #fff;
}
.bg-teal-light {
background-color: #64d5ca;
}
.bg-blue-dark {
background-color: #2779bd;
}
.bg-indigo-light {
background-color: #7886d7;
}
.bg-purple-light {
background-color: #a779e9;
}
.bg-no-repeat {
background-repeat: no-repeat;
}
.bg-cover {
background-size: cover;
}
.border-grey-light {
border-color: #dae1e7;
}
.hover\:border-grey:hover {
border-color: #b8c2cc;
}
.rounded-lg {
border-radius: .5rem;
}
.border-2 {
border-width: 2px;
}
.hidden {
display: none;
}
.flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.items-center {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.justify-center {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.font-sans {
font-family: Nunito, sans-serif;
}
.font-light {
font-weight: 300;
}
.font-bold {
font-weight: 700;
}
.font-black {
font-weight: 900;
}
.h-1 {
height: .25rem;
}
.leading-normal {
line-height: 1.5;
}
.m-8 {
margin: 2rem;
}
.my-3 {
margin-top: .75rem;
margin-bottom: .75rem;
}
.mb-8 {
margin-bottom: 2rem;
}
.max-w-sm {
max-width: 30rem;
}
.min-h-screen {
min-height: 100vh;
}
.py-3 {
padding-top: .75rem;
padding-bottom: .75rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.pb-full {
padding-bottom: 100%;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.pin {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.text-black {
color: #22292f;
}
.text-grey-darkest {
color: #3d4852;
}
.text-grey-darker {
color: #606f7b;
}
.text-2xl {
font-size: 1.5rem;
}
.text-5xl {
font-size: 3rem;
}
.uppercase {
text-transform: uppercase;
}
.antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.tracking-wide {
letter-spacing: .05em;
}
.w-16 {
width: 4rem;
}
.w-full {
width: 100%;
}
@media (min-width: 768px) {
.md\:bg-left {
background-position: left;
}
.md\:bg-right {
background-position: right;
}
.md\:flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.md\:my-6 {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.md\:min-h-screen {
min-height: 100vh;
}
.md\:pb-0 {
padding-bottom: 0;
}
.md\:text-3xl {
font-size: 1.875rem;
}
.md\:text-15xl {
font-size: 9rem;
}
.md\:w-1\/2 {
width: 50%;
}
}
@media (min-width: 992px) {
.lg\:bg-center {
background-position: center;
}
}
</style>
</head>
<body class="antialiased font-sans">
<div class="md:flex min-h-screen">
<div class="w-full md:w-1/2 bg-white flex items-center justify-center">
<div class="max-w-sm m-8">
<div class="text-black text-5xl md:text-15xl font-black">
@yield('code', __('Oh no'))
</div>
<div class="w-16 h-1 bg-purple-light my-3 md:my-6"></div>
<p class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal">
@yield('message')
</p>
<a href="{{ app('router')->has('home') ? route('home') : url('/') }}">
<button class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">
{{ __('Go Home') }}
</button>
</a>
</div>
</div>
<div class="relative pb-full md:flex md:pb-0 md:min-h-screen w-full md:w-1/2">
@yield('image')
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title')</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.content {
text-align: center;
}
.title {
font-size: 36px;
padding: 20px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="content">
<div class="title">
@yield('message')
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title')</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.code {
border-right: 2px solid;
font-size: 26px;
padding: 0 15px 0 15px;
text-align: center;
}
.message {
font-size: 18px;
text-align: center;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="code">
@yield('code')
</div>
<div class="message" style="padding: 10px;">
@yield('message')
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
@foreach($items as $item)
<li @lm_attrs($item) @if($item->hasChildren()) class="nav-item dropdown" @endif @lm_endattrs>
@if($item->link) <a @lm_attrs($item->link) @if($item->hasChildren()) class="nav-link dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @else class="nav-link" @endif @lm_endattrs href="{!! $item->url() !!}">
{!! $item->title !!}
@if($item->hasChildren()) <b class="caret"></b> @endif
</a>
@else
<span class="navbar-text">{!! $item->title !!}</span>
@endif
@if($item->hasChildren())
<ul class="dropdown-menu">
@include(config('laravel-menu.views.bootstrap-items'),
array('items' => $item->children()))
</ul>
@endif
</li>
@if($item->divider)
<li{!! Lavary\Menu\Builder::attributes($item->divider) !!}></li>
@endif
@endforeach

View File

@@ -0,0 +1,342 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LogViewer - Created by ARCANEDEV</title>
<meta name="description" content="LogViewer">
<meta name="author" content="ARCANEDEV">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700|Source+Sans+Pro:400,600' rel='stylesheet' type='text/css'>
<style>
html {
position: relative;
min-height: 100%;
}
body {
padding-top: 50px;
/* Margin bottom by footer height */
margin-bottom: 50px;
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, sans-serif;
font-weight: 600;
}
h1, h2, h3 {
font-family: 'Montserrat', 'Helvetica Neue', Helvetica, sans-serif;
}
.sub-header {
padding-bottom: 10px;
border-bottom: 1px solid #EEE;
}
.navbar-inverse {
background-color: #1a237e;
border-color: #1a237e;
}
.navbar-inverse .navbar-nav>.active>a,
.navbar-inverse .navbar-nav>.active>a:focus,
.navbar-inverse .navbar-nav>.active>a:hover {
background-color: #3949ab;
}
.navbar-inverse .navbar-brand {
color: #c5cae9;
}
.navbar-inverse .navbar-nav>li>a {
color: #c5cae9;
}
.navbar-fixed-top {
border: 0;
}
.main {
padding: 20px;
}
.main .page-header {
margin-top: 0;
}
footer.main-footer {
position: absolute;
padding: 10px 0;
bottom: 0;
width: 100%;
background-color: #e8eaf6;
font-weight: 600;
}
footer.main-footer p {
margin: 0;
}
footer.main-footer i.fa.fa-heart {
color: #C62828;
}
.pagination {
margin: 0;
}
.pagination > li > a,
.pagination > li > span {
padding: 4px 10px;
}
.table-condensed > tbody > tr > td.stack,
.table-condensed > tfoot > tr > td.stack,
.table-condensed > thead > tr > td.stack {
padding: 0;
border-top: none;
background-color: #F6F6F6;
border-top: 1px solid #D1D1D1;
max-width: 0;
overflow-x: auto;
}
.table-condensed > tbody > tr > td > p {
margin: 0;
}
.stack-content {
padding: 8px;
color: #AE0E0E;
font-family: consolas, Menlo, Courier, monospace;
font-size: 12px;
font-weight: 400;
white-space: pre-line;
}
.info-box.level {
display: block;
padding: 0;
margin-bottom: 15px;
min-height: 70px;
background: #fff;
width: 100%;
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
border-radius: 2px;
}
.info-box.level .info-box-text,
.info-box.level .info-box-number,
.info-box.level .info-box-icon > i {
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}
.info-box.level .info-box-text {
display: block;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.info-box.level .info-box-content {
padding: 5px 10px;
margin-left: 70px;
}
.info-box.level .info-box-number {
display: block;
font-weight: bold;
font-size: 18px;
}
.info-box.level .info-box-icon {
border-radius: 2px 0 0 2px;
display: block;
float: left;
height: 70px; width: 70px;
text-align: center;
font-size: 40px;
line-height: 70px;
background: rgba(0,0,0,0.2);
}
.info-box.level .progress {
background: rgba(0,0,0,0.2);
margin: 5px -10px 5px -10px;
height: 2px;
}
.info-box.level .progress .progress-bar {
background: #fff;
}
.info-box.level-empty {
opacity: .6;
-webkit-filter: grayscale(1);
-moz-filter: grayscale(1);
-ms-filter: grayscale(1);
filter: grayscale(1);
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
-webkit-transition-property: -webkit-filter, opacity;
-moz-transition-property: -moz-filter, opacity;
-o-transition-property: filter, opacity;
transition-property: -webkit-filter, -moz-filter, -o-filter, filter, opacity;
}
.info-box.level-empty:hover {
opacity: 1;
-webkit-filter: grayscale(0);
-moz-filter: grayscale(0);
-ms-filter: grayscale(0);
filter: grayscale(0);
}
.level {
padding: 2px 6px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
border-radius: 2px;
font-size: .9em;
font-weight: 600;
}
.badge.level-all,
.badge.level-emergency,
.badge.level-alert,
.badge.level-critical,
.badge.level-error,
.badge.level-warning,
.badge.level-notice,
.badge.level-info,
.badge.level-debug,
.level, .level i,
.info-box.level-all,
.info-box.level-emergency,
.info-box.level-alert,
.info-box.level-critical,
.info-box.level-error,
.info-box.level-warning,
.info-box.level-notice,
.info-box.level-info,
.info-box.level-debug {
color: #FFF;
}
.label-env {
font-size: .85em;
}
.badge.level-all, .level.level-all, .info-box.level-all {
background-color: {{ log_styler()->color('all') }};
}
.badge.level-emergency, .level.level-emergency, .info-box.level-emergency {
background-color: {{ log_styler()->color('emergency') }};
}
.badge.level-alert, .level.level-alert, .info-box.level-alert {
background-color: {{ log_styler()->color('alert') }};
}
.badge.level-critical, .level.level-critical, .info-box.level-critical {
background-color: {{ log_styler()->color('critical') }};
}
.badge.level-error, .level.level-error, .info-box.level-error {
background-color: {{ log_styler()->color('error') }};
}
.badge.level-warning, .level.level-warning, .info-box.level-warning {
background-color: {{ log_styler()->color('warning') }};
}
.badge.level-notice, .level.level-notice, .info-box.level-notice {
background-color: {{ log_styler()->color('notice') }};
}
.badge.level-info, .level.level-info, .info-box.level-info {
background-color: {{ log_styler()->color('info') }};
}
.badge.level-debug, .level.level-debug, .info-box.level-debug {
background-color: {{ log_styler()->color('debug') }};
}
.badge.level-empty, .level.level-empty {
background-color: {{ log_styler()->color('empty') }};
}
.badge.label-env, .label.label-env {
background-color: #6A1B9A;
}
</style>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
{{-- Navbar --}}
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="{{ route('log-viewer::dashboard') }}" class="navbar-brand">
<i class="fa fa-fw fa-book"></i> LogViewer
</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<ul class="nav navbar-nav">
<li class="{{ Route::is('log-viewer::dashboard') ? 'active' : '' }}">
<a href="{{ route('log-viewer::dashboard') }}">
<i class="fa fa-dashboard"></i> Dashboard
</a>
</li>
<li class="{{ Route::is('log-viewer::logs.list') ? 'active' : '' }}">
<a href="{{ route('log-viewer::logs.list') }}">
<i class="fa fa-archive"></i> Logs
</a>
</li>
</ul>
</div>
</div>
</nav>
{{-- Main container --}}
<main class="container-fluid">
@yield('content')
</main>
{{-- Footer --}}
<footer class="main-footer">
<div class="container">
<p class="text-muted pull-left">
LogViewer - <span class="label label-info">version {{ log_viewer()->version() }}</span>
</p>
<p class="text-muted pull-right">
Created with <i class="fa fa-heart"></i> by ARCANEDEV <sup>&copy;</sup>
</p>
</div>
</footer>
{{-- Scripts --}}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
<script>
Chart.defaults.global.responsive = true;
Chart.defaults.global.scaleFontFamily = "'Source Sans Pro'";
Chart.defaults.global.animationEasing = "easeOutQuart";
</script>
@yield('modals')
@yield('scripts')
</body>
</html>

View File

@@ -0,0 +1,52 @@
@extends('log-viewer::bootstrap-3._master')
@section('content')
<h1 class="page-header">Dashboard</h1>
<div class="row">
<div class="col-md-3">
<canvas id="stats-doughnut-chart" height="300"></canvas>
</div>
<div class="col-md-9">
<section class="box-body">
<div class="row">
@foreach($percents as $level => $item)
<div class="col-md-4">
<div class="info-box level level-{{ $level }} {{ $item['count'] === 0 ? 'level-empty' : '' }}">
<span class="info-box-icon">
{{ log_styler()->icon($level) }}
</span>
<div class="info-box-content">
<span class="info-box-text">{{ $item['name'] }}</span>
<span class="info-box-number">
{{ $item['count'] }} entries - {!! $item['percent'] !!} %
</span>
<div class="progress">
<div class="progress-bar" style="width: {{ $item['percent'] }}%"></div>
</div>
</div>
</div>
</div>
@endforeach
</div>
</section>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function() {
new Chart($('canvas#stats-doughnut-chart'), {
type: 'doughnut',
data: {!! $chartData !!},
options: {
legend: {
position: 'bottom'
}
}
});
});
</script>
@endsection

View File

@@ -0,0 +1,152 @@
@extends('log-viewer::bootstrap-3._master')
<?php /** @var Illuminate\Pagination\LengthAwarePaginator $rows */ ?>
@section('content')
<h1 class="page-header">Logs</h1>
{{ $rows->render() }}
<div class="table-responsive">
<table class="table table-condensed table-hover table-stats">
<thead>
<tr>
@foreach($headers as $key => $header)
<th class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
@if ($key == 'date')
<span class="label label-info">{{ $header }}</span>
@else
<span class="level level-{{ $key }}">
{{ log_styler()->icon($key) }} {{ $header }}
</span>
@endif
</th>
@endforeach
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@forelse($rows as $date => $row)
<tr>
@foreach($row as $key => $value)
<td class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
@if ($key == 'date')
<span class="label label-primary">{{ $value }}</span>
@elseif ($value == 0)
<span class="level level-empty">{{ $value }}</span>
@else
<a href="{{ route('log-viewer::logs.filter', [$date, $key]) }}">
<span class="level level-{{ $key }}">{{ $value }}</span>
</a>
@endif
</td>
@endforeach
<td class="text-right">
<a href="{{ route('log-viewer::logs.show', [$date]) }}" class="btn btn-xs btn-info">
<i class="fa fa-search"></i>
</a>
<a href="{{ route('log-viewer::logs.download', [$date]) }}" class="btn btn-xs btn-success">
<i class="fa fa-download"></i>
</a>
<a href="#delete-log-modal" class="btn btn-xs btn-danger" data-log-date="{{ $date }}">
<i class="fa fa-trash-o"></i>
</a>
</td>
</tr>
@empty
<tr>
<td colspan="11" class="text-center">
<span class="label label-default">{{ trans('log-viewer::general.empty-logs') }}</span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $rows->render() }}
@endsection
@section('modals')
{{-- DELETE MODAL --}}
<div id="delete-log-modal" class="modal fade">
<div class="modal-dialog">
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="date" value="">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">DELETE LOG FILE</h4>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="Loading&hellip;">DELETE FILE</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function () {
var deleteLogModal = $('div#delete-log-modal'),
deleteLogForm = $('form#delete-log-form'),
submitBtn = deleteLogForm.find('button[type=submit]');
$("a[href=#delete-log-modal]").on('click', function(event) {
event.preventDefault();
var date = $(this).data('log-date');
deleteLogForm.find('input[name=date]').val(date);
deleteLogModal.find('.modal-body p').html(
'Are you sure you want to <span class="label label-danger">DELETE</span> this log file <span class="label label-primary">' + date + '</span> ?'
);
deleteLogModal.modal('show');
});
deleteLogForm.on('submit', function(event) {
event.preventDefault();
submitBtn.button('loading');
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
dataType: 'json',
data: $(this).serialize(),
success: function(data) {
submitBtn.button('reset');
if (data.result === 'success') {
deleteLogModal.modal('hide');
location.reload();
}
else {
alert('AJAX ERROR ! Check the console !');
console.error(data);
}
},
error: function(xhr, textStatus, errorThrown) {
alert('AJAX ERROR ! Check the console !');
console.error(errorThrown);
submitBtn.button('reset');
}
});
return false;
});
deleteLogModal.on('hidden.bs.modal', function() {
deleteLogForm.find('input[name=date]').val('');
deleteLogModal.find('.modal-body p').html('');
});
});
</script>
@endsection

View File

@@ -0,0 +1,287 @@
<?php
/**
* @var Arcanedev\LogViewer\Entities\Log $log
* @var Illuminate\Pagination\LengthAwarePaginator $entries
* @var string|null $query
*/
?>
@extends('log-viewer::bootstrap-3._master')
@section('content')
<h1 class="page-header">Log [{{ $log->date }}]</h1>
<div class="row">
<div class="col-md-2">
<div class="panel panel-default">
<div class="panel-heading"><i class="fa fa-fw fa-flag"></i> Levels</div>
<ul class="list-group">
@foreach($log->menu() as $levelKey => $item)
@if ($item['count'] === 0)
<a href="#" class="list-group-item disabled">
<span class="badge">
{{ $item['count'] }}
</span>
{!! $item['icon'] !!} {{ $item['name'] }}
</a>
@else
<a href="{{ $item['url'] }}" class="list-group-item {{ $levelKey }}">
<span class="badge level-{{ $levelKey }}">
{{ $item['count'] }}
</span>
<span class="level level-{{ $levelKey }}">
{!! $item['icon'] !!} {{ $item['name'] }}
</span>
</a>
@endif
@endforeach
</ul>
</div>
</div>
<div class="col-md-10">
{{-- Log Details --}}
<div class="panel panel-default">
<div class="panel-heading">
Log info :
<div class="group-btns pull-right">
<a href="{{ route('log-viewer::logs.download', [$log->date]) }}" class="btn btn-xs btn-success">
<i class="fa fa-download"></i> DOWNLOAD
</a>
<a href="#delete-log-modal" class="btn btn-xs btn-danger" data-toggle="modal">
<i class="fa fa-trash-o"></i> DELETE
</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-condensed">
<thead>
<tr>
<td>File path :</td>
<td colspan="5">{{ $log->getPath() }}</td>
</tr>
</thead>
<tbody>
<tr>
<td>Log entries :</td>
<td>
<span class="label label-primary">{{ $entries->total() }}</span>
</td>
<td>Size :</td>
<td>
<span class="label label-primary">{{ $log->size() }}</span>
</td>
<td>Created at :</td>
<td>
<span class="label label-primary">{{ $log->createdAt() }}</span>
</td>
<td>Updated at :</td>
<td>
<span class="label label-primary">{{ $log->updatedAt() }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="panel-footer">
{{-- Search --}}
<form action="{{ route('log-viewer::logs.search', [$log->date, $level]) }}" method="GET">
<div class=form-group">
<div class="input-group">
<input id="query" name="query" class="form-control" value="{{ $query }}" placeholder="Type here to search">
<span class="input-group-btn">
@unless (is_null($query))
<a href="{{ route('log-viewer::logs.show', [$log->date]) }}" class="btn btn-default">
({{ $entries->count() }} results) <span class="glyphicon glyphicon-remove"></span>
</a>
@endunless
<button id="search-btn" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</div>
</form>
</div>
</div>
{{-- Log Entries --}}
<div class="panel panel-default">
@if ($entries->hasPages())
<div class="panel-heading">
{{ $entries->appends(compact('query'))->render() }}
<span class="label label-info pull-right">
Page {{ $entries->currentPage() }} of {{ $entries->lastPage() }}
</span>
</div>
@endif
<div class="table-responsive">
<table id="entries" class="table table-condensed">
<thead>
<tr>
<th>ENV</th>
<th style="width: 120px;">Level</th>
<th style="width: 65px;">Time</th>
<th>Header</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@forelse($entries as $key => $entry)
<?php /** @var Arcanedev\LogViewer\Entities\LogEntry $entry */ ?>
<tr>
<td>
<span class="label label-env">{{ $entry->env }}</span>
</td>
<td>
<span class="level level-{{ $entry->level }}">{!! $entry->level() !!}</span>
</td>
<td>
<span class="label label-default">
{{ $entry->datetime->format('H:i:s') }}
</span>
</td>
<td>
<p>{{ $entry->header }}</p>
</td>
<td class="text-right">
@if ($entry->hasStack())
<a class="btn btn-xs btn-default" role="button" data-toggle="collapse"
href="#log-stack-{{ $key }}" aria-expanded="false" aria-controls="log-stack-{{ $key }}">
<i class="fa fa-toggle-on"></i> Stack
</a>
@endif
@if ($entry->hasContext())
<a class="btn btn-xs btn-default" role="button" data-toggle="collapse"
href="#log-context-{{ $key }}" aria-expanded="false" aria-controls="log-context-{{ $key }}">
<i class="fa fa-toggle-on"></i> Context
</a>
@endif
</td>
</tr>
@if ($entry->hasStack() || $entry->hasContext())
<tr>
<td colspan="5" class="stack">
@if ($entry->hasStack())
<div class="stack-content collapse" id="log-stack-{{ $key }}">
{!! $entry->stack() !!}
</div>
@endif
@if ($entry->hasContext())
<div class="stack-content collapse" id="log-context-{{ $key }}">
<pre>{{ $entry->context() }}</pre>
</div>
@endif
</td>
</tr>
@endif
@empty
<tr>
<td colspan="5" class="text-center">
<span class="label label-default">{{ trans('log-viewer::general.empty-logs') }}</span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if ($entries->hasPages())
<div class="panel-footer">
{!! $entries->appends(compact('query'))->render() !!}
<span class="label label-info pull-right">
Page {{ $entries->currentPage() }} of {{ $entries->lastPage() }}
</span>
</div>
@endif
</div>
</div>
</div>
@endsection
@section('modals')
{{-- DELETE MODAL --}}
<div id="delete-log-modal" class="modal fade">
<div class="modal-dialog">
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="date" value="{{ $log->date }}">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">DELETE LOG FILE</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to <span class="label label-danger">DELETE</span> this log file <span class="label label-primary">{{ $log->date }}</span> ?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-default pull-left" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="Loading&hellip;">DELETE FILE</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function () {
var deleteLogModal = $('div#delete-log-modal'),
deleteLogForm = $('form#delete-log-form'),
submitBtn = deleteLogForm.find('button[type=submit]');
deleteLogForm.on('submit', function(event) {
event.preventDefault();
submitBtn.button('loading');
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
dataType: 'json',
data: $(this).serialize(),
success: function(data) {
submitBtn.button('reset');
if (data.result === 'success') {
deleteLogModal.modal('hide');
location.replace("{{ route('log-viewer::logs.list') }}");
}
else {
alert('OOPS ! This is a lack of coffee exception !')
}
},
error: function(xhr, textStatus, errorThrown) {
alert('AJAX ERROR ! Check the console !');
console.error(errorThrown);
submitBtn.button('reset');
}
});
return false;
});
@unless (empty(log_styler()->toHighlight()))
@php
$htmlHighlight = version_compare(PHP_VERSION, '7.4.0') >= 0
? join('|', log_styler()->toHighlight())
: join(log_styler()->toHighlight(), '|');
@endphp
$('.stack-content').each(function() {
var $this = $(this);
var html = $this.html().trim()
.replace(/({!! $htmlHighlight !!})/gm, '<strong>$1</strong>');
$this.html(html);
});
@endunless
});
</script>
@endsection

View File

@@ -0,0 +1,266 @@
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="LogViewer">
<meta name="author" content="ARCANEDEV">
<title>LogViewer - Created by ARCANEDEV</title>
{{-- Styles --}}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link href='https://fonts.googleapis.com/css?family=Montserrat:400,700|Source+Sans+Pro:400,600' rel='stylesheet' type='text/css'>
<style>
html {
position: relative;
min-height: 100%;
}
body {
font-size: .875rem;
margin-bottom: 60px;
}
.main-footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
line-height: 60px;
background-color: #E8EAF6;
}
.main-footer p {
margin-bottom: 0;
}
.main-footer .fa.fa-heart {
color: #C62828;
}
.page-header {
border-bottom: 1px solid #8a8a8a;
}
/*
* Navbar
*/
.navbar-brand {
padding: .75rem 1rem;
font-size: 1rem;
}
.navbar-nav .nav-link {
padding-right: .5rem;
padding-left: .5rem;
}
/*
* Boxes
*/
.box {
display: block;
padding: 0;
min-height: 70px;
background: #fff;
width: 100%;
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
border-radius: .25rem;
}
.box > .box-icon > i,
.box .box-content .box-text,
.box .box-content .box-number {
color: #FFF;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}
.box > .box-icon {
border-radius: 2px 0 0 2px;
display: block;
float: left;
height: 70px; width: 70px;
text-align: center;
font-size: 40px;
line-height: 70px;
background: rgba(0,0,0,0.2);
}
.box .box-content {
padding: 5px 10px;
margin-left: 70px;
}
.box .box-content .box-text {
display: block;
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
}
.box .box-content .box-number {
display: block;
}
.box .box-content .progress {
background: rgba(0,0,0,0.2);
margin: 5px -10px 5px -10px;
}
.box .box-content .progress .progress-bar {
background-color: #FFF;
}
/*
* Log Menu
*/
.log-menu .list-group-item.disabled {
cursor: not-allowed;
}
.log-menu .list-group-item.disabled .level-name {
color: #D1D1D1;
}
/*
* Log Entry
*/
.stack-content {
color: #AE0E0E;
font-family: consolas, Menlo, Courier, monospace;
white-space: pre-line;
font-size: .8rem;
}
/*
* Colors: Badge & Infobox
*/
.badge.badge-env,
.badge.badge-level-all,
.badge.badge-level-emergency,
.badge.badge-level-alert,
.badge.badge-level-critical,
.badge.badge-level-error,
.badge.badge-level-warning,
.badge.badge-level-notice,
.badge.badge-level-info,
.badge.badge-level-debug,
.badge.empty {
color: #FFF;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}
.badge.badge-level-all,
.box.level-all {
background-color: {{ log_styler()->color('all') }};
}
.badge.badge-level-emergency,
.box.level-emergency {
background-color: {{ log_styler()->color('emergency') }};
}
.badge.badge-level-alert,
.box.level-alert {
background-color: {{ log_styler()->color('alert') }};
}
.badge.badge-level-critical,
.box.level-critical {
background-color: {{ log_styler()->color('critical') }};
}
.badge.badge-level-error,
.box.level-error {
background-color: {{ log_styler()->color('error') }};
}
.badge.badge-level-warning,
.box.level-warning {
background-color: {{ log_styler()->color('warning') }};
}
.badge.badge-level-notice,
.box.level-notice {
background-color: {{ log_styler()->color('notice') }};
}
.badge.badge-level-info,
.box.level-info {
background-color: {{ log_styler()->color('info') }};
}
.badge.badge-level-debug,
.box.level-debug {
background-color: {{ log_styler()->color('debug') }};
}
.badge.empty,
.box.empty {
background-color: {{ log_styler()->color('empty') }};
}
.badge.badge-env {
background-color: #6A1B9A;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark sticky-top bg-dark p-0">
<a href="{{ route('log-viewer::dashboard') }}" class="navbar-brand mr-0">
<i class="fa fa-fw fa-book"></i> LogViewer
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav mr-auto">
<li class="nav-item {{ Route::is('log-viewer::dashboard') ? 'active' : '' }}">
<a href="{{ route('log-viewer::dashboard') }}" class="nav-link">
<i class="fa fa-dashboard"></i> Dashboard
</a>
</li>
<li class="nav-item {{ Route::is('log-viewer::logs.list') ? 'active' : '' }}">
<a href="{{ route('log-viewer::logs.list') }}" class="nav-link">
<i class="fa fa-archive"></i> Logs
</a>
</li>
</ul>
</div>
</nav>
<div class="container-fluid">
<main role="main" class="pt-3">
@yield('content')
</main>
</div>
{{-- Footer --}}
<footer class="main-footer">
<div class="container-fluid">
<p class="text-muted pull-left">
LogViewer - <span class="badge badge-info">version {{ log_viewer()->version() }}</span>
</p>
<p class="text-muted pull-right">
Created with <i class="fa fa-heart"></i> by ARCANEDEV <sup>&copy;</sup>
</p>
</div>
</footer>
{{-- Scripts --}}
<script src="https://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
@yield('modals')
@yield('scripts')
</body>
</html>

View File

@@ -0,0 +1,53 @@
@extends('log-viewer::bootstrap-4._master')
@section('content')
<div class="page-header mb-4">
<h1>Dashboard</h1>
</div>
<div class="row">
<div class="col-md-6 col-lg-3">
<canvas id="stats-doughnut-chart" height="300" class="mb-3"></canvas>
</div>
<div class="col-md-6 col-lg-9">
<div class="row">
@foreach($percents as $level => $item)
<div class="col-sm-6 col-md-12 col-lg-4 mb-3">
<div class="box level-{{ $level }} {{ $item['count'] === 0 ? 'empty' : '' }}">
<div class="box-icon">
{!! log_styler()->icon($level) !!}
</div>
<div class="box-content">
<span class="box-text">{{ $item['name'] }}</span>
<span class="box-number">
{{ $item['count'] }} entries - {!! $item['percent'] !!} %
</span>
<div class="progress" style="height: 3px;">
<div class="progress-bar" style="width: {{ $item['percent'] }}%"></div>
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function() {
new Chart(document.getElementById("stats-doughnut-chart"), {
type: 'doughnut',
data: {!! $chartData !!},
options: {
legend: {
position: 'bottom'
}
}
});
});
</script>
@endsection

View File

@@ -0,0 +1,152 @@
@extends('log-viewer::bootstrap-4._master')
<?php /** @var Illuminate\Pagination\LengthAwarePaginator $rows */ ?>
@section('content')
<div class="page-header mb-4">
<h1>Logs</h1>
</div>
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
@foreach($headers as $key => $header)
<th scope="col" class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
@if ($key == 'date')
<span class="badge badge-info">{{ $header }}</span>
@else
<span class="badge badge-level-{{ $key }}">
{{ log_styler()->icon($key) }} {{ $header }}
</span>
@endif
</th>
@endforeach
<th scope="col" class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@forelse($rows as $date => $row)
<tr>
@foreach($row as $key => $value)
<td class="{{ $key == 'date' ? 'text-left' : 'text-center' }}">
@if ($key == 'date')
<span class="badge badge-primary">{{ $value }}</span>
@elseif ($value == 0)
<span class="badge empty">{{ $value }}</span>
@else
<a href="{{ route('log-viewer::logs.filter', [$date, $key]) }}">
<span class="badge badge-level-{{ $key }}">{{ $value }}</span>
</a>
@endif
</td>
@endforeach
<td class="text-right">
<a href="{{ route('log-viewer::logs.show', [$date]) }}" class="btn btn-sm btn-info">
<i class="fa fa-search"></i>
</a>
<a href="{{ route('log-viewer::logs.download', [$date]) }}" class="btn btn-sm btn-success">
<i class="fa fa-download"></i>
</a>
<a href="#delete-log-modal" class="btn btn-sm btn-danger" data-log-date="{{ $date }}">
<i class="fa fa-trash-o"></i>
</a>
</td>
</tr>
@empty
<tr>
<td colspan="11" class="text-center">
<span class="badge badge-secondary">{{ trans('log-viewer::general.empty-logs') }}</span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $rows->render() }}
@endsection
@section('modals')
{{-- DELETE MODAL --}}
<div id="delete-log-modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="date" value="">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">DELETE LOG FILE</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary mr-auto" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="Loading&hellip;">DELETE FILE</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function () {
var deleteLogModal = $('div#delete-log-modal'),
deleteLogForm = $('form#delete-log-form'),
submitBtn = deleteLogForm.find('button[type=submit]');
$("a[href='#delete-log-modal']").on('click', function(event) {
event.preventDefault();
var date = $(this).data('log-date');
deleteLogForm.find('input[name=date]').val(date);
deleteLogModal.find('.modal-body p').html(
'Are you sure you want to <span class="badge badge-danger">DELETE</span> this log file <span class="badge badge-primary">' + date + '</span> ?'
);
deleteLogModal.modal('show');
});
deleteLogForm.on('submit', function(event) {
event.preventDefault();
submitBtn.button('loading');
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
dataType: 'json',
data: $(this).serialize(),
success: function(data) {
submitBtn.button('reset');
if (data.result === 'success') {
deleteLogModal.modal('hide');
location.reload();
}
else {
alert('AJAX ERROR ! Check the console !');
console.error(data);
}
},
error: function(xhr, textStatus, errorThrown) {
alert('AJAX ERROR ! Check the console !');
console.error(errorThrown);
submitBtn.button('reset');
}
});
return false;
});
deleteLogModal.on('hidden.bs.modal', function() {
deleteLogForm.find('input[name=date]').val('');
deleteLogModal.find('.modal-body p').html('');
});
});
</script>
@endsection

View File

@@ -0,0 +1,274 @@
<?php
/**
* @var Arcanedev\LogViewer\Entities\Log $log
* @var Illuminate\Pagination\LengthAwarePaginator $entries
* @var string|null $query
*/
?>
@extends('log-viewer::bootstrap-4._master')
@section('content')
<div class="page-header mb-4">
<h1>Log [{{ $log->date }}]</h1>
</div>
<div class="row">
<div class="col-lg-2">
{{-- Log Menu --}}
<div class="card mb-4">
<div class="card-header"><i class="fa fa-fw fa-flag"></i> Levels</div>
<div class="list-group list-group-flush log-menu">
@foreach($log->menu() as $levelKey => $item)
@if ($item['count'] === 0)
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center disabled">
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
<span class="badge empty">{{ $item['count'] }}</span>
</a>
@else
<a href="{{ $item['url'] }}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center level-{{ $levelKey }}{{ $level === $levelKey ? ' active' : ''}}">
<span class="level-name">{!! $item['icon'] !!} {{ $item['name'] }}</span>
<span class="badge badge-level-{{ $levelKey }}">{{ $item['count'] }}</span>
</a>
@endif
@endforeach
</div>
</div>
</div>
<div class="col-lg-10">
{{-- Log Details --}}
<div class="card mb-4">
<div class="card-header">
Log info :
<div class="group-btns pull-right">
<a href="{{ route('log-viewer::logs.download', [$log->date]) }}" class="btn btn-sm btn-success">
<i class="fa fa-download"></i> DOWNLOAD
</a>
<a href="#delete-log-modal" class="btn btn-sm btn-danger" data-toggle="modal">
<i class="fa fa-trash-o"></i> DELETE
</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-condensed mb-0">
<tbody>
<tr>
<td>File path :</td>
<td colspan="7">{{ $log->getPath() }}</td>
</tr>
<tr>
<td>Log entries :</td>
<td>
<span class="badge badge-primary">{{ $entries->total() }}</span>
</td>
<td>Size :</td>
<td>
<span class="badge badge-primary">{{ $log->size() }}</span>
</td>
<td>Created at :</td>
<td>
<span class="badge badge-primary">{{ $log->createdAt() }}</span>
</td>
<td>Updated at :</td>
<td>
<span class="badge badge-primary">{{ $log->updatedAt() }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
{{-- Search --}}
<form action="{{ route('log-viewer::logs.search', [$log->date, $level]) }}" method="GET">
<div class=form-group">
<div class="input-group">
<input id="query" name="query" class="form-control" value="{{ $query }}" placeholder="Type here to search">
<div class="input-group-append">
@unless (is_null($query))
<a href="{{ route('log-viewer::logs.show', [$log->date]) }}" class="btn btn-secondary">
({{ $entries->count() }} results) <i class="fa fa-fw fa-times"></i>
</a>
@endunless
<button id="search-btn" class="btn btn-primary">
<span class="fa fa-fw fa-search"></span>
</button>
</div>
</div>
</div>
</form>
</div>
</div>
{{-- Log Entries --}}
<div class="card mb-4">
@if ($entries->hasPages())
<div class="card-header">
<span class="badge badge-info float-right">
Page {{ $entries->currentPage() }} of {{ $entries->lastPage() }}
</span>
</div>
@endif
<div class="table-responsive">
<table id="entries" class="table mb-0">
<thead>
<tr>
<th>ENV</th>
<th style="width: 120px;">Level</th>
<th style="width: 65px;">Time</th>
<th>Header</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
@forelse($entries as $key => $entry)
<?php /** @var Arcanedev\LogViewer\Entities\LogEntry $entry */ ?>
<tr>
<td>
<span class="badge badge-env">{{ $entry->env }}</span>
</td>
<td>
<span class="badge badge-level-{{ $entry->level }}">
{!! $entry->level() !!}
</span>
</td>
<td>
<span class="badge badge-secondary">
{{ $entry->datetime->format('H:i:s') }}
</span>
</td>
<td>
{{ $entry->header }}
</td>
<td class="text-right">
@if ($entry->hasStack())
<a class="btn btn-sm btn-light" role="button" data-toggle="collapse"
href="#log-stack-{{ $key }}" aria-expanded="false" aria-controls="log-stack-{{ $key }}">
<i class="fa fa-toggle-on"></i> Stack
</a>
@endif
@if ($entry->hasContext())
<a class="btn btn-sm btn-light" role="button" data-toggle="collapse"
href="#log-context-{{ $key }}" aria-expanded="false" aria-controls="log-context-{{ $key }}">
<i class="fa fa-toggle-on"></i> Context
</a>
@endif
</td>
</tr>
@if ($entry->hasStack() || $entry->hasContext())
<tr>
<td colspan="5" class="stack py-0">
@if ($entry->hasStack())
<div class="stack-content collapse" id="log-stack-{{ $key }}">
{!! $entry->stack() !!}
</div>
@endif
@if ($entry->hasContext())
<div class="stack-content collapse" id="log-context-{{ $key }}">
<pre>{{ $entry->context() }}</pre>
</div>
@endif
</td>
</tr>
@endif
@empty
<tr>
<td colspan="5" class="text-center">
<span class="badge badge-secondary">{{ trans('log-viewer::general.empty-logs') }}</span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{!! $entries->appends(compact('query'))->render() !!}
</div>
</div>
@endsection
@section('modals')
{{-- DELETE MODAL --}}
<div id="delete-log-modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<form id="delete-log-form" action="{{ route('log-viewer::logs.delete') }}" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="date" value="{{ $log->date }}">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">DELETE LOG FILE</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Are you sure you want to <span class="badge badge-danger">DELETE</span> this log file <span class="badge badge-primary">{{ $log->date }}</span> ?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary mr-auto" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-danger" data-loading-text="Loading&hellip;">DELETE FILE</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(function () {
var deleteLogModal = $('div#delete-log-modal'),
deleteLogForm = $('form#delete-log-form'),
submitBtn = deleteLogForm.find('button[type=submit]');
deleteLogForm.on('submit', function(event) {
event.preventDefault();
submitBtn.button('loading');
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
dataType: 'json',
data: $(this).serialize(),
success: function(data) {
submitBtn.button('reset');
if (data.result === 'success') {
deleteLogModal.modal('hide');
location.replace("{{ route('log-viewer::logs.list') }}");
}
else {
alert('OOPS ! This is a lack of coffee exception !')
}
},
error: function(xhr, textStatus, errorThrown) {
alert('AJAX ERROR ! Check the console !');
console.error(errorThrown);
submitBtn.button('reset');
}
});
return false;
});
@unless (empty(log_styler()->toHighlight()))
@php
$htmlHighlight = version_compare(PHP_VERSION, '7.4.0') >= 0
? join('|', log_styler()->toHighlight())
: join(log_styler()->toHighlight(), '|');
@endphp
$('.stack-content').each(function() {
var $this = $(this);
var html = $this.html().trim()
.replace(/({!! $htmlHighlight !!})/gm, '<strong>$1</strong>');
$this.html(html);
});
@endunless
});
</script>
@endsection

View File

@@ -0,0 +1,19 @@
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color ?? 'primary' }}" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>

View File

@@ -0,0 +1,7 @@
<tr>
<td class="header">
<a href="{{ $url }}">
{{ $slot }}
</a>
</td>
</tr>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{{ $header ?? '' }}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}
{{ $subcopy ?? '' }}
</td>
</tr>
</table>
</td>
</tr>
{{ $footer ?? '' }}
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot
@endcomponent

View File

@@ -0,0 +1,14 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,7 @@
<table class="promotion" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@@ -0,0 +1,13 @@
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-green" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>

View File

@@ -0,0 +1,292 @@
/* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
box-sizing: border-box;
}
body {
background-color: #f8fafc;
color: #74787e;
height: 100%;
hyphens: auto;
line-height: 1.4;
margin: 0;
-moz-hyphens: auto;
-ms-word-break: break-all;
width: 100% !important;
-webkit-hyphens: auto;
-webkit-text-size-adjust: none;
word-break: break-all;
word-break: break-word;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869d4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #3d4852;
font-size: 19px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
color: #3d4852;
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
color: #3d4852;
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
color: #3d4852;
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
background-color: #f8fafc;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.content {
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #bbbfc3;
font-size: 19px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
/* Body */
.body {
background-color: #ffffff;
border-bottom: 1px solid #edeff2;
border-top: 1px solid #edeff2;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.inner-body {
background-color: #ffffff;
margin: 0 auto;
padding: 0;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #edeff2;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 12px;
}
/* Footer */
.footer {
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
.footer p {
color: #aeaeae;
font-size: 12px;
text-align: center;
}
/* Tables */
.table table {
margin: 30px auto;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.table th {
border-bottom: 1px solid #edeff2;
padding-bottom: 8px;
margin: 0;
}
.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
padding: 10px 0;
margin: 0;
}
.content-cell {
padding: 35px;
}
/* Buttons */
.action {
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.button {
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
color: #fff;
display: inline-block;
text-decoration: none;
-webkit-text-size-adjust: none;
}
.button-blue,
.button-primary {
background-color: #3490dc;
border-top: 10px solid #3490dc;
border-right: 18px solid #3490dc;
border-bottom: 10px solid #3490dc;
border-left: 18px solid #3490dc;
}
.button-green,
.button-success {
background-color: #38c172;
border-top: 10px solid #38c172;
border-right: 18px solid #38c172;
border-bottom: 10px solid #38c172;
border-left: 18px solid #38c172;
}
.button-red,
.button-error {
background-color: #e3342f;
border-top: 10px solid #e3342f;
border-right: 18px solid #e3342f;
border-bottom: 10px solid #e3342f;
border-left: 18px solid #e3342f;
}
/* Panels */
.panel {
margin: 0 0 21px;
}
.panel-content {
background-color: #f1f5f8;
padding: 16px;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Promotions */
.promotion {
background-color: #ffffff;
border: 2px dashed #9ba2ab;
margin: 0;
margin-bottom: 25px;
margin-top: 25px;
padding: 24px;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.promotion h1 {
text-align: center;
}
.promotion p {
font-size: 15px;
text-align: center;
}

View File

@@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

View File

@@ -0,0 +1,9 @@
{!! strip_tags($header) !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer) !!}

View File

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot
@endcomponent

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1 @@
{{ $slot }}

View File

@@ -0,0 +1,64 @@
@component('mail::message')
{{-- Greeting --}}
@if (! empty($greeting))
# {{ $greeting }}
@else
@if ($level === 'error')
# @lang('Whoops!')
@else
# @lang('Hello!')
@endif
@endif
{{-- Intro Lines --}}
@foreach ($introLines as $line)
{{ $line }}
@endforeach
{{-- Action Button --}}
@isset($actionText)
<?php
switch ($level) {
case 'success':
case 'error':
$color = $level;
break;
default:
$color = 'primary';
}
?>
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
{{ $actionText }}
@endcomponent
@endisset
{{-- Outro Lines --}}
@foreach ($outroLines as $line)
{{ $line }}
@endforeach
{{-- Salutation --}}
@if (! empty($salutation))
{{ $salutation }}
@else
@lang('Regards'),<br>
{{ config('app.name') }}
@endif
{{-- Subcopy --}}
@isset($actionText)
@slot('subcopy')
@lang(
"If youre having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
'into your web browser: [:displayableActionUrl](:actionURL)',
[
'actionText' => $actionText,
'actionURL' => $actionUrl,
'displayableActionUrl' => $displayableActionUrl,
]
)
@endslot
@endisset
@endcomponent

View File

@@ -0,0 +1,46 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
<span class="page-link" aria-hidden="true">&lsaquo;</span>
</li>
@else
<li class="page-item">
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</a>
</li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
@else
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item">
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">&rsaquo;</a>
</li>
@else
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span class="page-link" aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@@ -0,0 +1,46 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
<span aria-hidden="true">&lsaquo;</span>
</li>
@else
<li>
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</a>
</li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="disabled" aria-disabled="true"><span>{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="active" aria-current="page"><span>{{ $page }}</span></li>
@else
<li><a href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li>
<a href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">&rsaquo;</a>
</li>
@else
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@@ -0,0 +1,36 @@
@if ($paginator->hasPages())
<div class="ui pagination menu" role="navigation">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
@else
<a class="icon item" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<a class="icon item disabled" aria-disabled="true">{{ $element }}</a>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<a class="item active" href="{{ $url }}" aria-current="page">{{ $page }}</a>
@else
<a class="item" href="{{ $url }}">{{ $page }}</a>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<a class="icon item" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
@else
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
@endif
</div>
@endif

View File

@@ -0,0 +1,27 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled" aria-disabled="true">
<span class="page-link">@lang('pagination.previous')</span>
</li>
@else
<li class="page-item">
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
</li>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item">
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
</li>
@else
<li class="page-item disabled" aria-disabled="true">
<span class="page-link">@lang('pagination.next')</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@@ -0,0 +1,19 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled" aria-disabled="true"><span>@lang('pagination.previous')</span></li>
@else
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
@else
<li class="disabled" aria-disabled="true"><span>@lang('pagination.next')</span></li>
@endif
</ul>
</nav>
@endif

View File

@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.content {
text-align: center;
}
.title {
font-size: 84px;
}
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 13px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
.m-b-md {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">Register</a>
@endif
@endauth
</div>
@endif
<div class="content">
<div class="title m-b-md">
Laravel
</div>
<div class="links">
<a href="https://laravel.com/docs">Docs</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://blog.laravel.com">Blog</a>
<a href="https://nova.laravel.com">Nova</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://vapor.laravel.com">Vapor</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</body>
</html>