comp-4 hai 1 mes
pai
achega
fe7a62db56

+ 46 - 0
app/Http/Controllers/ApplicationController.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Support\Facades\Auth;
+use App\Models\Application;
+use App\Models\Course;
+use Illuminate\Http\Request;
+
+class ApplicationController extends Controller
+{
+    public function index() {
+        $applications = Auth::user()
+        ->applications()
+        ->with('course')
+        ->orderBy('created_at')
+        ->get();
+
+        return view('applications.index', compact('applications'));
+    }
+
+    public function create() {
+        $courses = Course::all();
+
+        return view('applications.create', compact('courses'));
+    }
+
+    public function store(Request $request) {
+        $request->validate([
+            'course_id' => ['required', 'exists:courses,id'],
+            'date_start' => ['required', 'date', 'after_or_equal:today'],
+            'payment_method' => ['required'],
+        ]);
+
+        Application::create([
+            'user_id' => Auth::id(),
+            'course_id' => $request->course_id,
+            'date_start' => $request->date_start,
+            'payment_method' => $request->payment_method,
+        ]);
+
+        return redirect()
+        ->route('applications.index')
+        ->with('success', 'Заявка успешно создана!');
+    }
+}

+ 6 - 2
app/Http/Controllers/Auth/RegisteredUserController.php

@@ -31,13 +31,17 @@ class RegisteredUserController extends Controller
     public function store(Request $request): RedirectResponse
     {
         $request->validate([
-            'name' => ['required', 'string', 'max:255'],
+            'login' => ['required', 'string', 'max:255', 'unique:' . User::class],
+            'fio' => ['required', 'string', 'max:255'],
+            'phone' => ['required', 'string', 'max:255'],
             'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class],
             'password' => ['required', 'confirmed', Rules\Password::defaults()],
         ]);
 
         $user = User::create([
-            'name' => $request->name,
+            'login' => $request->login,
+            'fio' => $request->fio,
+            'phone' => $request->phone,
             'email' => $request->email,
             'password' => Hash::make($request->password),
         ]);

+ 5 - 5
app/Http/Requests/Auth/LoginRequest.php

@@ -28,7 +28,7 @@ class LoginRequest extends FormRequest
     public function rules(): array
     {
         return [
-            'email' => ['required', 'string', 'email'],
+            'login' => ['required', 'string'],
             'password' => ['required', 'string'],
         ];
     }
@@ -42,11 +42,11 @@ class LoginRequest extends FormRequest
     {
         $this->ensureIsNotRateLimited();
 
-        if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
+        if (! Auth::attempt($this->only('login', 'password'), $this->boolean('remember'))) {
             RateLimiter::hit($this->throttleKey());
 
             throw ValidationException::withMessages([
-                'email' => trans('auth.failed'),
+                'login' => trans('auth.failed'),
             ]);
         }
 
@@ -69,7 +69,7 @@ class LoginRequest extends FormRequest
         $seconds = RateLimiter::availableIn($this->throttleKey());
 
         throw ValidationException::withMessages([
-            'email' => trans('auth.throttle', [
+            'login' => trans('auth.throttle', [
                 'seconds' => $seconds,
                 'minutes' => ceil($seconds / 60),
             ]),
@@ -81,6 +81,6 @@ class LoginRequest extends FormRequest
      */
     public function throttleKey(): string
     {
-        return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
+        return Str::transliterate(Str::lower($this->string('login')).'|'.$this->ip());
     }
 }

+ 31 - 0
app/Models/Application.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Models;
+
+
+use Illuminate\Database\Eloquent\Model;
+
+class Application extends Model
+{
+        /**
+     * The attributes that are mass assignable.
+     *
+     * @var list<string>
+     */
+    protected $fillable = [
+        'user_id',
+        'course_id',
+        'date_start',
+        'payment_method',
+        'review',
+        'status',
+    ];
+
+    public function user() {
+        return $this->belongsTo(User::class);
+    }
+
+        public function course() {
+        return $this->belongsTo(Course::class);
+    }
+}

+ 17 - 0
app/Models/Course.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Course extends Model
+{
+        /**
+     * The attributes that are mass assignable.
+     *
+     * @var list<string>
+     */
+    protected $fillable = [
+        'name',
+    ];
+}

+ 8 - 1
app/Models/User.php

@@ -18,7 +18,9 @@ class User extends Authenticatable
      * @var list<string>
      */
     protected $fillable = [
-        'name',
+        'login',
+        'fio',
+        'phone',
         'email',
         'password',
     ];
@@ -31,6 +33,7 @@ class User extends Authenticatable
     protected $hidden = [
         'password',
         'remember_token',
+        'role',
     ];
 
     /**
@@ -45,4 +48,8 @@ class User extends Authenticatable
             'password' => 'hashed',
         ];
     }
+
+    public function applications() {
+        return $this->hasMany(Application::class);
+    }
 }

+ 5 - 2
database/migrations/0001_01_01_000000_create_users_table.php

@@ -13,10 +13,13 @@ return new class extends Migration
     {
         Schema::create('users', function (Blueprint $table) {
             $table->id();
-            $table->string('name');
-            $table->string('email')->unique();
+            $table->string('login')->unique();
+            $table->string('fio')->nullable();
+            $table->string('phone')->nullable();
+            $table->string('email')->unique()->nullable();
             $table->timestamp('email_verified_at')->nullable();
             $table->string('password');
+            $table->string('role')->default('user');
             $table->rememberToken();
             $table->timestamps();
         });

+ 28 - 0
database/migrations/2026_04_18_065407_create_courses_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('courses', function (Blueprint $table) {
+            $table->id();
+            $table->string('name');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('courses');
+    }
+};

+ 33 - 0
database/migrations/2026_04_18_065412_create_applications_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('applications', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('user_id')->constrained()->onDelete('cascade');
+            $table->foreignId('course_id')->constrained()->onDelete('cascade');
+            $table->date('date_start');
+            $table->string('payment_method');
+            $table->text('review')->nullable();
+            $table->string('status')->default('Новая');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('applications');
+    }
+};

BIN=BIN
public/images/image01.webp


BIN=BIN
public/images/image02.jpg


BIN=BIN
public/images/image03.png


BIN=BIN
public/images/image04.jpg


BIN=BIN
public/images/image05.jpg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 5 - 0
public/libs/bootstrap/bootstrap.bundle.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 4 - 0
public/libs/bootstrap/bootstrap.min.css


+ 56 - 0
resources/views/applications/create.blade.php

@@ -0,0 +1,56 @@
+<x-app-layout>
+    <div class="py-12">
+        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
+            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
+                <div class="p-6 text-gray-900">
+                    <h1 class="text-xl font-medium">Создание заявки</h1>
+
+                    @if ($errors->any())
+                        <ul class="mt-4 mb-4">
+                            @foreach ($errors->all() as $error)
+                             <li class="mt-2">
+                                <span class="btn btn-danger">{{$error}}</span>
+                            </li>
+                            @endforeach
+                        </ul>
+                    @endif
+
+                    <form method="POST" action="{{ route('applications.store')}}">
+                        @csrf
+
+                        <div class="flex flex-col gap-2 mt-4" style="align-items: start;">
+                            <label for="course_id" class="block font-medium text-sm text-gray-700">Желаемый курс обучения</label>
+                            <select class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="course_id">
+                                @foreach ($courses as $course)
+                                <option value="{{ $course->id }}">{{$course->name}}</option>
+                                @endforeach
+                            </select>
+                        </div>
+
+                       <div class="flex flex-col gap-2 mt-4" style="align-items: start;">
+                            <label for="date_start" class="block font-medium text-sm text-gray-700">Желаемая дата обучения</label>
+                            <input name="date_start" type="text" data-date-input placeholder="ДД.ММ.ГГГГ"  class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"  />
+                        </div>
+
+                        <div class="flex flex-col gap-2 mt-4" style="align-items: start;">
+                            <label class="block font-medium text-sm text-gray-700">Желаемый способ оплаты</label>
+
+                            <div class="flex gap-4">
+                            <label class="block font-medium text-sm text-gray-700">
+                                <input name="payment_method" type="radio" value="Наличными" class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"  />
+                                Наличными
+                            </label>
+                            <label class="block font-medium text-sm text-gray-700">
+                                <input name="payment_method" type="radio" value="Переводом по номеру телефона" class="border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"  />
+                                Переводом по номеру телефона
+                            </label>
+                            </div>
+                        </div>
+
+                        <x-primary-button class="mt-6">Отправить</x-primary-button>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+</x-app-layout>

+ 61 - 0
resources/views/applications/index.blade.php

@@ -0,0 +1,61 @@
+<x-app-layout>
+    <div class="py-12">
+        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
+            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
+                <div class="p-6 text-gray-900">
+                    <h1 class="text-xl font-medium">Мои заявки</h1>
+
+                    @if (session('success'))
+                        <span class="btn btn-success mt-4 mb-4">{{session('success')}}</span>
+                    @endif
+
+                    <table class="w-full text-center mt-4">
+                        <thead>
+                            <tr>
+                                <th>
+                                    №
+                                </th>
+                                <th>
+                                    Курс
+                                </th>
+                                <th>
+                                    Дата начала обучения
+                                </th>
+                                <th>
+                                    Способ оплаты
+                                </th>
+                                <th>
+                                    Статус
+                                </th>
+                            </tr>
+                            <tr style="height: 15px;"></tr>
+                        </thead>
+                        <tbody>
+                            @foreach ($applications as $app)
+                                <tr style="height: 50px; border-block: 1px solid #6b7280;">
+                                    <td>{{$app->id}}</td>
+                                    <td>{{$app->course->name}}</td>
+                                    <td>{{$app->date_start}}</td>
+                                    <td>{{$app->payment_method}}</td>
+                                    <td>{{$app->status}}</td>
+                                    
+                                    @if ($app->status === 'Обучение завершено')
+                                    <td>
+                                        <x-primary-button>Отзыв</x-primary-button>
+
+                                        <form method="" action="">
+                                            @csrf
+
+                                        </form>
+                                    </td>
+                                    @endif
+                            
+                                </tr>
+                            @endforeach
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</x-app-layout>

+ 5 - 8
resources/views/auth/login.blade.php

@@ -5,11 +5,10 @@
     <form method="POST" action="{{ route('login') }}">
         @csrf
 
-        <!-- Email Address -->
         <div>
-            <x-input-label for="email" :value="__('Email')" />
-            <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
-            <x-input-error :messages="$errors->get('email')" class="mt-2" />
+            <x-input-label for="login" :value="__('Логин')" />
+            <x-text-input id="login" class="block mt-1 w-full" type="text" name="login" :value="old('login')" required autofocus autocomplete="username" />
+            <x-input-error :messages="$errors->get('login')" class="mt-2" />
         </div>
 
         <!-- Password -->
@@ -33,11 +32,9 @@
         </div>
 
         <div class="flex items-center justify-end mt-4">
-            @if (Route::has('password.request'))
-                <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('password.request') }}">
-                    {{ __('Forgot your password?') }}
+                <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('register') }}">
+                    {{ __('Еще не зарегистрированы? Регистрация') }}
                 </a>
-            @endif
 
             <x-primary-button class="ms-3">
                 {{ __('Log in') }}

+ 18 - 4
resources/views/auth/register.blade.php

@@ -2,11 +2,25 @@
     <form method="POST" action="{{ route('register') }}">
         @csrf
 
-        <!-- Name -->
+        <!-- login -->
         <div>
-            <x-input-label for="name" :value="__('Name')" />
-            <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
-            <x-input-error :messages="$errors->get('name')" class="mt-2" />
+            <x-input-label for="login" :value="__('Логин')" />
+            <x-text-input id="login" class="block mt-1 w-full" type="text" name="login" :value="old('login')" required autofocus autocomplete="login" />
+            <x-input-error :messages="$errors->get('login')" class="mt-2" />
+        </div>
+
+                <!-- fio -->
+        <div class="mt-4">
+            <x-input-label for="fio" :value="__('ФИО')" />
+            <x-text-input id="fio" class="block mt-1 w-full" type="text" name="fio" :value="old('fio')" required autofocus autocomplete="fio" />
+            <x-input-error :messages="$errors->get('fio')" class="mt-2" />
+        </div>
+
+                <!-- phone -->
+        <div class="mt-4">
+            <x-input-label for="phone" :value="__('Номер телефона')" />
+            <x-text-input id="phone" class="block mt-1 w-full" type="text" name="phone" :value="old('phone')" required autofocus autocomplete="phone" placeholder="8(XXX)XXX-XX-XX" />
+            <x-input-error :messages="$errors->get('phone')" class="mt-2" />
         </div>
 
         <!-- Email Address -->

+ 1 - 7
resources/views/dashboard.blade.php

@@ -1,15 +1,9 @@
 <x-app-layout>
-    <x-slot name="header">
-        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
-            {{ __('Dashboard') }}
-        </h2>
-    </x-slot>
-
     <div class="py-12">
         <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
             <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                 <div class="p-6 text-gray-900">
-                    {{ __("You're logged in!") }}
+                    <h1 class="text-xl font-medium"></h1>
                 </div>
             </div>
         </div>

+ 4 - 0
resources/views/layouts/app.blade.php

@@ -11,6 +11,8 @@
         <link rel="preconnect" href="https://fonts.bunny.net">
         <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
 
+        <link rel="stylesheet" href="/libs/bootstrap/bootstrap.min.css">
+
         <!-- Scripts -->
         @vite(['resources/css/app.css', 'resources/js/app.js'])
     </head>
@@ -32,5 +34,7 @@
                 {{ $slot }}
             </main>
         </div>
+
+        <script src="/libs/bootstrap/bootstrap.bundle.min.js"></script> 
     </body>
 </html>

+ 76 - 2
resources/views/layouts/navigation.blade.php

@@ -1,3 +1,4 @@
+@auth
 <nav x-data="{ open: false }" class="bg-white border-b border-gray-100">
     <!-- Primary Navigation Menu -->
     <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -15,6 +16,20 @@
                     <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
                         {{ __('Dashboard') }}
                     </x-nav-link>
+
+                                                                                <x-nav-link :href="route('applications.index')" :active="request()->routeIs('applications.index')">
+                        {{ __('Заявки') }}
+                    </x-nav-link>
+
+                                                            <x-nav-link :href="route('applications.create')" :active="request()->routeIs('applications.create')">
+                        {{ __('Создать заявку') }}
+                    </x-nav-link>
+
+                    @if (Auth::user()->role === 'admin')
+                    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
+                        {{ __('Администрирование') }}
+                    </x-nav-link>
+                    @endif
                 </div>
             </div>
 
@@ -23,7 +38,7 @@
                 <x-dropdown align="right" width="48">
                     <x-slot name="trigger">
                         <button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150">
-                            <div>{{ Auth::user()->name }}</div>
+                            <div>{{ Auth::user()->login }}</div>
 
                             <div class="ms-1">
                                 <svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
@@ -70,12 +85,27 @@
             <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
                 {{ __('Dashboard') }}
             </x-responsive-nav-link>
+
+            <x-responsive-nav-link :href="route('applications.index')" :active="request()->routeIs('applications.index')">
+                {{ __('Заявки') }}
+            </x-responsive-nav-link>
+
+            <x-responsive-nav-link :href="route('applications.create')" :active="request()->routeIs('applications.create')">
+                {{ __('Создать заявку') }}
+            </x-responsive-nav-link>
+
+                    @if (Auth::user()->role === 'admin')
+                    <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
+                        {{ __('Администрирование') }}
+                    </x-responsive-nav-link>
+                    @endif
+
         </div>
 
         <!-- Responsive Settings Options -->
         <div class="pt-4 pb-1 border-t border-gray-200">
             <div class="px-4">
-                <div class="font-medium text-base text-gray-800">{{ Auth::user()->name }}</div>
+                <div class="font-medium text-base text-gray-800">{{ Auth::user()->login }}</div>
                 <div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
             </div>
 
@@ -98,3 +128,47 @@
         </div>
     </div>
 </nav>
+@else
+<nav x-data="{ open: false }" class="bg-white border-b border-gray-100">
+    <!-- Primary Navigation Menu -->
+    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+        <div class="flex justify-between h-16">
+            <div class="flex">
+                <!-- Navigation Links -->
+                <div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
+                                        <x-nav-link :href="route('login')" :active="request()->routeIs('login')">
+                        {{ __('Войти') }}
+                    </x-nav-link>
+
+                                        <x-nav-link :href="route('register')" :active="request()->routeIs('register')">
+                        {{ __('Регистрация') }}
+                    </x-nav-link>
+                </div>
+            </div>
+
+            <!-- Hamburger -->
+            <div class="-me-2 flex items-center sm:hidden">
+                <button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out">
+                    <svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
+                        <path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
+                        <path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+                    </svg>
+                </button>
+            </div>
+        </div>
+    </div>
+
+    <!-- Responsive Navigation Menu -->
+    <div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
+        <div class="pt-2 pb-3 space-y-1">
+            <x-responsive-nav-link :href="route('login')" :active="request()->routeIs('login')">
+                {{ __('Войти') }}
+            </x-responsive-nav-link>
+
+            <x-responsive-nav-link :href="route('register')" :active="request()->routeIs('register')">
+                {{ __('Регистрация') }}
+            </x-responsive-nav-link>
+        </div>
+    </div>
+</nav>
+@endauth

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 13 - 16
resources/views/welcome.blade.php


+ 8 - 0
routes/web.php

@@ -1,5 +1,6 @@
 <?php
 
+use App\Http\Controllers\ApplicationController;
 use App\Http\Controllers\ProfileController;
 use Illuminate\Support\Facades\Route;
 
@@ -15,6 +16,13 @@ Route::middleware('auth')->group(function () {
     Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
     Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
     Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
+
+    Route::get('/applications', [ApplicationController::class, 'index'])->name('applications.index');
+
+    Route::get('/applications/create', [ApplicationController::class, 'create'])->name('applications.create');
+
+    Route::post('/applications/store', [ApplicationController::class, 'store'])->name('applications.store');
+
 });
 
 require __DIR__.'/auth.php';

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio