feat: implement site visit in controller and add visit table
This commit is contained in:
@@ -6,15 +6,31 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\Booking;
|
||||
use App\Models\Flight;
|
||||
use App\Models\User;
|
||||
use App\Models\Visit;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
|
||||
$startDate = Carbon::now()->subDays(9)->startOfDay();
|
||||
$endDate = Carbon::now()->endOfDay();
|
||||
|
||||
|
||||
$flightsCount = Flight::count();
|
||||
$usersCount = User::count();
|
||||
$bookingsCount = Booking::count();
|
||||
$indexPageCont = Visit::count();
|
||||
|
||||
return view('admin.dashboard', compact('flightsCount', 'usersCount', 'bookingsCount'));
|
||||
$visits = Visit::whereBetween('created_at', [$startDate, $endDate])
|
||||
->selectRaw('DATE(created_at) as visit_date, COUNT(*) as total')
|
||||
->groupBy('visit_date')
|
||||
->orderBy('visit_date')
|
||||
->get();
|
||||
|
||||
|
||||
return view('admin.dashboard', compact('flightsCount', 'usersCount',
|
||||
'bookingsCount', 'indexPageCont' , 'visits'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,6 @@ class FlightController extends Controller
|
||||
|
||||
'status' => [
|
||||
'required',
|
||||
'in:0,1',
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,13 +5,24 @@ namespace App\Http\Controllers;
|
||||
use App\Models\Airport;
|
||||
use App\Models\Flight;
|
||||
use App\Models\Tour;
|
||||
use App\Models\Visit;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class IndexController extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (! $request->session()->has('visited')) {
|
||||
Visit::create([
|
||||
'ip_address' => $request->ip(),
|
||||
'url' => $request->fullUrl(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
]);
|
||||
|
||||
$request->session()->put('visited', true);
|
||||
}
|
||||
|
||||
$flights = Flight::query()
|
||||
->where('status', \App\Enums\FlightStatus::ENABLE)
|
||||
->with([
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\FlightStatus;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -52,7 +53,7 @@ class Flight extends Model
|
||||
'price' => 'float',
|
||||
'capacity' => 'int',
|
||||
'views' => 'int',
|
||||
'status' => 'int'
|
||||
'status' => FlightStatus::class
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\TourStatus;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -15,7 +16,8 @@ class Tour extends Model
|
||||
'duration' => 'int',
|
||||
'views' => 'int',
|
||||
'price' => 'int',
|
||||
'status' => 'int',
|
||||
'status' => TourStatus::class,
|
||||
'departure_time'=> 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
@@ -30,6 +32,7 @@ class Tour extends Model
|
||||
'duration',
|
||||
'views',
|
||||
'price',
|
||||
'departure_time',
|
||||
'status',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by Reliese Model.
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class Visit
|
||||
*
|
||||
* @property int $id
|
||||
* @property string|null $ip_address
|
||||
* @property string|null $url
|
||||
* @property string|null $user_agent
|
||||
* @property Carbon|null $created_at
|
||||
* @property Carbon|null $updated_at
|
||||
*
|
||||
* @package App\Models
|
||||
*/
|
||||
class Visit extends Model
|
||||
{
|
||||
protected $table = 'visits';
|
||||
public static $snakeAttributes = false;
|
||||
|
||||
protected $fillable = [
|
||||
'ip_address',
|
||||
'url',
|
||||
'user_agent'
|
||||
];
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
```blade
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('breadcrumb')
|
||||
@@ -23,8 +22,47 @@
|
||||
|
||||
<div class="row">
|
||||
|
||||
{{-- تعداد پروازها --}}
|
||||
<div class="col-xl-4 col-lg-4 col-md-6 col-sm-12">
|
||||
<div class="col-xl-3 col-lg-3 col-md-6 col-sm-12">
|
||||
|
||||
<div class="card custom-card overflow-hidden">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="d-flex gap-3">
|
||||
|
||||
<div class="avatar avatar-md bg-info svg-white">
|
||||
|
||||
<i class="ri-eye-line fs-24"></i>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex-fill">
|
||||
|
||||
<div class="fw-medium fs-13 mb-1 text-dark">
|
||||
بازدید کل سایت
|
||||
</div>
|
||||
|
||||
<div class="fs-22 fw-semibold mb-1 text-info">
|
||||
{{ $indexPageCont }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center fs-11">
|
||||
<span class="text-default op-6">
|
||||
کل بازدیدها
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-xl-3 col-lg-3 col-md-6 col-sm-12">
|
||||
|
||||
<div class="card custom-card overflow-hidden">
|
||||
|
||||
@@ -64,9 +102,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{-- تعداد کاربران --}}
|
||||
<div class="col-xl-4 col-lg-4 col-md-6 col-sm-12">
|
||||
<div class="col-xl-3 col-lg-3 col-md-6 col-sm-12">
|
||||
|
||||
<div class="card custom-card overflow-hidden">
|
||||
|
||||
@@ -106,9 +142,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{-- تعداد رزروها --}}
|
||||
<div class="col-xl-4 col-lg-4 col-md-6 col-sm-12">
|
||||
<div class="col-xl-3 col-lg-3 col-md-6 col-sm-12">
|
||||
|
||||
<div class="card custom-card overflow-hidden">
|
||||
|
||||
@@ -150,9 +184,57 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<div class="col-xl-12">
|
||||
|
||||
<div class="card custom-card">
|
||||
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
نمودار بازدید سایت (۱۰ روز اخیر)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<canvas id="visitsChart" height="90"></canvas>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
new Chart(document.getElementById('visitsChart'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: @json($visits->map(fn($v) => verta($v->visit_date)->format('m/d'))),
|
||||
datasets: [{
|
||||
label: 'تعداد بازدید',
|
||||
data: @json($visits->pluck('total')),
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
||||
borderRadius: 6,
|
||||
maxBarThickness: 40
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: { beginAtZero: true, ticks: { precision: 0 } }
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: false }
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class="text-success">{{ $message }}</span>
|
||||
@enderror
|
||||
|
||||
@error('errors')
|
||||
@error('error')
|
||||
<span class="text-danger">{{ $message }}</span>
|
||||
@enderror
|
||||
<div class="row">
|
||||
@@ -236,11 +236,11 @@
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@if(\App\Enums\FlightStatus::ENABLE)
|
||||
@if($flight->status == \App\Enums\FlightStatus::ENABLE)
|
||||
<span class="badge bg-primary-transparent">
|
||||
فعال
|
||||
</span>
|
||||
@elseif(\App\Enums\FlightStatus::DISABLE)
|
||||
@elseif($flight->status == \App\Enums\FlightStatus::DISABLE )
|
||||
<span class="badge bg-warning-transparent">
|
||||
غیرفعال
|
||||
</span>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
data-menu-styles="light"
|
||||
dir="rtl"
|
||||
loader="enable"
|
||||
data-vertical-style="overlay">
|
||||
data-vertical-style="default">
|
||||
<head>
|
||||
<!-- Meta Data -->
|
||||
<meta charset="UTF-8">
|
||||
|
||||
@@ -1,45 +1,140 @@
|
||||
<style>
|
||||
.flight-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flight-col {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flight-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flight-img {
|
||||
height: 200px;
|
||||
min-height: 200px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flight-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.flight-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flight-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flight-title-info {
|
||||
min-height: 55px;
|
||||
}
|
||||
|
||||
.flight-title-info h4 {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.flight-date {
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.flight-bottom {
|
||||
margin-top: auto;
|
||||
}
|
||||
</style>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="flight-item">
|
||||
|
||||
<div class="flight-img">
|
||||
@if(getFileUrl($flight->file_id))
|
||||
|
||||
@if($flight?->file_id)
|
||||
<img
|
||||
src="{{ getFileUrl($flight->file_id) }}"
|
||||
alt="{{ $flight->title }}"
|
||||
style="width: 100%; height: 200px; object-fit: cover;"
|
||||
>
|
||||
@else
|
||||
<img src="{{ asset('assets/img/flight/01.jpg') }}" alt
|
||||
style="width: 100%; height: 200px; object-fit: cover;">
|
||||
<img
|
||||
src="{{ asset('assets/img/flight/01.jpg') }}"
|
||||
alt="{{ $flight->title }}"
|
||||
>
|
||||
@endif
|
||||
|
||||
<a href="{{ route('account.wishlist.toggle', [
|
||||
<a
|
||||
href="{{ route('account.wishlist.toggle', [
|
||||
'type' => 'flight',
|
||||
'id' => $flight->id
|
||||
]) }}" class="add-wishlist">
|
||||
]) }}"
|
||||
class="add-wishlist"
|
||||
>
|
||||
<i class="far fa-heart"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flight-content">
|
||||
|
||||
<div class="flight-title">
|
||||
|
||||
<div class="flight-title-info">
|
||||
<img src="{{ asset('assets/img/flight/airline-1.png') }}" alt>
|
||||
<h4><a href="#">{{ $flight->departureAirport->city->name }}<i
|
||||
class="far fa-exchange"></i> {{ $flight->arrivalAirport->city->name }} </a></h4>
|
||||
|
||||
<img
|
||||
src="{{ asset('assets/img/flight/airline-1.png') }}"
|
||||
alt
|
||||
>
|
||||
|
||||
<h4>
|
||||
<a href="#">
|
||||
{{ $flight->departureAirport?->city?->name }}
|
||||
|
||||
<i class="far fa-exchange"></i>
|
||||
|
||||
{{ $flight->arrivalAirport?->city?->name }}
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
</div>
|
||||
<p class="flight-date"><i
|
||||
class="far fa-calendar-days"></i>{{ $flight->departure_time->toJalali()->format('Y/m/d H:i') }}
|
||||
|
||||
<p class="flight-date">
|
||||
<i class="far fa-calendar-days"></i>
|
||||
{{ $flight->departure_time->toJalali()->format('Y/m/d H:i') }}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flight-bottom">
|
||||
|
||||
<div class="flight-price">
|
||||
از <span> {{ number_format($flight->price) }} تومان </span>
|
||||
از
|
||||
<span>
|
||||
{{ number_format($flight->price) }} تومان
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flight-text-btn">
|
||||
<a href="{{ route('flights.show', $flight->id) }}">نمایش جزئیات <i
|
||||
class="fas fa-arrow-left"></i></a>
|
||||
<a href="{{ route('flights.show', $flight->id) }}">
|
||||
نمایش جزئیات
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,69 @@
|
||||
<style>
|
||||
.tour-item {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tour-img {
|
||||
height: 260px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tour-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tour-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tour-title {
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.tour-content > p {
|
||||
min-height: 48px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tour-duration {
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.tour-bottom {
|
||||
margin-top: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<div class="tour-item">
|
||||
|
||||
<div class="tour-img">
|
||||
@if(getFileUrl($tour?->file?->id))
|
||||
<img
|
||||
src="{{ $tour->file
|
||||
? asset('storage/' . $tour->file->path)
|
||||
: asset('assets/img/tour/01.jpg') }}"
|
||||
src="{{ getFileUrl($tour?->file?->id) }}"
|
||||
alt="{{ $tour->title }}"
|
||||
style=""
|
||||
>
|
||||
@else
|
||||
<img src="{{ asset('assets/img/tour/02.jpg') }}" alt
|
||||
style="">
|
||||
@endif
|
||||
|
||||
<a href="{{ route('account.wishlist.toggle', [
|
||||
'type' => 'tour',
|
||||
'id' => $tour->id
|
||||
]) }}" class="add-wishlist">
|
||||
]) }}" class="add-wishlist">
|
||||
<i class="far fa-heart"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user