Compare commits

...

4 Commits

Author SHA1 Message Date
shahrzad 931d67cdd8 fix: update admin dashboard 2026-08-17 19:47:25 +03:30
shahrzad 7107286c1a fix: update admin management controller 2026-08-17 19:46:45 +03:30
shahrzad 994add824f feat: add user password change interface 2026-08-17 19:45:47 +03:30
shahrzad 0b7e0b8e94 feat: add visits export 2026-08-17 19:44:50 +03:30
5 changed files with 322 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
class VisitsExport implements FromArray, WithHeadings, ShouldAutoSize
{
protected $rows;
public function __construct(array $rows)
{
$this->rows = $rows;
}
public function headings(): array
{
return [
'شناسه',
'نام',
'نام خانوادگی',
'ایمیل',
'موبایل',
'تاریخ ثبت‌نام',
];
}
public function array(): array
{
return $this->rows;
}
}
@@ -4,16 +4,100 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\AdminStoreRequest;
use App\Http\Requests\AdminUpdateRequest;
use App\Models\Admin;
use App\Models\Permission;
use Illuminate\Support\Facades\Hash;
class AdminController extends Controller
{
public function index()
{
$admins = Admin::query()
->with('permissions')
->latest()
->paginate(10);
return view('admin.admins.index', compact('admins'));
}
public function create()
{
$permissions = Permission::query()
->orderBy('name')
->get();
return view(
'admin.admins.create',
compact('permissions')
);
}
public function store(AdminStoreRequest $request)
{
$data = $request->validated();
$permissions = $data['permissions'] ?? [];
unset($data['permissions']);
$data['password'] = Hash::make($data['password']);
$admin = Admin::create($data);
$admin->permissions()->sync($permissions);
return redirect()
->route('admin.admins.index')
->with('success', 'مدیر با موفقیت ایجاد شد.');
}
public function edit(Admin $admin)
{
$permissions = Permission::query()
->orderBy('name')
->get();
$admin->load('permissions');
return view(
'admin.admins.edit',
compact('admin', 'permissions')
);
}
public function update(AdminUpdateRequest $request, Admin $admin)
{
$data = $request->validated();
$permissions = $data['permissions'] ?? [];
unset($data['permissions']);
if (!empty($data['password'])) {
$data['password'] = Hash::make($data['password']);
} else {
unset($data['password']);
}
$admin->update($data);
$admin->permissions()->sync($permissions);
return redirect()
->route('admin.admins.index')
->with('success', 'اطلاعات مدیر با موفقیت ویرایش شد.');
}
public function destroy(Admin $admin)
{
$admin->permissions()->detach();
$admin->delete();
return redirect()
->route('admin.admins.index')
->with('success', 'مدیر با موفقیت حذف شد.');
}
}
@@ -10,6 +10,11 @@ use App\Models\Flight;
use App\Models\Payment;
use App\Models\Tour;
use App\Models\User;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\VisitsExport;
use App\Models\VisitStatistic;
use Illuminate\Support\Carbon;
use Hekmatinasser\Verta\Verta;
class DashboardController extends Controller
{
@@ -39,4 +44,25 @@ class DashboardController extends Controller
'paymentsCount'
));
}
public function exportExcel()
{
// ۱. لیست کاربران با فیلدهایی که واقعاً در جدول users داری
$users = User::select('id', 'first_name', 'last_name', 'email', 'mobile', 'created_at')->get();
// ۲. تبدیل به آرایه برای اکسل
$rows = $users->map(fn($user) => [
$user->id,
$user->first_name,
$user->last_name,
$user->email,
$user->mobile,
$user->created_at ? Verta::instance($user->created_at)->format('Y/m/d') : '-',
])->toArray();
// ۳. تغییر کلاس اکسل و اسم فایل
return Excel::download(new VisitsExport($rows), 'users-report-' . now()->format('Y-m-d') . '.xlsx');
}
}
+120
View File
@@ -0,0 +1,120 @@
/* ========================================
Admin Panel
Airline Agency
======================================== */
body {
background-color: #f5f7fb;
}
/* Main admin layout */
.admin-wrapper {
min-height: 100vh;
}
/* Sidebar */
.admin-sidebar {
width: 260px;
min-height: 100vh;
background: #ffffff;
border-left: 1px solid #e9ecef;
}
/* Main content */
.admin-content {
min-height: 100vh;
padding: 24px;
}
/* Cards */
.admin-card {
background: #ffffff;
border: 1px solid #e9ecef;
border-radius: 12px;
}
/* Dashboard stat cards */
.admin-stat-card {
transition: all .2s ease;
}
.admin-stat-card:hover {
transform: translateY(-2px);
}
/* Sidebar links */
.admin-sidebar .nav-link {
color: #495057;
border-radius: 8px;
margin-bottom: 4px;
padding: 10px 12px;
}
.admin-sidebar .nav-link:hover {
background-color: #f1f5f9;
}
.admin-sidebar .nav-link.active {
background-color: #eaf2ff;
color: #0d6efd;
}
/* Airline icon box */
.admin-icon-box {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
}
/* Tables */
.admin-table th {
white-space: nowrap;
}
.admin-table td {
vertical-align: middle;
}
/* Breadcrumb */
.admin-breadcrumb {
margin-bottom: 20px;
}
/* Responsive */
@media (max-width: 991px) {
.admin-sidebar {
width: 100%;
min-height: auto;
}
.admin-content {
padding: 16px;
}
}
@@ -0,0 +1,56 @@
@extends('admin.layouts.app')
@section('title', 'تغییر رمز کاربر')
@section('content')
<div class="container-fluid pt-4">
@if($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="row">
<div class="col-xl-6">
<div class="card custom-card">
<div class="card-header">
<h5 class="mb-0">تغییر رمز عبور — {{ $user->full_name ?? $user->name }}</h5>
</div>
<div class="card-body">
<form method="POST" action="{{ route('admin.users.update-password', $user) }}">
@csrf
@method('PUT')
<div class="mb-3">
<label class="form-label">رمز عبور جدید</label>
<input type="password" name="password"
class="form-control @error('password') is-invalid @enderror">
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="form-label">تکرار رمز جدید</label>
<input type="password" name="password_confirmation"
class="form-control">
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success">ذخیره رمز</button>
<a href="{{ route('admin.users.index') }}" class="btn btn-secondary">انصراف</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection