git add .

git commit "Add admin management search sort and permission structure preparation"
This commit is contained in:
2026-08-18 12:56:32 +03:30
parent d7bd95f71a
commit 78a3b1d63d
24 changed files with 1167 additions and 75 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ class AdminGenerator extends Command
'mobile' => '09119533286', 'mobile' => '09119533286',
'password' => Hash::make('hatame14'), 'password' => Hash::make('hatame14'),
'image' => '', 'image' => '',
'status' => 'active', 'status' => 1,
]); ]);
$this->info('hatame created successfully.'); $this->info('hatame created successfully.');
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Enums;
enum AdminStatus : int
{
case Inactive=0;
case Active=1;
case Blocked=2;
}
@@ -0,0 +1,74 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminUpdateRequest;
use App\Http\Requests\AdminStoreRequest;
use App\Models\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AdminController extends Controller
{
public function index()
{
$admins=Admin::query()
->applySearch()
->applySort()
->latest()
->paginate(2);
return view('admin.admins.index',compact('admins')) ;
}
public function create()
{
return view('admin.admins.create');
}
public function store(AdminStoreRequest $request)
{
$adminData=$request->validated();
$adminData['password']=Hash::make($adminData['password']);
Admin::create($adminData);
return redirect()
->route('admin.admins.index')
->with('success','مدیر جدید با موفقیت اضافه شد.');
}
public function edit(Admin $admin)
{
return view('admin.admins.edit',compact('admin'));
}
public function update( AdminUpdateRequest $request,Admin $admin)
{
$adminData=$request->validated();
if ($request->filled('password')) {
$adminData['password'] = Hash::make($adminData['password']);
} else {
unset($adminData['password']);
}
$admin->update($adminData);
return redirect()
->route('admin.admins.index')
->with('success','اطلاعات مدیر با موفقیت ویرایش شد.');
}
public function destroy(Admin $admin)
{
$admin->delete();
return redirect()->route('admin.admins.index')
->with('مدیر با موفقیت حذف شد. ');
}
}
@@ -2,17 +2,37 @@
namespace App\Http\Controllers\Admin\Auth; namespace App\Http\Controllers\Admin\Auth;
use App\Enums\AdminStatus;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use App\Http\Requests\Admin\Auth\LoginPostRequest;
use App\Models\Admin;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller class AuthController extends Controller
{ {
public function login() public function login()
{ {
return view('admin.auth.login'); $rawLayout = true;
$title='ورود به حساب کاربری';
return view('admin.auth.login',compact('rawLayout','title'));
} }
public function loginPost() public function loginPost(LoginPostRequest $request)
{ {
$loginData=$request->validated();
$admins=Admin::query()
->where('username',$request->input('username'))
->where('status',AdminStatus::Active)
->first();
if (!$admins){
return back()->withErrors([
'general' => 'اطلاعات وارد شده نامعتبر است'
]);
}
Auth::guard('admin')->login($admins);
return redirect()->route('admin.dashboard.index');
} }
public function logout() public function logout()
@@ -3,8 +3,25 @@
namespace App\Http\Controllers\Admin\Auth; namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\Flight;
use App\Models\Tour;
use App\Models\User;
class DashboardController extends Controller class DashboardController extends Controller
{ {
// public function index()
{
$adminsCount = Admin::count();
$usersCount = User::count();
$toursCount = Tour::count();
$flightsCount = Flight::count();
return view('admin.dashboard',compact('adminsCount',
'usersCount','toursCount','flightsCount'
));
}
} }
+3 -4
View File
@@ -10,16 +10,15 @@ class CommentController extends Controller
{ {
public function index() public function index()
{ {
$withoutHero=true;
$comments=Comment::query() $comments=Comment::query()
->where('user_id',auth()->id()) ->where('user_id',auth()->id())
->with('tour') ->with('tour')
->latest() ->latest()
->paginate(4); ->paginate(4);
return view('account.users.comments.index', [ return view('account.users.comments.index',compact('withoutHero','comments'));
'comments' => $comments,
'withoutHero' => true
]);
} }
public function store(CommentStoreRequest $request) public function store(CommentStoreRequest $request)
+3 -4
View File
@@ -9,15 +9,14 @@ class FavoriteController extends Controller
{ {
public function index() public function index()
{ {
$withoutHero=true;
$favorites = Favorite::with('favoritable') $favorites = Favorite::with('favoritable')
->where('user_id', auth()->id()) ->where('user_id', auth()->id())
->latest() ->latest()
->get(); ->get();
return view('account.users.favorite.index', [ return view('account.users.favorite.index',compact('withoutHero','favorites'));
'favorites' => $favorites,
'withoutHero' => true
]);
} public function store(Request $request) } public function store(Request $request)
{ {
Favorite::firstOrCreate([ Favorite::firstOrCreate([
-14
View File
@@ -1,14 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
//
public function index()
{
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class AdminUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name'=>[
'required',
'string',
'max:50'
],
'last_name'=>[
'required',
'string',
'max:50'
],
'username'=>[
'required',
'string',
],
'email'=>[
'required',
'email',
],
'mobile'=>[
'required',
],
'password'=>[
'nullable',
'min:8'
],
'status'=>[
'required',
],
];
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Http\Requests\Admin\Auth;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class LoginPostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'username'=>[
'required',
'string',
'min:3',
'max:50'
],
'password'=>[
'required',
'string',
'min:8',
'max:255'
]
];
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class AdminStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name'=>[
'required',
'string',
'max:50'
],
'last_name'=>[
'required',
'string',
'max:50'
],
'username'=>[
'required',
'string',
'unique:admins,username'
],
'email'=>[
'required',
'email',
'unique:admins,email'
],
'mobile'=>[
'required',
'unique:admins,mobile'
],
'password'=>[
'required',
'min:8'
],
'status'=>[
'required',
],
];
}
}
+38 -3
View File
@@ -7,8 +7,9 @@
namespace App\Models; namespace App\Models;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\User; use Illuminate\Foundation\Auth\User;
@@ -54,8 +55,42 @@ class Admin extends User
'image', 'image',
'status' 'status'
]; ];
#[Scope]
protected function applySearch(Builder $query): void
{
$request = request();
public function roles() if ($request->filled('keyword')) {
$keyword = $request->input('keyword');
$query->whereAny([
'first_name',
'last_name',
], 'LIKE', "%$keyword%");
}
}
#[Scope]
protected function applySort(Builder $query): void
{
$sort = request('sort');
match ($sort) {
'first_name_asc' =>
$query->orderBy('first_name', 'asc'),
'first_name_desc' =>
$query->orderBy('first_name', 'desc'),
'newest' =>
$query->orderBy('created_at', 'desc'),
default =>
$query->orderBy('created_at', 'desc'),
};
} public function roles()
{ {
return $this->belongsToMany(Role::class, 'admin_roles'); return $this->belongsToMany(Role::class, 'admin_roles');
} }
+16 -2
View File
@@ -18,8 +18,22 @@ return Application::configure(basePath: dirname(__DIR__))
health: '/up', health: '/up',
) )
->withMiddleware(function (Middleware $middleware) { ->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo(fn (Request $request) => route('auth.login.index')); $middleware->redirectGuestsTo(function (Request $request) {
$middleware->redirectUsersTo(fn (Request $request) => route('admin.dashboard.index'));
if ($request->is('admin/*')) {
return route('admin.auth.login.index');
}
return route('auth.login.index');
});
$middleware->redirectUsersTo(function (Request $request) {
if ($request->is('admin/*')) {
return route('admin.dashboard.index');
}
return route('index');
});
$middleware->alias([ $middleware->alias([
'admin' => AdminMiddleware::class, 'admin' => AdminMiddleware::class,
]); ]);
+92
View File
@@ -0,0 +1,92 @@
body {
background: #f5f6fa;
font-family: Vazirmatn, sans-serif;
}
/* Sidebar */
.sidebar {
width: 260px;
min-height: 100vh;
background: #111827;
color: white;
position: fixed;
right: 0;
top: 0;
}
.sidebar .logo {
padding: 25px 15px;
text-align: center;
}
.sidebar a {
color: #cbd5e1;
text-decoration: none;
display: block;
padding: 13px 20px;
transition: .3s;
}
.sidebar a:hover {
background: #1f2937;
color: white;
}
/* Main Content */
.content {
margin-right: 260px;
}
/* Navbar */
.navbar-admin {
height: 70px;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,.08);
}
/* Cards */
.card-box {
border: none;
border-radius: 18px;
box-shadow: 0 5px 20px rgba(0,0,0,.05);
transition: .3s;
}
.card-box:hover {
transform: translateY(-5px);
}
/* Footer */
.admin-footer {
text-align: center;
padding: 20px;
color: #6b7280;
}
+132 -3
View File
@@ -1,3 +1,132 @@
<div> @extends('admin.layouts.app')
<!-- Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi -->
</div> @section('content')
<div class="container mt-4">
<div class="card shadow">
<div class="card-body">
<h3 class="mb-4">
افزودن مدیر جدید
</h3>
<form action="{{ route('admin.admins.store') }}" method="POST">
@csrf
<div class="row">
<div class="col-md-6 mb-3">
<label>نام</label>
<input
type="text"
name="first_name"
class="form-control"
value="{{old('first_name')}}"
>
@error('first_name')
<small class="text-danger">
{{$message}}
</small>
@enderror
</div>
<div class="col-md-6 mb-3">
<label>نام خانوادگی</label>
<input
type="text"
name="last_name"
class="form-control"
value="{{old('last_name')}}"
>
</div>
<div class="col-md-6 mb-3">
<label>نام کاربری</label>
<input
type="text"
name="username"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>ایمیل</label>
<input
type="email"
name="email"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>موبایل</label>
<input
type="text"
name="mobile"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>رمز عبور</label>
<input
type="password"
name="password"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>وضعیت</label>
<select name="status" class="form-control">
<option value="1">
فعال
</option>
<option value="0">
غیرفعال
</option>
</select>
</div>
</div>
<button class="btn btn-success">
ذخیره
</button>
</form>
</div>
</div>
</div>
@endsection
+132 -3
View File
@@ -1,3 +1,132 @@
<div> @extends('admin.layouts.app')
<!-- Let all your things have their places; let each part of your business have its time. - Benjamin Franklin -->
</div> @section('content')
<div class="container mt-4">
<div class="card shadow">
<div class="card-body">
<h3 class="mb-4">
ویرایش مدیر
</h3>
<form action="{{ route('admin.admins.update', $admin->id) }}" method="POST">
@csrf
@method('PUT')
<div class="row">
<div class="col-md-6 mb-3">
<label>نام</label>
<input
type="text"
name="first_name"
class="form-control"
value="{{old('first_name')}}"
>
@error('first_name')
<small class="text-danger">
{{$message}}
</small>
@enderror
</div>
<div class="col-md-6 mb-3">
<label>نام خانوادگی</label>
<input
type="text"
name="last_name"
class="form-control"
value="{{old('last_name')}}"
>
</div>
<div class="col-md-6 mb-3">
<label>نام کاربری</label>
<input
type="text"
name="username"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>ایمیل</label>
<input
type="email"
name="email"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>موبایل</label>
<input
type="text"
name="mobile"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>رمز عبور</label>
<input
type="password"
name="password"
class="form-control"
>
</div>
<div class="col-md-6 mb-3">
<label>وضعیت</label>
<select name="status" class="form-control">
<option value="1">
فعال
</option>
<option value="0">
غیرفعال
</option>
</select>
</div>
</div>
<button class="btn btn-success">
ذخیره
</button>
</form>
</div>
</div>
</div>
@endsection
+209 -3
View File
@@ -1,3 +1,209 @@
<div> @extends('admin.layouts.app')
<!-- Simplicity is the essence of happiness. - Cedric Bledsoe -->
</div> @section('breadcrumb')
<span>
مدیریت مدیران
</span>
@endsection
@section('content')
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">
لیست مدیران
</h5>
<a href="{{ route('admin.admins.create') }}" class="btn btn-primary">
افزودن مدیر جدید
</a>
</div>
<div class="card shadow-sm mb-3">
<div class="card-body">
<form method="GET" action="{{ route('admin.admins.index') }}">
<div class="row g-2">
<div class="col-md-5">
<input
type="text"
name="keyword"
value="{{ request('keyword') }}"
class="form-control"
placeholder="جستجو بر اساس نام..."
>
</div>
<div class="col-md-3">
<select
class="form-control"
name="sort"
onchange="this.form.submit()"
>
<option value="">
مرتب‌سازی بر اساس
</option>
<option value="first_name_asc"
{{ request('sort') == 'first_name_asc' ? 'selected' : '' }}>
نام (الف - ی)
</option>
<option value="first_name_desc"
{{ request('sort') == 'first_name_desc' ? 'selected' : '' }}>
نام (ی - الف)
</option>
<option value="newest"
{{ request('sort') == 'newest' ? 'selected' : '' }}>
جدیدترین
</option>
</select>
</div>
<div class="col-md-2">
<button class="btn btn-primary w-100">
جستجو
</button>
</div>
</div>
</form>
</div>
</div>
<div class="card shadow">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-hover text-center align-middle">
<thead class="table-light">
<tr>
<th>شناسه</th>
<th>نام</th>
<th>نام خانوادگی</th>
<th>نام کاربری</th>
<th>ایمیل</th>
<th>وضعیت</th>
<th>عملیات</th>
</tr>
</thead>
<tbody>
@forelse($admins as $admin)
<tr>
<td>{{ $admin->id }}</td>
<td>{{ $admin->first_name }}</td>
<td>{{ $admin->last_name }}</td>
<td>{{ $admin->username }}</td>
<td>{{ $admin->email }}</td>
<td>
@if($admin->status)
<span class="badge bg-success">
فعال
</span>
@else
<span class="badge bg-danger">
غیرفعال
</span>
@endif
</td>
<td>
<a href="{{ route('admin.admins.edit', $admin->id) }}"
class="btn btn-sm btn-warning">
ویرایش
</a>
<form action="{{ route('admin.admins.destroy', $admin->id) }}"
method="POST"
class="d-inline">
@csrf
@method('DELETE')
<button class="btn btn-sm btn-danger"
onclick="return confirm('آیا مطمئن هستید؟')">
حذف
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="7">
رکوردی پیدا نشد
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{ $admins->links() }}
</div>
</div>
</div>
@endsection
@@ -1,3 +0,0 @@
<div>
<!-- Happiness is not something readymade. It comes from your own actions. - Dalai Lama -->
</div>
+84 -3
View File
@@ -1,3 +1,84 @@
<div> @extends('admin.layouts.app')
<!-- Smile, breathe, and go slowly. - Thich Nhat Hanh -->
</div> @section('content')
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-5">
<div class="card shadow rounded-4">
<div class="card-body p-4">
<h3 class="text-center mb-4">
ورود مدیر
</h3>
@error('general')
<span style="color: #42a5f5"> {{$message}} </span>
@enderror
<form action="{{ route('admin.auth.login.post') }}" method="POST">
@csrf
<div class="mb-3">
<label>
نام کاربری
</label>
<input
type="text"
name="username"
class="form-control"
value="{{old('username')}}"
>
@error('username')
<span style="color: #42a5f5"> {{$message}} </span>
@enderror
</div>
<div class="mb-3">
<label>
رمز عبور
</label>
<input
type="password"
name="password"
class="form-control"
>
@error('password')
<span style="color: #42a5f5"> {{$message}} </span>
@enderror
</div>
<button class="btn btn-primary w-100">
ورود
</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+133 -5
View File
@@ -1,21 +1,149 @@
@extends('admin.layouts.app') @extends('admin.layouts.app')
@section('breadcrumb')
@section('title','داشبورد') <span>
داشبورد
</span>
@endsection
@section('content') @section('content')
<div class="card shadow">
<div class="container-fluid">
<h3 class="mb-4">
داشبورد مدیریت
</h3>
<div class="row g-4">
<div class="col-md-3">
<div class="card dashboard-card shadow-sm">
<div class="card-body"> <div class="card-body">
<h3> <h6>
خوش آمدید به پنل مدیریت مدیران
</h3> </h6>
<h2>
{{ $adminsCount ?? 0 }}
</h2>
</div> </div>
</div> </div>
</div>
<div class="col-md-3">
<div class="card dashboard-card shadow-sm">
<div class="card-body">
<h6>
کاربران
</h6>
<h2>
{{ $usersCount ?? 0 }}
</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card dashboard-card shadow-sm">
<div class="card-body">
<h6>
تورها
</h6>
<h2>
{{ $toursCount ?? 0 }}
</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card dashboard-card shadow-sm">
<div class="card-body">
<h6>
پروازها
</h6>
<h2>
{{ $flightsCount ?? 0 }}
</h2>
</div>
</div>
</div>
</div>
<div class="card shadow-sm mt-5">
<div class="card-header">
آخرین فعالیت‌ها
</div>
<div class="card-body">
<p class="text-muted">
هنوز فعالیتی ثبت نشده است.
</p>
</div>
</div>
</div>
@endsection @endsection
+10 -21
View File
@@ -8,43 +8,32 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> <title>
@yield('title', 'پنل مدیریت') پنل مدیریت
@isset($title)
|
@endisset
</title> </title>
<link href="{{asset('assets/css/bootstrap.css')}}" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
<link href="{{ asset('assets/admin/css/style.css') }}" rel="stylesheet">
</head> </head>
<body> <body>
@includeUnless(isset($rawLayout),'admin.layouts.header')
<div class="d-flex"> <div class="d-flex">
@includeUnless(isset($rawLayout),'admin.layouts.sidebar')
@include('admin.layouts.sidebar') <div class="main-content flex-grow-1">
<div class="flex-grow-1">
@include('admin.layouts.header')
<main class="p-4">
@yield('content') @yield('content')
</main>
@include('admin.layouts.footer')
</div> </div>
</div> </div>
@includeUnless(isset($rawLayout),'admin.layouts.footer')
</body> </body>
</html> </html>
@@ -4,6 +4,11 @@
<span class="navbar-brand"> <span class="navbar-brand">
داشبورد مدیریت داشبورد مدیریت
</span> </span>
<div class="breadcrumb-wrapper">
@yield('breadcrumb')
</div>
<div> <div>
@@ -20,6 +20,16 @@
</a> </a>
</li> </li>
<li class="nav-item mb-2">
<a href="{{ route('admin.admins.index') }}"
class="nav-link text-white">
مدیریت مدیران
</a>
</li>
<li class="nav-item mb-2"> <li class="nav-item mb-2">
+17
View File
@@ -1,6 +1,7 @@
<?php <?php
use App\Http\Controllers\Admin\AdminController;
use App\Http\Controllers\Admin\Auth\AuthController; use App\Http\Controllers\Admin\Auth\AuthController;
use App\Http\Controllers\Admin\Auth\DashboardController; use App\Http\Controllers\Admin\Auth\DashboardController;
use App\Http\Controllers\Admin\UserController; use App\Http\Controllers\Admin\UserController;
@@ -72,6 +73,22 @@ Route::prefix('admin')->name('admin.')->group(function (){
}); });
}); });
Route::prefix('admins')->name('admins.')->controller(AdminController::class)->group(function (){
Route::get('/','index')->name('index');
Route::get('create','create')->name('create');
Route::post('store','store')->name('store');
Route::prefix('{admin}')->group(function (){
Route::get('edit','edit')->name('edit');
Route::put('update','update')->name('update');
Route::delete('destroy','destroy')->name('destroy');
});
});
}); });
}); });