Ver código fonte

импорт их xls в базу, распознавание по article
постраницчный вывод таблицы товаров, количество товаров на странице
фильры: общий поиск, серия, диапазон цен
редактирование товара (все поля, кроме изображения и артикула)

Александр Мусихин 2 anos atrás
pai
commit
d7036286f7
28 arquivos alterados com 294 adições e 825 exclusões
  1. 0 40
      app/Http/Controllers/Auth/ConfirmPasswordController.php
  2. 0 22
      app/Http/Controllers/Auth/ForgotPasswordController.php
  3. 0 40
      app/Http/Controllers/Auth/LoginController.php
  4. 0 73
      app/Http/Controllers/Auth/RegisterController.php
  5. 0 30
      app/Http/Controllers/Auth/ResetPasswordController.php
  6. 0 42
      app/Http/Controllers/Auth/VerificationController.php
  7. 64 18
      app/Http/Controllers/ProductController.php
  8. 38 0
      app/Http/Requests/SaveProductRequest.php
  9. 0 36
      database/migrations/2014_10_12_000000_create_users_table.php
  10. 0 32
      database/migrations/2014_10_12_100000_create_password_resets_table.php
  11. 0 36
      database/migrations/2019_08_19_000000_create_failed_jobs_table.php
  12. 0 37
      database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php
  13. 1 1
      database/migrations/2023_01_26_032600_create_products_table.php
  14. 10 0
      resources/sass/_custom.scss
  15. 3 1
      resources/sass/_variables.scss
  16. 2 0
      resources/sass/app.scss
  17. 0 73
      resources/views/auth/login.blade.php
  18. 0 49
      resources/views/auth/passwords/confirm.blade.php
  19. 0 47
      resources/views/auth/passwords/email.blade.php
  20. 0 65
      resources/views/auth/passwords/reset.blade.php
  21. 0 77
      resources/views/auth/register.blade.php
  22. 0 28
      resources/views/auth/verify.blade.php
  23. 0 23
      resources/views/home.blade.php
  24. 68 42
      resources/views/products/index.blade.php
  25. 76 0
      resources/views/products/product.blade.php
  26. 0 13
      resources/views/welcome.blade.php
  27. 2 0
      routes/web.php
  28. 30 0
      todo.txt

+ 0 - 40
app/Http/Controllers/Auth/ConfirmPasswordController.php

@@ -1,40 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use App\Providers\RouteServiceProvider;
-use Illuminate\Foundation\Auth\ConfirmsPasswords;
-
-class ConfirmPasswordController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Confirm Password Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller is responsible for handling password confirmations and
-    | uses a simple trait to include the behavior. You're free to explore
-    | this trait and override any functions that require customization.
-    |
-    */
-
-    use ConfirmsPasswords;
-
-    /**
-     * Where to redirect users when the intended url fails.
-     *
-     * @var string
-     */
-    protected $redirectTo = RouteServiceProvider::HOME;
-
-    /**
-     * Create a new controller instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        $this->middleware('auth');
-    }
-}

+ 0 - 22
app/Http/Controllers/Auth/ForgotPasswordController.php

@@ -1,22 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
-
-class ForgotPasswordController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Password Reset Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller is responsible for handling password reset emails and
-    | includes a trait which assists in sending these notifications from
-    | your application to your users. Feel free to explore this trait.
-    |
-    */
-
-    use SendsPasswordResetEmails;
-}

+ 0 - 40
app/Http/Controllers/Auth/LoginController.php

@@ -1,40 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use App\Providers\RouteServiceProvider;
-use Illuminate\Foundation\Auth\AuthenticatesUsers;
-
-class LoginController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Login Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller handles authenticating users for the application and
-    | redirecting them to your home screen. The controller uses a trait
-    | to conveniently provide its functionality to your applications.
-    |
-    */
-
-    use AuthenticatesUsers;
-
-    /**
-     * Where to redirect users after login.
-     *
-     * @var string
-     */
-    protected $redirectTo = RouteServiceProvider::HOME;
-
-    /**
-     * Create a new controller instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        $this->middleware('guest')->except('logout');
-    }
-}

+ 0 - 73
app/Http/Controllers/Auth/RegisterController.php

@@ -1,73 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use App\Providers\RouteServiceProvider;
-use App\Models\User;
-use Illuminate\Foundation\Auth\RegistersUsers;
-use Illuminate\Support\Facades\Hash;
-use Illuminate\Support\Facades\Validator;
-
-class RegisterController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Register Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller handles the registration of new users as well as their
-    | validation and creation. By default this controller uses a trait to
-    | provide this functionality without requiring any additional code.
-    |
-    */
-
-    use RegistersUsers;
-
-    /**
-     * Where to redirect users after registration.
-     *
-     * @var string
-     */
-    protected $redirectTo = RouteServiceProvider::HOME;
-
-    /**
-     * Create a new controller instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        $this->middleware('guest');
-    }
-
-    /**
-     * Get a validator for an incoming registration request.
-     *
-     * @param  array  $data
-     * @return \Illuminate\Contracts\Validation\Validator
-     */
-    protected function validator(array $data)
-    {
-        return Validator::make($data, [
-            'name' => ['required', 'string', 'max:255'],
-            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
-            'password' => ['required', 'string', 'min:8', 'confirmed'],
-        ]);
-    }
-
-    /**
-     * Create a new user instance after a valid registration.
-     *
-     * @param  array  $data
-     * @return \App\Models\User
-     */
-    protected function create(array $data)
-    {
-        return User::create([
-            'name' => $data['name'],
-            'email' => $data['email'],
-            'password' => Hash::make($data['password']),
-        ]);
-    }
-}

+ 0 - 30
app/Http/Controllers/Auth/ResetPasswordController.php

@@ -1,30 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use App\Providers\RouteServiceProvider;
-use Illuminate\Foundation\Auth\ResetsPasswords;
-
-class ResetPasswordController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Password Reset Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller is responsible for handling password reset requests
-    | and uses a simple trait to include this behavior. You're free to
-    | explore this trait and override any methods you wish to tweak.
-    |
-    */
-
-    use ResetsPasswords;
-
-    /**
-     * Where to redirect users after resetting their password.
-     *
-     * @var string
-     */
-    protected $redirectTo = RouteServiceProvider::HOME;
-}

+ 0 - 42
app/Http/Controllers/Auth/VerificationController.php

@@ -1,42 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Auth;
-
-use App\Http\Controllers\Controller;
-use App\Providers\RouteServiceProvider;
-use Illuminate\Foundation\Auth\VerifiesEmails;
-
-class VerificationController extends Controller
-{
-    /*
-    |--------------------------------------------------------------------------
-    | Email Verification Controller
-    |--------------------------------------------------------------------------
-    |
-    | This controller is responsible for handling email verification for any
-    | user that recently registered with the application. Emails may also
-    | be re-sent if the user didn't receive the original email message.
-    |
-    */
-
-    use VerifiesEmails;
-
-    /**
-     * Where to redirect users after verification.
-     *
-     * @var string
-     */
-    protected $redirectTo = RouteServiceProvider::HOME;
-
-    /**
-     * Create a new controller instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        $this->middleware('auth');
-        $this->middleware('signed')->only('verify');
-        $this->middleware('throttle:6,1')->only('verify', 'resend');
-    }
-}

+ 64 - 18
app/Http/Controllers/ProductController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers;
 
+use App\Http\Requests\SaveProductRequest;
 use App\Models\Product;
 use Illuminate\Http\Request;
 use Illuminate\Support\Str;
@@ -9,14 +10,46 @@ use Illuminate\Support\Str;
 
 class ProductController extends Controller
 {
-    public function index(Request $request){
+    public function index(Request $request)
+    {
+        $data = $request->validate([
+            's' => 'nullable|string',
+            's_series' => 'nullable|string',
+            's_price_min' => 'nullable|integer',
+            's_price_max' => 'nullable|integer',
+            'perpage' => 'nullable|string',
+        ]);
+
+        $query = Product::query();
+        if (!empty($data['s'])) {
+            $query->whereRaw(" (`article` LIKE '%{$data['s']}%' OR `name` LIKE '%{$data['s']}%' OR
+                    `name_for_form` LIKE '%{$data['s']}%' OR `product_group` LIKE '%{$data['s']}%' OR
+                    `characteristics` LIKE '%{$data['s']}%' OR `tech_description` LIKE '%{$data['s']}%'
+                    OR `tech_description_short` LIKE '%{$data['s']}%') ");
+        }
+
+        if (!empty($data['s_series'])) {
+            $query->where('series', '=', $data['s_series']);
+        }
+
+        if (!empty($data['s_price_min'])) {
+            $query->where('price', '>=', $data['s_price_min']);
+        }
+
+        if (!empty($data['s_price_max'])) {
+            $query->where('price', '<=', $data['s_price_max']);
+        }
 
-        $data['products'] = Product::query()->paginate(20);
+        $data['products'] = $query->paginate($data['perpage'] ?? 20)->withQueryString();
+
+        // get all series for select field
         $data['series'] = Product::query()->select('series')->distinct()->OrderBy('series')->get();
         return view('products.index', $data);
     }
 
-    public function upload(Request $request){
+    // todo вынести в job, чтобы работала в фоне
+    public function upload(Request $request)
+    {
         $xls = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($request->file('file'));
         $xls->setReadDataOnly(true);
         $sheet = $xls->load($request->file('file'));
@@ -26,21 +59,21 @@ class ProductController extends Controller
         unset($sheet, $xls);
 
         $series = '';
-        $i = 1;
+        $i = 0;
         $created = 0;
         $updated = 0;
         $no_image = [];
-        foreach ($goods as $good){
+        foreach ($goods as $good) {
             // check first line and skip it
-            if($good[0] === '№п/п' && $good['3'] === 'Наименование') continue;
+            if ($good[0] === '№п/п' && $good['3'] === 'Наименование') continue;
 
             // check the line is name of series
-            if($good[0] == NULL && $good[1] == NULL && $good[2] == NULL && is_string($good[3])){
+            if ($good[0] == NULL && $good[1] == NULL && $good[2] == NULL && is_string($good[3])) {
                 $series = $good[3];
                 continue;
             }
             $tmp = explode("\n", $good[3]);
-            if(!isset($tmp[1])){
+            if (!isset($tmp[1])) {
                 $good[3] = preg_replace('!\s+!', ' ', $good[3]);
                 $tmp = explode(' ', $good[3], 2);
             }
@@ -58,32 +91,45 @@ class ProductController extends Controller
                 'image_path' => $this->find_image($tmp[0]),
             ];
 
-            $a= Product::query()->updateOrCreate(['article' => $tmp[0]], $data);
-            if($a->wasRecentlyCreated) $created++; else $updated++;
+            $a = Product::query()->updateOrCreate(['article' => $tmp[0]], $data);
+            if ($a->wasRecentlyCreated) $created++; else $updated++;
             //echo $i++ . '. Серия: ' . $series . ', артикул: ' . $tmp[0] . '<br>';
             $i++;
-            if($data['image_path'] == '') $no_image[] = $tmp[0];
+            if ($data['image_path'] == '') $no_image[] = $tmp[0];
         }
 
         return view('products.import_result', ['count' => $i, 'updated' => $updated, 'created' => $created, 'no_image' => $no_image]);
 
     }
 
-    private $allfiles; // rememer files list
+    private $allfiles; // remember files list
 
-    private function find_image($article){
+    private function find_image($article)
+    {
         $path_to_files = './' . env('IMAGES_PATH', '---') . '/';
-        if(!isset($this->allfiles) || empty($this->allfiles)){
+        if (!isset($this->allfiles) || empty($this->allfiles)) {
             $this->allfiles = scandir($path_to_files);
         }
-        foreach ($this->allfiles as $filename){
-            if((mb_strpos($filename, $article) !== false) || (
-                    mb_strpos(Str::lower($filename), Str::slug($article)) !== false
-                ))
+        foreach ($this->allfiles as $filename) {
+            if ((mb_strpos($filename, $article) === 0) || (
+                    mb_strpos(Str::lower($filename), Str::slug($article)) === 0))
                 return $filename;
         }
 
         return '';
     }
 
+    public function product(Request $request, $id)
+    {
+        $data['product'] = Product::query()->findOrFail($id);
+        return view('products.product', $data);
+    }
+
+    public function save_product(SaveProductRequest $request)
+    {
+        $p = Product::query()->where('id', $request->validated('id'))->update($request->validated());
+
+        return redirect()->route('index');
+    }
+
 }

+ 38 - 0
app/Http/Requests/SaveProductRequest.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class SaveProductRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        return true; // todo just admin can save it
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, mixed>
+     */
+    public function rules()
+    {
+        return [
+            'id' => 'required',
+            'series' => 'required|string',
+            'name' => 'required|string',
+            'name_for_form' => 'required|string',
+            'product_group' => 'required|string',
+            'price' => 'required|numeric',
+            'characteristics' => 'required|string',
+            'tech_description' => 'required|string',
+            'tech_description_short' => 'required|string',
+        ];
+    }
+}

+ 0 - 36
database/migrations/2014_10_12_000000_create_users_table.php

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

+ 0 - 32
database/migrations/2014_10_12_100000_create_password_resets_table.php

@@ -1,32 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-return new class extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('password_resets', function (Blueprint $table) {
-            $table->string('email')->index();
-            $table->string('token');
-            $table->timestamp('created_at')->nullable();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('password_resets');
-    }
-};

+ 0 - 36
database/migrations/2019_08_19_000000_create_failed_jobs_table.php

@@ -1,36 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-return new class extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('failed_jobs', function (Blueprint $table) {
-            $table->id();
-            $table->string('uuid')->unique();
-            $table->text('connection');
-            $table->text('queue');
-            $table->longText('payload');
-            $table->longText('exception');
-            $table->timestamp('failed_at')->useCurrent();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('failed_jobs');
-    }
-};

+ 0 - 37
database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php

@@ -1,37 +0,0 @@
-<?php
-
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Support\Facades\Schema;
-
-return new class extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('personal_access_tokens', function (Blueprint $table) {
-            $table->id();
-            $table->morphs('tokenable');
-            $table->string('name');
-            $table->string('token', 64)->unique();
-            $table->text('abilities')->nullable();
-            $table->timestamp('last_used_at')->nullable();
-            $table->timestamp('expires_at')->nullable();
-            $table->timestamps();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('personal_access_tokens');
-    }
-};

+ 1 - 1
database/migrations/2023_01_26_032600_create_products_table.php

@@ -20,7 +20,7 @@ return new class extends Migration
             $table->string('name');
             $table->string('name_for_form')->default('');
             $table->string('product_group')->default('');
-            $table->string('price')->default('');
+            $table->unsignedBigInteger('price')->default(0);
             $table->text('characteristics');
             $table->text('tech_description');
             $table->text('tech_description_short');

+ 10 - 0
resources/sass/_custom.scss

@@ -0,0 +1,10 @@
+.prod-tr > td > a {
+    color: var(--bs-dark);
+}
+.prod-tr > td:hover > a {
+    color: var(--bs-primary);
+}
+
+.prod-tr:has(td:hover) {
+    background-color: rgba(220,224,255,0.85);
+}

+ 3 - 1
resources/sass/_variables.scss

@@ -1,7 +1,9 @@
 // Body
-$body-bg: #f8fafc;
+$body-bg: #ffffff;
 
 // Typography
 $font-family-sans-serif: 'Nunito', sans-serif;
 $font-size-base: 0.9rem;
 $line-height-base: 1.6;
+
+

+ 2 - 0
resources/sass/app.scss

@@ -6,3 +6,5 @@
 
 // Bootstrap
 @import 'bootstrap/scss/bootstrap';
+
+@import 'custom';

+ 0 - 73
resources/views/auth/login.blade.php

