Include graph.js and build some dashboard tiles.
This commit is contained in:
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\View\Composers\MonthGraph;
|
||||||
use App\View\Composers\OptionLogo;
|
use App\View\Composers\OptionLogo;
|
||||||
use App\View\Composers\TaxDropdown;
|
use App\View\Composers\TaxDropdown;
|
||||||
|
use App\View\Composers\YearGraph;
|
||||||
use Illuminate\Support\Facades\URL;
|
use Illuminate\Support\Facades\URL;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\Number;
|
use Illuminate\Support\Number;
|
||||||
@@ -36,6 +38,8 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
View::composer('components.tax-dropdown', TaxDropdown::class);
|
View::composer('components.tax-dropdown', TaxDropdown::class);
|
||||||
View::composer('components.company-logo', OptionLogo::class);
|
View::composer('components.company-logo', OptionLogo::class);
|
||||||
|
View::composer('components.graph-year', YearGraph::class);
|
||||||
|
View::composer('components.graph-month', MonthGraph::class);
|
||||||
Number::useLocale(config('app.locale'));
|
Number::useLocale(config('app.locale'));
|
||||||
Number::useCurrency(config('app.currency'));
|
Number::useCurrency(config('app.currency'));
|
||||||
|
|
||||||
|
|||||||
41
app/View/Composers/MonthGraph.php
Normal file
41
app/View/Composers/MonthGraph.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Composers;
|
||||||
|
|
||||||
|
use App\Models\Incoming;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class MonthGraph
|
||||||
|
{
|
||||||
|
public function compose(View $view): void
|
||||||
|
{
|
||||||
|
$monthly_invoices = Invoice::whereYear('created_at', '=', Carbon::now()->year)->get()
|
||||||
|
->groupBy(function ($invoice) {
|
||||||
|
return $invoice->created_at->format('n');
|
||||||
|
})
|
||||||
|
->map(function ($month) {
|
||||||
|
return $month->sum('sum');
|
||||||
|
});
|
||||||
|
|
||||||
|
$monthly_incoming = Incoming::whereYear('issue_date', '=', Carbon::now()->year)->get()
|
||||||
|
->groupBy(function ($incoming) {
|
||||||
|
return Carbon::parse($incoming->issue_date)->format('n');
|
||||||
|
})
|
||||||
|
->map(function ($month) {
|
||||||
|
return $month->sum('net');
|
||||||
|
});
|
||||||
|
|
||||||
|
$diff_invoices = $monthly_invoices->diffKeys($monthly_incoming);
|
||||||
|
foreach ($diff_invoices as $year => $invoices) {
|
||||||
|
$monthly_incoming[$year] = 0;
|
||||||
|
}
|
||||||
|
$diff_incoming = $monthly_incoming->diffKeys($monthly_invoices);
|
||||||
|
foreach ($diff_incoming as $year => $invoices) {
|
||||||
|
$monthly_invoices[$year] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->with(['monthly_invoices' => $monthly_invoices, 'monthly_incoming' => $monthly_incoming]);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/View/Composers/YearGraph.php
Normal file
41
app/View/Composers/YearGraph.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Composers;
|
||||||
|
|
||||||
|
use App\Models\Incoming;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class YearGraph
|
||||||
|
{
|
||||||
|
public function compose(View $view): void
|
||||||
|
{
|
||||||
|
$yearly_invoices = Invoice::all()
|
||||||
|
->groupBy(function ($invoice) {
|
||||||
|
return $invoice->created_at->format('Y');
|
||||||
|
})
|
||||||
|
->map(function ($year) {
|
||||||
|
return $year->sum('sum');
|
||||||
|
});
|
||||||
|
|
||||||
|
$yearly_incoming = Incoming::all()
|
||||||
|
->groupBy(function ($incoming) {
|
||||||
|
return Carbon::parse($incoming->pay_date)->format('Y');
|
||||||
|
})
|
||||||
|
->map(function ($year) {
|
||||||
|
return $year->sum('net');
|
||||||
|
});
|
||||||
|
|
||||||
|
$diff_invoices = $yearly_invoices->diffKeys($yearly_incoming);
|
||||||
|
foreach ($diff_invoices as $year => $invoices) {
|
||||||
|
$yearly_incoming[$year] = 0;
|
||||||
|
}
|
||||||
|
$diff_incoming = $yearly_incoming->diffKeys($yearly_invoices);
|
||||||
|
foreach ($diff_incoming as $year => $invoices) {
|
||||||
|
$yearly_invoices[$year] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->with(['yearly_invoices' => $yearly_invoices, 'yearly_incoming' => $yearly_incoming]);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
package-lock.json
generated
23
package-lock.json
generated
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "project",
|
"name": "html",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alpinejs/sort": "^3.14.7",
|
"@alpinejs/sort": "^3.14.7",
|
||||||
"alpine": "^0.2.1"
|
"alpine": "^0.2.1",
|
||||||
|
"chart.js": "^4.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
@@ -501,6 +502,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -1134,6 +1141,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz",
|
||||||
|
"integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alpinejs/sort": "^3.14.7",
|
"@alpinejs/sort": "^3.14.7",
|
||||||
"alpine": "^0.2.1"
|
"alpine": "^0.2.1",
|
||||||
|
"chart.js": "^4.4.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import './bootstrap';
|
|||||||
|
|
||||||
import Alpine from 'alpinejs';
|
import Alpine from 'alpinejs';
|
||||||
import sort from '@alpinejs/sort';
|
import sort from '@alpinejs/sort';
|
||||||
|
import Chart from "chart.js/auto";
|
||||||
|
|
||||||
Alpine.plugin(sort);
|
Alpine.plugin(sort);
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
|
window.Chart = Chart;
|
||||||
|
|
||||||
Alpine.start();
|
Alpine.start();
|
||||||
|
|||||||
27
resources/views/components/graph-month.blade.php
Normal file
27
resources/views/components/graph-month.blade.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="w-full">
|
||||||
|
<canvas id="monthly"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function drawMonth() {
|
||||||
|
let monthly_invoices = {!! $monthly_invoices !!};
|
||||||
|
let monthly_incoming = {!! $monthly_incoming !!};
|
||||||
|
|
||||||
|
new Chart(document.getElementById('monthly'),
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: '{{ __('invoice.Invoices') }}',
|
||||||
|
data: monthly_invoices
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{{ __('incoming.Incoming') }}',
|
||||||
|
data: monthly_incoming
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
27
resources/views/components/graph-year.blade.php
Normal file
27
resources/views/components/graph-year.blade.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="w-full">
|
||||||
|
<canvas id="yearly"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function drawYear() {
|
||||||
|
let yearly_invoices = {!! $yearly_invoices !!};
|
||||||
|
let yearly_incoming = {!! $yearly_incoming !!};
|
||||||
|
|
||||||
|
new Chart(document.getElementById('yearly'),
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: '{{ __('invoice.Invoices') }}',
|
||||||
|
data: yearly_invoices
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '{{ __('incoming.Incoming') }}',
|
||||||
|
data: yearly_incoming
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -16,73 +16,97 @@
|
|||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 grid grid-cols-3 gap-4">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 grid grid-cols-3 gap-4">
|
||||||
|
|
||||||
@if($sent_invoices->count() != 0)
|
@if($sent_invoices->count() != 0)
|
||||||
<!-- Invoices not paid -->
|
<!-- Invoices not paid -->
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
||||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Invoices not paid') }}</h2>
|
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Invoices not paid') }}</h2>
|
||||||
@foreach($sent_invoices as $invoice)
|
@foreach($sent_invoices as $invoice)
|
||||||
<a href="{{ route('payment.create') }}"
|
<a href="{{ route('payment.create') }}"
|
||||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||||
<div class="w-1/4">{{ $invoice->number }}</div>
|
<div class="w-1/4">{{ $invoice->number }}</div>
|
||||||
<div class="w-1/4">{{ $invoice->customer->name }}</div>
|
<div class="w-1/4">{{ $invoice->customer->name }}</div>
|
||||||
<div class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum) }}</div>
|
<div
|
||||||
<div class="w-1/4 text-right">{{ $invoice->created }}</div>
|
class="w-1/4 text-right">{{ \Illuminate\Support\Number::currency($invoice->sum) }}</div>
|
||||||
</a>
|
<div class="w-1/4 text-right">{{ $invoice->created }}</div>
|
||||||
@endforeach
|
</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($created_invoices->count() != 0)
|
@if($created_invoices->count() != 0)
|
||||||
<!-- Invoices not sent -->
|
<!-- Invoices not sent -->
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Invoices not sent') }}</h2>
|
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Invoices not sent') }}</h2>
|
||||||
@foreach($created_invoices as $invoice)
|
@foreach($created_invoices as $invoice)
|
||||||
<a href="{{ route('invoice.edit', $invoice->id) }}"
|
<a href="{{ route('invoice.edit', $invoice->id) }}"
|
||||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||||
<div class="w-1/2">{{ $invoice->number }}</div>
|
<div class="w-1/2">{{ $invoice->number }}</div>
|
||||||
<div class="w-1/2">{{ $invoice->customer->name }}</div>
|
<div class="w-1/2">{{ $invoice->customer->name }}</div>
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($customers->count() != 0)
|
@if($customers->count() != 0)
|
||||||
<!-- Customers without address -->
|
<!-- Customers without address -->
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Customers without address') }}</h2>
|
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Customers without address') }}</h2>
|
||||||
@foreach($customers as $customer)
|
@foreach($customers as $customer)
|
||||||
<a href="{{ route('customer.edit', $customer->id) }}"
|
<a href="{{ route('customer.edit', $customer->id) }}"
|
||||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||||
<div class="w-1/2">{{ $customer->name }}</div>
|
<div class="w-1/2">{{ $customer->name }}</div>
|
||||||
<div class="w-1/2">{{ $customer->email }}</div>
|
<div class="w-1/2">{{ $customer->email }}</div>
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if($unpaid_incoming->count() != 0)
|
@if($unpaid_incoming->count() != 0)
|
||||||
<!-- Incoming invoices, that are not paid -->
|
<!-- Incoming invoices, that are not paid -->
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
||||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Incoming not paid') }}</h2>
|
<h2 class="mb-4 text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('dashboard.Incoming not paid') }}</h2>
|
||||||
@foreach($unpaid_incoming as $incoming)
|
@foreach($unpaid_incoming as $incoming)
|
||||||
<a href="{{ route('incoming.edit', $incoming->id) }}"
|
<a href="{{ route('incoming.edit', $incoming->id) }}"
|
||||||
class="flex max-w even:bg-gray-100 odd:bg-white">
|
class="flex max-w even:bg-gray-100 odd:bg-white">
|
||||||
<div class="w-1/6">{{ $incoming->invoice_number }}</div>
|
<div class="w-1/6">{{ $incoming->invoice_number }}</div>
|
||||||
<div class="w-1/2">{{ $incoming->supplier->name }}</div>
|
<div class="w-1/2">{{ $incoming->supplier->name }}</div>
|
||||||
<div class="w-1/6 text-right">{{ \Illuminate\Support\Number::currency($incoming->gross) }}</div>
|
<div
|
||||||
<div class="w-1/6 text-right">{{ $incoming->due }}</div>
|
class="w-1/6 text-right">{{ \Illuminate\Support\Number::currency($incoming->gross) }}</div>
|
||||||
</a>
|
<div class="w-1/6 text-right">{{ $incoming->due }}</div>
|
||||||
@endforeach
|
</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
||||||
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
|
<x-graph-year/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg col-span-2">
|
||||||
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||||
|
<x-graph-month/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</x-app-layout>
|
</x-app-layout>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = (event) => {
|
||||||
|
if (typeof(drawMonth) != 'undefined') {
|
||||||
|
drawMonth();
|
||||||
|
}
|
||||||
|
if (typeof(drawYear) != 'undefined') {
|
||||||
|
drawYear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user