git add .

git commit  "add tours pages, components and show views"
This commit is contained in:
2026-08-17 11:36:19 +03:30
parent e99c340f1e
commit 970786ae94
15 changed files with 595 additions and 249 deletions
+20 -4
View File
@@ -10,10 +10,26 @@ class HomeController extends Controller
{ {
public function index() public function index()
{ {
$newestFlight = Flight::latest()->take(10)->get(); $newestFlight = Flight::
$flights=Flight::latest()->paginate(5); where('departure_date', '>=', now())
$newestTour = Tour::latest()->take(10)->get(); ->orderBy('departure_date', 'asc')
->take(3)
->get();
$newestTour = Tour::latest()
->take(10)
->get();
return view('home.index', compact('newestFlight','flights', 'newestTour')); $flights = Flight::latest()
->paginate(5);
$tours = Tour::latest()
->paginate(5);
return view('home.index', compact(
'newestFlight',
'newestTour',
'flights',
'tours'
));
} }
} }
+10 -5
View File
@@ -6,19 +6,24 @@ use App\Models\Tour;
class TourController extends Controller class TourController extends Controller
{ {
// صفحه لیست تورها: اسلایدر آخرین‌ها + گرید صفحه‌بندی‌شده
public function index() public function index()
{ {
$newestTour = Tour::latest()->take(10)->get(); $newestTour = Tour::with('travelType')
$tours = Tour::latest()->paginate(9); ->latest()
->take(10)
->get();
$tours = Tour::with('travelType')
->latest()
->paginate(5);
return view('tours.index', compact('newestTour', 'tours')); return view('tours.index', compact('newestTour', 'tours'));
} }
// جزئیات تور + شمارنده بازدید
public function show(Tour $tour) public function show(Tour $tour)
{ {
$tour->increment('views'); $tour->load('travelType');
return view('tours.show', compact('tour')); return view('tours.show', compact('tour'));
} }
+2 -2
View File
@@ -14,7 +14,7 @@ use Illuminate\Database\Eloquent\Model;
* Class City * Class City
* *
* @property int $id * @property int $id
* @property int $provinces * @property int $province_id
* @property string $name * @property string $name
* @property Carbon $created_at * @property Carbon $created_at
* @property Carbon $updated_at * @property Carbon $updated_at
@@ -33,7 +33,7 @@ class City extends Model
]; ];
protected $fillable = [ protected $fillable = [
'provinces', 'province_id',
'name' 'name'
]; ];
+1 -1
View File
@@ -34,7 +34,7 @@ class Province extends Model
} }
public function cities() public function cities()
{ {
return $this->hasMany(City::class); return $this->belongsTo(City::class);
} }
} }
+7 -3
View File
@@ -12,7 +12,7 @@ use Illuminate\Database\Eloquent\Model;
/** /**
* Class Tour * Class Tour
* *
* @property int $id * @property int $id
* @property string $title * @property string $title
* @property string $summary * @property string $summary
@@ -24,7 +24,7 @@ use Illuminate\Database\Eloquent\Model;
* @property int $status * @property int $status
* @property Carbon $created_at * @property Carbon $created_at
* @property Carbon $updated_at * @property Carbon $updated_at
* *
* @property Collection|Comment[] $comments * @property Collection|Comment[] $comments
* *
* @package App\Models * @package App\Models
@@ -56,4 +56,8 @@ class Tour extends Model
{ {
return $this->hasMany(Comment::class); return $this->hasMany(Comment::class);
} }
} public function travelType()
{
return $this->belongsTo(TravelType::class);
}
}
+6 -2
View File
@@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\Model;
/** /**
* Class TravelType * Class TravelType
* *
* @property int $id * @property int $id
* @property string $name * @property string $name
* @property int $status * @property int $status
@@ -33,4 +33,8 @@ class TravelType extends Model
'name', 'name',
'status' 'status'
]; ];
} public function tours()
{
return $this->hasMany(Tour::class, 'travel_type_id');
}
}
+1
View File
@@ -7,6 +7,7 @@
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"hekmatinasser/verta": "^9.0",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1", "laravel/tinker": "^2.10.1",
"sadegh19b/laravel-persian-validation": "^2.1" "sadegh19b/laravel-persian-validation": "^2.1"
Generated
+184 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "32f38eafa73f2600bea6b75e538fc42e", "content-hash": "d2f9c73ad216c4d120251d41cec0c1ee",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -1058,6 +1058,189 @@
], ],
"time": "2026-07-17T13:53:03+00:00" "time": "2026-07-17T13:53:03+00:00"
}, },
{
"name": "hekmatinasser/jalali",
"version": "v8.2.4",
"source": {
"type": "git",
"url": "https://github.com/hekmatinasser/jalali.git",
"reference": "423c848ec32fb36e9ab9610d82f98efa2de0f79d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hekmatinasser/jalali/zipball/423c848ec32fb36e9ab9610d82f98efa2de0f79d",
"reference": "423c848ec32fb36e9ab9610d82f98efa2de0f79d",
"shasum": ""
},
"require": {
"hekmatinasser/notowo": "^1.0",
"php": "^8.0"
},
"require-dev": {
"orchestra/testbench": "^4.18|^5.20|^6.24|^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Hekmatinasser\\Jalali\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nasser Hekmati",
"email": "hekmati.nasser@gmail.com"
}
],
"description": "This Package helps developers to work with Jalali Datetime class for PHP",
"keywords": [
"Jalali",
"date",
"datetime",
"php",
"shamsi"
],
"support": {
"issues": "https://github.com/hekmatinasser/jalali/issues",
"source": "https://github.com/hekmatinasser/jalali/tree/x8.2.3"
},
"funding": [
{
"url": "https://github.com/hekmatinasser",
"type": "github"
}
],
"time": "2024-04-09T12:43:59+00:00"
},
{
"name": "hekmatinasser/notowo",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/hekmatinasser/notowo.git",
"reference": "c51ad5dcedac966dd93ce558ac7e6a84e0020d6d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hekmatinasser/notowo/zipball/c51ad5dcedac966dd93ce558ac7e6a84e0020d6d",
"reference": "c51ad5dcedac966dd93ce558ac7e6a84e0020d6d",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Hekmatinasser\\Notowo\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nasser Hekmati",
"email": "hekmati.nasser@gmail.com"
}
],
"description": "This Package helps developers change number to word for all language on Laravel freamwork PHP",
"keywords": [
"Notowo",
"laravel",
"n2w",
"number",
"word"
],
"support": {
"issues": "https://github.com/hekmatinasser/notowo/issues",
"source": "https://github.com/hekmatinasser/notowo/tree/v1.0.1"
},
"time": "2017-09-22T13:41:16+00:00"
},
{
"name": "hekmatinasser/verta",
"version": "v9.0",
"source": {
"type": "git",
"url": "https://github.com/hekmatinasser/verta.git",
"reference": "f3cff537f07ef302b1ea24f59e9c1d5e3a1c36cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hekmatinasser/verta/zipball/f3cff537f07ef302b1ea24f59e9c1d5e3a1c36cb",
"reference": "f3cff537f07ef302b1ea24f59e9c1d5e3a1c36cb",
"shasum": ""
},
"require": {
"hekmatinasser/jalali": "^8.2.3",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/validation": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
"php": "^8.0"
},
"require-dev": {
"orchestra/testbench": "^6.23|^7.0|^8.0|^11.0"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Verta": "Hekmatinasser\\Verta\\Verta"
},
"providers": [
"Hekmatinasser\\Verta\\Laravel\\VertaServiceProvider"
]
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Hekmatinasser\\Verta\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nasser Hekmati",
"email": "hekmati.nasser@gmail.com"
}
],
"description": "This Package helps developers to work with Jalali Datetime class for Laravel Framework PHP",
"keywords": [
"Jalali",
"Verta",
"date",
"datetime",
"laravel",
"shamsi"
],
"support": {
"issues": "https://github.com/hekmatinasser/verta/issues",
"source": "https://github.com/hekmatinasser/verta/tree/v9.0"
},
"funding": [
{
"url": "https://github.com/hekmatinasser",
"type": "github"
}
],
"time": "2026-05-08T06:26:09+00:00"
},
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v12.65.0", "version": "v12.65.0",
-157
View File
@@ -1,157 +0,0 @@
<style>
.flight-detail-page {
padding: 70px 0;
background: #f5f7fb;
direction: rtl;
}
.flight-detail-card {
background: #fff;
border-radius: 25px;
overflow: hidden;
display: flex;
gap: 0;
box-shadow: 0 15px 50px rgba(0,0,0,.12);
}
.flight-detail-image {
width: 40%;
min-height: 450px;
}
.flight-detail-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.flight-detail-content {
width: 60%;
padding: 45px;
}
.flight-detail-content h1 {
font-size: 34px;
font-weight: 800;
color: #102a43;
margin: 0;
}
.flight-badge-show {
background: #1976d2;
color: white;
padding: 8px 22px;
border-radius: 50px;
font-size: 14px;
}
.route-box {
margin: 35px 0;
padding: 25px;
border-radius: 20px;
background: #f8fafc;
display: flex;
align-items: center;
justify-content: space-around;
}
.route-box div {
text-align: center;
}
.route-box small {
display: block;
color: #78909c;
margin-bottom: 8px;
}
.route-box strong {
font-size: 22px;
color: #102a43;
}
.route-line {
font-size: 30px;
color: #f4b400;
}
.flight-info-grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 20px;
margin-bottom: 35px;
}
.flight-info-grid div {
background: #f8fafc;
padding: 18px;
border-radius: 15px;
}
.flight-info-grid span {
display:block;
color:#78909c;
font-size:13px;
margin-bottom:8px;
}
.flight-info-grid b {
font-size:18px;
color:#102a43;
}
.buy-flight-btn {
display:inline-block;
width:100%;
text-align:center;
background:#1976d2;
color:white;
padding:15px;
border-radius:15px;
text-decoration:none;
font-weight:700;
transition:.3s;
}
.buy-flight-btn:hover {
background:#125ca8;
color:white;
}
@media(max-width:768px){
.flight-detail-card{
flex-direction:column;
}
.flight-detail-image,
.flight-detail-content{
width:100%;
}
.flight-detail-image{
height:280px;
}
.flight-info-grid{
grid-template-columns:1fr;
}
}
</style>
+45 -22
View File
@@ -1,43 +1,66 @@
<div class="swiper-slide"> <div class="col-md-4">
<div class="card border-0 shadow-sm rounded-4 overflow-hidden h-100"> <div class="card border-0 shadow-sm rounded-4 overflow-hidden h-100">
<div class="position-relative" style="height: 170px;">
@if($tour->image)
<img src="{{ asset('storage/'.$tour->image) }}"
class="w-100 h-100 object-fit-cover"
alt="{{ $tour->title }}">
@else
<div class="w-100 h-100 bg-light"></div>
@endif
<span class="position-absolute top-0 end-0 m-2 badge rounded-pill" style="background: var(--teal);"> <div class="flight-image">
<img src="{{ asset($tour->image) }}"
alt="{{ $tour->title }}">
<span class="flight-badge">
تور تور
</span> </span>
</div> </div>
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<h5 class="card-title fw-bold fs-6 mb-2">{{ $tour->title }}</h5>
@if($tour->summary)
<p class="text-secondary small mb-3">{{ \Illuminate\Support\Str::limit($tour->summary, 70) }}</p>
@endif
<div class="small text-secondary mb-3"> <h5 class="fw-bold">
<i class="bi bi-calendar-event"></i> {{ $tour->title }}
تاریخ حرکت: {{ optional($tour->departure_date)->format('Y/m/d') }} </h5>
<div class="route">
<span>
{{ $tour->travelType?->name ?? 'تور گردشگری' }}
</span>
<span class="line"></span>
<span>
{{ \Carbon\Carbon::parse($tour->departure_date)->format('Y/m/d') }}
</span>
</div> </div>
<div class="mt-auto d-flex justify-content-between align-items-center"> <div class="mt-auto d-flex justify-content-between align-items-center">
<span class="fw-bold" style="color: var(--blue);">
<strong>
{{ number_format($tour->price) }} تومان {{ number_format($tour->price) }} تومان
</span> </strong>
<a href="{{ route('tours.show', $tour) }}"
class="btn btn-sm rounded-pill px-3 text-white"
style="background: var(--blue);"> <a href="{{ route('tours.show',$tour) }}"
class="btn btn-primary rounded-pill">
مشاهده مشاهده
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
+160 -1
View File
@@ -1,3 +1,162 @@
<style>
.flight-detail-page {
padding: 70px 0;
background: #f5f7fb;
direction: rtl;
}
.flight-detail-card {
background: #fff;
border-radius: 25px;
overflow: hidden;
display: flex;
gap: 0;
box-shadow: 0 15px 50px rgba(0,0,0,.12);
}
.flight-detail-image {
width: 40%;
min-height: 450px;
}
.flight-detail-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.flight-detail-content {
width: 60%;
padding: 45px;
}
.flight-detail-content h1 {
font-size: 34px;
font-weight: 800;
color: #102a43;
margin: 0;
}
.flight-badge-show {
background: #1976d2;
color: white;
padding: 8px 22px;
border-radius: 50px;
font-size: 14px;
}
.route-box {
margin: 35px 0;
padding: 25px;
border-radius: 20px;
background: #f8fafc;
display: flex;
align-items: center;
justify-content: space-around;
}
.route-box div {
text-align: center;
}
.route-box small {
display: block;
color: #78909c;
margin-bottom: 8px;
}
.route-box strong {
font-size: 22px;
color: #102a43;
}
.route-line {
font-size: 30px;
color: #f4b400;
}
.flight-info-grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 20px;
margin-bottom: 35px;
}
.flight-info-grid div {
background: #f8fafc;
padding: 18px;
border-radius: 15px;
}
.flight-info-grid span {
display:block;
color:#78909c;
font-size:13px;
margin-bottom:8px;
}
.flight-info-grid b {
font-size:18px;
color:#102a43;
}
.buy-flight-btn {
display:inline-block;
width:100%;
text-align:center;
background:#1976d2;
color:white;
padding:15px;
border-radius:15px;
text-decoration:none;
font-weight:700;
transition:.3s;
}
.buy-flight-btn:hover {
background:#125ca8;
color:white;
}
@media(max-width:768px){
.flight-detail-card{
flex-direction:column;
}
.flight-detail-image,
.flight-detail-content{
width:100%;
}
.flight-detail-image{
height:280px;
}
.flight-info-grid{
grid-template-columns:1fr;
}
}
</style>
<section class="flight-detail-page"> <section class="flight-detail-page">
<div class="container"> <div class="container">
@@ -54,7 +213,7 @@
<div> <div>
<span>تاریخ حرکت</span> <span>تاریخ حرکت</span>
<b>{{ $flight->departure_date }}</b> <b>{{ $flight->departure_date->toJalali()->format('Y/m/d') }}</b>
</div> </div>
<div> <div>
+21 -15
View File
@@ -8,29 +8,35 @@
<div class="swiper-wrapper py-3"> <div class="swiper-wrapper py-3">
@forelse($newestFlight as $flight) @forelse($newestFlight as $flight)
@include('components.flight') @include('components.flight')
@empty @empty
<p class="text-secondary">هنوز پروازی ثبت نشده است.</p> <p class="text-secondary">هنوز پروازی ثبت نشده است.</p>
@endforelse @endforelse
@foreach($newestTour as $tour)
@include('components.tour', ['tour' => $tour])
@endforeach
</div> </div>
</div> </div>
</section> </section>
<section class="container my-5"> <section class="container my-5">
<h3 class="fw-bold mb-4">همه پروازها و تورها</h3> <h3 class="fw-bold mb-4">همه پروازها و تورها</h3>
<div class="row g-4"> <div class="row g-4">
@forelse($flights as $flight)
<div class="col-md-4">
@include('components.flight')
</div>
@empty
<p class="text-secondary">چیزی برای نمایش وجود ندارد.</p>
@endforelse
</div>
<div class="mt-4"> @foreach($flights as $flight)
{{ $flights->links() }} @include('components.flight', ['flight' => $flight])
</div> @endforeach
</section>
@foreach($tours as $tour)
@include('components.tour', ['tour' => $tour])
@endforeach
</div>
<div class="mt-4">
{{ $flights->links() }}
</div>
</section>
@endsection @endsection
+1 -2
View File
@@ -11,8 +11,7 @@
<link rel="stylesheet" href="{{asset('assets/css/weather-icons.css')}}"/> <link rel="stylesheet" href="{{asset('assets/css/weather-icons.css')}}"/>
<link rel="stylesheet" href="{{asset('assets/css/bootstrap.css')}}"/> <link rel="stylesheet" href="{{asset('assets/css/bootstrap.css')}}"/>
<link rel="stylesheet" href="{{ asset('assets/css/styles.css?v=2') }}"> <link rel="stylesheet" href="{{ asset('assets/css/styles.css?v=2') }}">
<link rel="stylesheet" href="{{ asset('assets/css/styles2.css?v=2') }}"> </head>
</head>
<body> <body>
@includeUnless(isset($withoutHeader),'layouts.header') @includeUnless(isset($withoutHeader),'layouts.header')
+24 -31
View File
@@ -1,64 +1,51 @@
@foreach($tours as $tour) @foreach($tours as $tour)
<!-- TOUR ITEM -->
<div class="swiper-slide tour-card group"> <div class="swiper-slide tour-card group">
<!-- tour image --> <a href="{{ route('tours.show', $tour->id) }}" class="tour-card_image-link">
<a href="#">
<img <img
class="tour-card_img" class="tour-card_img"
src="{{ $tour->image ?? asset('assets/img/placeholder.jpg') }}" src="{{ asset($tour->image) }}" alt="{{ $tour->title }}" >
alt="{{ $tour->title }}"
>
</a> </a>
<!-- tour content -->
<div class="tour-card_content"> <div class="tour-card_content">
<!-- title --> <a href="{{ route('tours.show', $tour->id) }}"
<a href="#" class="tour-card_title"> class="tour-card_title">
{{ $tour->title }} {{ $tour->title }}
</a> </a>
<!-- destination -->
<div class="tour-card_destination"> <div class="tour-card_destination">
<span>📍</span>
<span> {{ Str::limit($tour->summary, 60) }}
📍
</span>
{{ $tour->destination }}
</div> </div>
<!-- details -->
<div class="tour-card_details"> <div class="tour-card_details">
<div> <div class="tour-detail-item">
<span>مدت تور</span> <span>تاریخ حرکت</span>
<strong> <strong>
{{ $tour->duration }} روز {{ \Carbon\Carbon::parse($tour->departure_date)->format('Y/m/d') }}
</strong> </strong>
</div> </div>
<div>
<span>ظرفیت</span>
<div class="tour-detail-item">
<span>نوع سفر</span>
<strong> <strong>
{{ $tour->capacity }} نفر {{ $tour->travelType->name ?? '---' }}
</strong> </strong>
</div> </div>
</div> </div>
<!-- price -->
<div class="tour-card_price"> <div class="tour-card_price">
<span> <span>شروع قیمت از</span>
شروع قیمت از
</span>
<strong> <strong>
{{ number_format($tour->price) }} {{ number_format($tour->price) }}
@@ -67,6 +54,12 @@
</div> </div>
<a href="{{ route('tours.show', $tour->id) }}"
class="tour-card_btn">
مشاهده تور
</a>
</div> </div>
</div> </div>
+113 -3
View File
@@ -1,3 +1,113 @@
<div> @extends('layouts.app')
<!-- Happiness is not something readymade. It comes from your own actions. - Dalai Lama -->
</div> @section('content')
<section class="container mt-5">
<div class="card border-0 shadow rounded-4 overflow-hidden">
<div class="position-relative">
<img src="{{ asset($tour->image) }}"
alt="{{ $tour->title }}">
<span class="badge bg-primary position-absolute top-0 end-0 m-4">
تور
</span>
</div>
<div class="card-body p-4">
<h1 class="fw-bold mb-4">
{{ $tour->title }}
</h1>
<p class="text-muted">
{{ $tour->summary }}
</p>
<hr>
<div class="row g-4">
<div class="col-md-4">
<span class="text-secondary">
نوع سفر
</span>
<h5>
{{ $tour->travelType?->name ?? '-' }}
</h5>
</div>
<div class="col-md-4">
<span class="text-secondary">
تاریخ حرکت
</span>
<h5>
{{ \Carbon\Carbon::parse($tour->departure_date)->format('Y/m/d') }}
</h5>
</div>
<div class="col-md-4">
<span class="text-secondary">
قیمت
</span>
<h5>
{{ number_format($tour->price) }}
تومان
</h5>
</div>
</div>
<hr>
<h4 class="fw-bold">
توضیحات تور
</h4>
<p class="mt-3">
{{ $tour->description }}
</p>
</div>
</div>
</section>
@endsection