@@ -1,73 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Login') }}</div>
-
-                <div class="card-body">
-                    <form method="POST" action="{{ route('login') }}">
-                        @csrf
-
-                        <div class="row mb-3">
-                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
-
-                                @error('email')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
-
-                                @error('password')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <div class="col-md-6 offset-md-4">
-                                <div class="form-check">
-                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
-
-                                    <label class="form-check-label" for="remember">
-                                        {{ __('Remember Me') }}
-                                    </label>
-                                </div>
-                            </div>
-                        </div>
-
-                        <div class="row mb-0">
-                            <div class="col-md-8 offset-md-4">
-                                <button type="submit" class="btn btn-primary">
-                                    {{ __('Login') }}
-                                </button>
-
-                                @if (Route::has('password.request'))
-                                    <a class="btn btn-link" href="{{ route('password.request') }}">
-                                        {{ __('Forgot Your Password?') }}
-                                    </a>
-                                @endif
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 49
resources/views/auth/passwords/confirm.blade.php

@@ -1,49 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Confirm Password') }}</div>
-
-                <div class="card-body">
-                    {{ __('Please confirm your password before continuing.') }}
-
-                    <form method="POST" action="{{ route('password.confirm') }}">
-                        @csrf
-
-                        <div class="row mb-3">
-                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
-
-                                @error('password')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-0">
-                            <div class="col-md-8 offset-md-4">
-                                <button type="submit" class="btn btn-primary">
-                                    {{ __('Confirm Password') }}
-                                </button>
-
-                                @if (Route::has('password.request'))
-                                    <a class="btn btn-link" href="{{ route('password.request') }}">
-                                        {{ __('Forgot Your Password?') }}
-                                    </a>
-                                @endif
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 47
resources/views/auth/passwords/email.blade.php

@@ -1,47 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Reset Password') }}</div>
-
-                <div class="card-body">
-                    @if (session('status'))
-                        <div class="alert alert-success" role="alert">
-                            {{ session('status') }}
-                        </div>
-                    @endif
-
-                    <form method="POST" action="{{ route('password.email') }}">
-                        @csrf
-
-                        <div class="row mb-3">
-                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
-
-                                @error('email')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-0">
-                            <div class="col-md-6 offset-md-4">
-                                <button type="submit" class="btn btn-primary">
-                                    {{ __('Send Password Reset Link') }}
-                                </button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 65
resources/views/auth/passwords/reset.blade.php

@@ -1,65 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Reset Password') }}</div>
-
-                <div class="card-body">
-                    <form method="POST" action="{{ route('password.update') }}">
-                        @csrf
-
-                        <input type="hidden" name="token" value="{{ $token }}">
-
-                        <div class="row mb-3">
-                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
-
-                                @error('email')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
-
-                                @error('password')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
-                            </div>
-                        </div>
-
-                        <div class="row mb-0">
-                            <div class="col-md-6 offset-md-4">
-                                <button type="submit" class="btn btn-primary">
-                                    {{ __('Reset Password') }}
-                                </button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 77
resources/views/auth/register.blade.php

@@ -1,77 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Register') }}</div>
-
-                <div class="card-body">
-                    <form method="POST" action="{{ route('register') }}">
-                        @csrf
-
-                        <div class="row mb-3">
-                            <label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
-
-                                @error('name')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
-
-                                @error('email')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
-
-                                @error('password')
-                                    <span class="invalid-feedback" role="alert">
-                                        <strong>{{ $message }}</strong>
-                                    </span>
-                                @enderror
-                            </div>
-                        </div>
-
-                        <div class="row mb-3">
-                            <label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
-
-                            <div class="col-md-6">
-                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
-                            </div>
-                        </div>
-
-                        <div class="row mb-0">
-                            <div class="col-md-6 offset-md-4">
-                                <button type="submit" class="btn btn-primary">
-                                    {{ __('Register') }}
-                                </button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 28
resources/views/auth/verify.blade.php

@@ -1,28 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Verify Your Email Address') }}</div>
-
-                <div class="card-body">
-                    @if (session('resent'))
-                        <div class="alert alert-success" role="alert">
-                            {{ __('A fresh verification link has been sent to your email address.') }}
-                        </div>
-                    @endif
-
-                    {{ __('Before proceeding, please check your email for a verification link.') }}
-                    {{ __('If you did not receive the email') }},
-                    <form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
-                        @csrf
-                        <button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
-                    </form>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 23
resources/views/home.blade.php

@@ -1,23 +0,0 @@
-@extends('layouts.app')
-
-@section('content')
-<div class="container">
-    <div class="row justify-content-center">
-        <div class="col-md-8">
-            <div class="card">
-                <div class="card-header">{{ __('Dashboard') }}</div>
-
-                <div class="card-body">
-                    @if (session('status'))
-                        <div class="alert alert-success" role="alert">
-                            {{ session('status') }}
-                        </div>
-                    @endif
-
-                    {{ __('You are logged in!') }}
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 68 - 42
resources/views/products/index.blade.php

@@ -2,46 +2,68 @@
 
 @section('content')
     <div class="container-fluid">
-        <form class="" action="" method="get">
-            <div class="row my-2 justify-content-center">
-                <div class="col">
-                    <label class="form-label" for="s">Поиск</label>
-                    <input class="form-control form-control-sm" type="text" id="s" name="s" placeholder="Поиск">
-
-                </div>
-                <div class="col">
-                    <label class="form-label " for="series">Серия</label>
-                    <select class="form-select form-select-sm" name="s_series" id="series">
-                        @foreach($series as $ser)
-                            <option>{{ $ser->series }}</option>
-                        @endforeach
-                    </select>
-                </div>
-                <div class="col">
-                    <label class="form-label " for="price_min">Цена от</label>
-                    <input class="form-control form-control-sm" type="number" id="s_price_min" name="price_min">
-                </div>
-                <div class="col">
-                    <label class="form-label " for="price_max">Цена до</label>
-                    <input class="form-control form-control-sm" type="number" id="s_price_max" name="price_max">
-                </div>
-
-                <div class="col-1">
-                    <button id="sb" class="btn btn-primary mt-4" type="submit">Поиск</button>
-                </div>
+        <div class="row align-items-center">
+            <div class="col-5">
+                <div class="">Всего найдено: {{ $products->total() }}</div>
+                {{ $products->links() }}
+            </div>
+            <div class="col-7">
+                <form class="" action="" method="get">
+                    <div class="row my-2 justify-content-center">
+                        <div class="col">
+                            <label class="form-label" for="s">Поиск</label>
+                            <input class="form-control form-control-sm"
+                                   value="{{ $s ?? '' }}"
+                                   type="text" id="s" name="s" placeholder="Поиск">
+                        </div>
+                        <div class="col">
+                            <label class="form-label " for="series">Серия</label>
+                            <select class="form-select form-select-sm" name="s_series" id="series">
+                                <option value="">Все</option>
+                                @foreach($series as $ser)
+                                    <option {{ (isset($s_series) && ($ser->series == $s_series)) ? 'selected' : '' }} >{{ $ser->series }}</option>
+                                @endforeach
+                            </select>
+                        </div>
+                        <div class="col">
+                            <label class="form-label " for="price_min">Цена от</label>
+                            <input class="form-control form-control-sm"
+                                   value="{{ $s_price_min ?? '' }}"
+                                   type="number" id="price_min" name="s_price_min">
+                        </div>
+                        <div class="col">
+                            <label class="form-label " for="price_max">Цена до</label>
+                            <input class="form-control form-control-sm"
+                                   value="{{ $s_price_max ?? '' }}"
+                                   type="number" id="price_max" name="s_price_max">
+                        </div>
+                        <div class="col-auto">
+                            <label class="form-label" for="perpage">На странице</label>
+                            <select class="form-select form-select-sm" id="perpage" name="perpage">
+                                <option {{ (isset($perpage) && ($perpage == 20)) ? 'selected' : '' }} value="20">20
+                                </option>
+                                <option {{ (isset($perpage) && ($perpage == 100)) ? 'selected' : '' }} value="100">100
+                                </option>
+                                <option {{ (isset($perpage) && ($perpage == 10000)) ? 'selected' : '' }} value="10000">
+                                    Все
+                                </option>
+                            </select>
+                        </div>
+                        <div class="col-1">
+                            <button id="sb" class="btn btn-primary mt-4" type="submit">Поиск</button>
+                        </div>
+                    </div>
+                </form>
             </div>
-        </form>
+        </div>
 
         <div class="row">
             <div class="col-12">
-                <div class="d-flex">
-                    {{ $products->links() }}
-                </div>
                 <table class="table">
                     <thead>
                     <tr class="align-middle">
-                        <th>Артикул</th>
-                        <th>Серия</th>
+                        <th>Артикул, Серия</th>
+
                         <th>Группа <br> Наименование <br> Наименование под образец формы</th>
                         <th>Цена</th>
                         <th>Характеристики</th>
@@ -52,26 +74,31 @@
                     </tr>
                     <tbody>
                     @foreach($products as $product)
-                        <tr class="align-middle">
+                        <tr class="align-middle prod-tr">
                             <td class="text-center">
                                 <label>
                                     <input type="checkbox" class="form-check-inline me-0" name="prd_{{ $product->id }}"><br>
-                                    {{ $product->article }}
+                                    {{ $product->article }}<br>
+                                    {{ $product->series }}
                                 </label>
                             </td>
-                            <td>{{ $product->series }}</td>
-                            <td><strong>Группа:</strong> {{ $product->product_group }}<br>
-                                <strong>Наименование:</strong> {{ $product->name }}<br>
-                                <strong>Наименование под образец формы:</strong> {{ $product->name_for_form }}</td>
+
+                            <td>
+                                <a href="{{ route('view_product', $product->id) }}">
+                                    <strong>Группа:</strong> {{ $product->product_group }}<br>
+                                    <strong>Наименование:</strong> {{ $product->name }}<br>
+                                    <strong>Наименование под образец формы:</strong> {{ $product->name_for_form }}
+                                </a>
+                            </td>
                             <td>{!! number_format($product->price, 2, ',', '&nbsp') !!}</td>
                             <td>{!! nl2br($product->characteristics) !!}</td>
-                            <td>{!! Str::words(nl2br($product->tech_description), 50) !!}</td>
+                            <td>{!! Str::words(nl2br($product->tech_description), 70) !!}</td>
                             <td>{!! date('d.m.Y_H:i', strtotime($product->created_at)) . '<br>' .date('d.m.Y_H:i', strtotime($product->updated_at)) !!}</td>
                             <td class="text-center align-middle">
                                 @empty($product->image_path)
                                     Нет изображения
                                 @else
-                                    <img style="max-width: 400px"
+                                    <img class="img-fluid"
                                          src="{{ '/' . env('IMAGES_PATH', '/fill_images_path_in_env') . '/' . $product->image_path }}"
                                          alt="{{ $product->article }}">
                                 @endempty
@@ -87,5 +114,4 @@
         </div>
     </div>
 
-
 @endsection

+ 76 - 0
resources/views/products/product.blade.php

@@ -0,0 +1,76 @@
+@extends('layouts.app')
+
+@section('content')
+    <div class="container-fluid">
+        <div class="row">
+            <div class="col-sm-12 col-md-6">
+                <h3>Редактирование продукта <strong>{{ $product->name }}</strong></h3>
+                <form action="{{ route('save_product') }}" method="post">
+                    @csrf
+                    <div class="row mt-3">
+                        <div class="col-6">
+                            <label for="article" class="form-label">Артикул</label>
+                            <input class="form-control" id="article" value="{{ $product->article }}"
+                                   disabled>
+                            <input type="hidden" name="id" value="{{ $product->id }}">
+                        </div>
+                        <div class="col-6">
+                            <label for="series" class="form-label">Серия</label>
+                            <input class="form-control" name="series" id="series" value="{{ $product->series }}">
+                        </div>
+                    </div>
+
+                    <div class="row mt-3">
+                        <div class="col-6">
+                            <label for="name" class="form-label">Наименовение</label>
+                            <input class="form-control" name="name" id="name" value="{{ $product->name }}">
+                        </div>
+                        <div class="col-6">
+                            <label for="name_for_form" class="form-label">Наименование под образец формы</label>
+                            <input class="form-control" name="name_for_form" id="name_for_form"
+                                   value="{{ $product->name_for_form }}">
+                        </div>
+                    </div>
+
+                    <div class="row mt-3">
+                        <div class="col-6">
+                            <label for="product_group" class="form-label">Группа</label>
+                            <input class="form-control" name="product_group" id="product_group"
+                                   value="{{ $product->product_group }}">
+                        </div>
+                        <div class="col-6">
+                            <label for="price" class="form-label">Цена</label>
+                            <input type="number" class="form-control" name="price" id="price"
+                                   value="{{ $product->price }}">
+                        </div>
+                    </div>
+
+                    <label for="characteristics" class="form-label">Характеристики</label>
+                    <textarea class="form-control" name="characteristics" rows="4"
+                              id="characteristics">{{ $product->characteristics }}</textarea>
+
+                    <label for="tech_description" class="form-label">Техническое описание</label>
+                    <textarea class="form-control" name="tech_description" rows="6"
+                              id="tech_description">{{ $product->tech_description }}</textarea>
+
+                    <label for="tech_description_short" class="form-label">Техническое описание сокращенное без
+                        артикула</label>
+                    <textarea class="form-control" name="tech_description_short" rows="6"
+                              id="tech_description_short">{{ $product->tech_description_short }}</textarea>
+
+                    <div class="col-12 text-center mt-3">
+                        <button type="submit" class="btn btn-primary">Сохранить</button>
+                    </div>
+                </form>
+            </div>
+
+            <div class="col-sm-12 col-md-6">
+                <img src="{{ '/' . env('IMAGES_PATH', '/fill_images_path_in_env') . '/' . $product->image_path }}"
+                     alt="{{ $product->article }}" class="img-fluid">
+            </div>
+
+
+        </div>
+    </div>
+
+@endsection

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 13
resources/views/welcome.blade.php


+ 2 - 0
routes/web.php

@@ -17,7 +17,9 @@ use Illuminate\Support\Facades\Route;
 
 
 Route::get('/', [ProductController::class, 'index'])->name('index');
+Route::get('/product/{id}', [ProductController::class, 'product'])->name('view_product');
 Route::post('/upload_xls', [ProductController::class, 'upload'])->name('upload');
+Route::post('/save_product', [ProductController::class, 'save_product'])->name('save_product');
 
 
 //Auth::routes();

+ 30 - 0
todo.txt

@@ -0,0 +1,30 @@
++ импорт их xls в базу, распознавание по article
++ постраницчный вывод таблицы товаров, количество товаров на странице
++ фильры: общий поиск, серия, диапазон цен
++ редактирование товара (все поля, кроме изображения и артикула)
+
+
+
+
+todo Авторизация в системе без разделения ролей на базе «манагера».
+
+Примерное ТЗ.
+todo Авторизация в системе без разделения ролей на базе «манагера».
++Каталог товаров по данной таблице xlsx.
++Возможность обновлять и загружать новые товары из этой (или ее части) таблицы xlsx.(по аналогии с «манагером»). Товары должны быть разделены по сериям.
+
+* Все картинки загружаются на сайт в одну папку. Название файла картинки обязательно содержит артикул изделия, может состоять из любых символов и быть любой длины, но при этом в названии может присутствовать разделитель «точка», до которого происходит идентификация изделия.
+*** upd картинки берутся с сайта стройпрофит, если она есть в наличии - подтягиватеся, если нет, то сообщение об этом при импорте
+*** upd поиск картинки в каталоге работает так: ищем в начале имени фала артикул, если нет то ищем в начале имени фала транслит артикула (чтобы избежать проблем с русскими\латинскими символами в эксль файле)
+
+
+todo Должна присутствовать возможность сформировать документ в формате word:
+Выгрузка в docx формат выбранных товаров (просто галочками, серию или весь список).
+- как для одного изделия, так и для группы изделий (как из одной серии, так и из разных) ;
+- в отдельный файл для каждого изделия из выборки и в общий файл из выборки.
+
++Просмотр каталога постранично, поиск по каталогу, возможно фильтры (например по серии, цене и тп).
+
+Редактирование товара, возможность подгрузить фото.  Фото загружаю на сайт я(админ), файл на сайт загружаю я, иначе велик риск уничтожения всей информации.
+Выгрузка в xlsx.
++Дизайн - стандартный шаблон.

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff