Procházet zdrojové kódy

Merge remote-tracking branch 'origin/master'

= před 3 měsíci
rodič
revize
e5e85d9d14

+ 47 - 0
app/Http/Controllers/AdminController.php

@@ -23,6 +23,7 @@ use Ramsey\Uuid\Uuid;
 use App\Models\Lab2;
 use App\Models\GuestData;
 use App\Models\TestResult;
+use App\Models\LoginAttempt;
 use Illuminate\Support\Facades\DB;
 
 class AdminController extends Controller
@@ -688,4 +689,50 @@ class AdminController extends Controller
         $testResults = TestResult::whereIn('store_order_id', $ordersIDS)->whereRaw('is_deleted IS NOT TRUE')->orderBy('created_at', 'DESC')->get();
         return view('app.my-account.admin.orders.sub.test-results', compact('order', 'testResults'));
     }
+
+    
+    public function verificationTokens(Request $request){
+        $filters = $request->all();
+        $records = LoginAttempt::query();
+        $query = $request->get('query');
+
+        if($query){
+            $wildCardedQuery = '%' . $query . '%';
+            $records = $records->where(function($qry)use($wildCardedQuery){
+                $qry->where('email', 'ilike', $wildCardedQuery)
+                ->orWhere('pin', 'ilike', $wildCardedQuery)
+                ->orWhere('name_first', 'ilike', $wildCardedQuery)
+                ->orWhere('name_last', 'ilike', $wildCardedQuery)
+                ->orWhere('full_name', 'ilike', $wildCardedQuery)
+                ->orWhere('phone_number', 'ilike', $wildCardedQuery);
+            });
+        }
+
+        $records = $records->orderBy('created_at', 'DESC')->paginate(30);
+        return view('app.my-account.admin.verification-tokens', compact('records', 'filters'));
+    }
+
+    public function usersVerificationTokens(Request $request){
+        $filters = $request->all();
+        $records = User::query();
+        $query = $request->get('query');
+
+        if($query){
+            $wildCardedQuery = '%' . $query . '%';
+            $records = $records->where(function($qry)use($wildCardedQuery){
+                $qry->where('email', 'ilike', $wildCardedQuery)
+                ->orWhere('email_confirmation_token', 'ilike', $wildCardedQuery)
+                ->orWhere('google_login_email', 'ilike', $wildCardedQuery)
+                ->orWhere('name_first', 'ilike', $wildCardedQuery)
+                ->orWhere('name_last', 'ilike', $wildCardedQuery)
+                ->orWhere('full_name', 'ilike', $wildCardedQuery)
+                ->orWhere('phone_number', 'ilike', $wildCardedQuery);
+            });
+        }
+
+        $records = $records->orderBy('created_at', 'DESC')->paginate(30);
+        return view('app.my-account.admin.users-verification-tokens', compact('records', 'filters'));
+    }
+
+    
 }

+ 30 - 0
app/Models/LoginAttempt.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use App\Models\BaseModel;
+
+class LoginAttempt extends BaseModel
+{
+    use HasFactory;
+
+    protected $table = 'login_or_signup_attempt';
+
+    public $timestamps = false;
+
+    public function getRouteKeyName()
+    {
+        return 'uid';
+    }
+
+    public function displayName()
+    {
+        if ($this->full_name) return $this->full_name;
+        if($this->name_first || $this->name_last){
+           return $this->name_first . ' ' . $this->name_last; 
+        }
+        return $this->email;
+    }
+}

+ 4 - 1
app/Models/User.php

@@ -65,7 +65,10 @@ class User extends BaseModel
     public function displayName()
     {
         if ($this->full_name) return $this->full_name;
-        return $this->name_first . ' ' . $this->name_last;
+        if($this->name_first || $this->name_last){
+           return $this->name_first . ' ' . $this->name_last; 
+        }
+        return $this->getEmail();
     }
 
     public function getName()

+ 96 - 0
resources/views/app/my-account/admin/login-attempts/filters.blade.php

@@ -0,0 +1,96 @@
+<?php
+$url = route('admin.verification-tokens');
+?>
+<style>
+    #recordsFilter label {
+        font-weight: bold;
+    }
+
+    #recordsFilter .mw-100px {
+        min-width: 100px;
+    }
+
+    .filter-container {
+        display: flex;
+        align-items: flex-start;
+        flex-wrap: wrap;
+    }
+
+    .filter-container>div {
+        /* width: 165px; */
+    }
+
+    .filter-container>div:not(:last-child) {
+        margin-right: 15px;
+    }
+
+    .filter-container .select2-container {
+        max-width: 150px;
+    }
+</style>
+<form id="recordsFilter" method="GET" action="{{ $url }}" class="filter-container align-items-end mb-3" v-cloak>
+    <div class="border p-2">
+        <h6 class="mb-1"><b>Query:</b></h6>
+        <div class="d-flex flex-wrap justify-content-start">
+            <div class="form-group mb-0 me-2">
+                <label class="text-secondary text-sm mb-1">Query</label>
+                <input name="query"
+                    class="d-block p-1 border border-secondary max-width-110px bg-white width-100px"
+                    v-model="filters.query" />
+            </div>
+            
+        </div>
+    </div>
+
+    <div class="">
+        <div class="form-group mb-0">
+            <label>&nbsp;</label>
+            <div class="d-flex align-items-center">
+                <button type="submit" v-on:click.prevent="doSubmit()"
+                    class="btn btn-sm btn-primary btn-sm mr-2">Filter</button>
+                <a href="#" v-on:click.prevent="fastLoad('{{ $url }}')"
+                    class="btn btn-link btn-sm text-danger">Clear Filters</a>
+            </div>
+        </div>
+    </div>
+</form>
+
+<?php
+$loadedFilters = $filters;
+$allFilterKeys = ['query'];
+for ($i = 0; $i < count($allFilterKeys); $i++) {
+    if (!isset($loadedFilters[$allFilterKeys[$i]]) || !$loadedFilters[$allFilterKeys[$i]]) {
+        $loadedFilters[$allFilterKeys[$i]] = '';
+    }
+}
+?>
+
+<script>
+    $(document).ready(function() {
+        new Vue({
+            el: '#recordsFilter',
+            delimiters: ['@{{ ', ' }}'],
+            data: {
+                filters: <?= json_encode($loadedFilters) ?>
+            },
+            methods: {
+                doSubmit: function() {
+                    fastLoad('{{ $url }}?' + $('#recordsFilter').serialize());
+                    return false;
+                },
+                initSelect2: function() {
+                    $('[select2]').select2({
+                        width: '150px'
+                    });
+                },
+                init: function() {
+                    this.initSelect2();
+
+                }
+            },
+            mounted: function() {
+                this.init();
+            },
+        });
+    });
+</script>

+ 46 - 0
resources/views/app/my-account/admin/users-verification-tokens.blade.php

@@ -0,0 +1,46 @@
+@extends('app.my-account.layout-lite')
+@section('page')
+    <div class="px-3">
+        <div class="d-flex align-items-baseline border-bottom px-3 py-2 mb-2 m-neg-3 bg-light">
+            <h2 class="font-size-16 text-secondary fw-bold m-0 me-3">Users Verification Pins</h2>
+        </div>
+        @include('app.my-account.admin.users.partials.verification-tokens-filters')
+        <div class="table-responsive">
+            <table class="table table-sm table-hover table-striped table-bordered mb-0">
+                <thead class="fw-bold text-secondary bg-light">
+                    <tr>
+                        <th>Created At</th>
+                        <th>Email Address</th>
+                        <th>Name</th>
+                        <th>Token</th>
+                        <th>Phone</th>
+                        <th>Pin Verified</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @foreach ($records as $record)
+                        <tr>
+                            <td>{{ friendly_date($record->created_at ?? $record->updated_at) }}</td>
+                            <td>{{ $record->email }}</td>
+                            <td>{{ $record->displayName() }}</td>
+                            <td>{{ $record->email_confirmation_token }}</td>
+                            <td>{{ $record->phone_number }}</td>
+                            </td>
+                            <td>
+                                @if($record->is_email_confirmed)
+                                    <span class="text-success">YES</span>
+                                @else
+                                    <span class="text-danger">NO</span>
+                                @endif
+                            </td>
+                        </tr>
+                    @endforeach
+                </tbody>
+            </table>
+        </div>
+
+        <div class="mt-3">
+            {{ $records->appends(request()->input())->links() }}
+        </div>
+    </div>
+@endsection

+ 96 - 0
resources/views/app/my-account/admin/users/partials/verification-tokens-filters.blade.php

@@ -0,0 +1,96 @@
+<?php
+$url = route('admin.users-verification-tokens');
+?>
+<style>
+    #recordsFilter label {
+        font-weight: bold;
+    }
+
+    #recordsFilter .mw-100px {
+        min-width: 100px;
+    }
+
+    .filter-container {
+        display: flex;
+        align-items: flex-start;
+        flex-wrap: wrap;
+    }
+
+    .filter-container>div {
+        /* width: 165px; */
+    }
+
+    .filter-container>div:not(:last-child) {
+        margin-right: 15px;
+    }
+
+    .filter-container .select2-container {
+        max-width: 150px;
+    }
+</style>
+<form id="recordsFilter" method="GET" action="{{ $url }}" class="filter-container align-items-end mb-3" v-cloak>
+    <div class="border p-2">
+        <h6 class="mb-1"><b>Query:</b></h6>
+        <div class="d-flex flex-wrap justify-content-start">
+            <div class="form-group mb-0 me-2">
+                <label class="text-secondary text-sm mb-1">Query</label>
+                <input name="query"
+                    class="d-block p-1 border border-secondary max-width-110px bg-white width-100px"
+                    v-model="filters.query" />
+            </div>
+            
+        </div>
+    </div>
+
+    <div class="">
+        <div class="form-group mb-0">
+            <label>&nbsp;</label>
+            <div class="d-flex align-items-center">
+                <button type="submit" v-on:click.prevent="doSubmit()"
+                    class="btn btn-sm btn-primary btn-sm mr-2">Filter</button>
+                <a href="#" v-on:click.prevent="fastLoad('{{ $url }}')"
+                    class="btn btn-link btn-sm text-danger">Clear Filters</a>
+            </div>
+        </div>
+    </div>
+</form>
+
+<?php
+$loadedFilters = $filters;
+$allFilterKeys = ['query'];
+for ($i = 0; $i < count($allFilterKeys); $i++) {
+    if (!isset($loadedFilters[$allFilterKeys[$i]]) || !$loadedFilters[$allFilterKeys[$i]]) {
+        $loadedFilters[$allFilterKeys[$i]] = '';
+    }
+}
+?>
+
+<script>
+    $(document).ready(function() {
+        new Vue({
+            el: '#recordsFilter',
+            delimiters: ['@{{ ', ' }}'],
+            data: {
+                filters: <?= json_encode($loadedFilters) ?>
+            },
+            methods: {
+                doSubmit: function() {
+                    fastLoad('{{ $url }}?' + $('#recordsFilter').serialize());
+                    return false;
+                },
+                initSelect2: function() {
+                    $('[select2]').select2({
+                        width: '150px'
+                    });
+                },
+                init: function() {
+                    this.initSelect2();
+
+                }
+            },
+            mounted: function() {
+                this.init();
+            },
+        });
+    });
+</script>

+ 48 - 0
resources/views/app/my-account/admin/verification-tokens.blade.php

@@ -0,0 +1,48 @@
+@extends('app.my-account.layout-lite')
+@section('page')
+    <div class="px-3">
+        <div class="d-flex align-items-baseline border-bottom px-3 py-2 mb-2 m-neg-3 bg-light">
+            <h2 class="font-size-16 text-secondary fw-bold m-0 me-3">Login Attempt Verification Pins</h2>
+        </div>
+        @include('app.my-account.admin.login-attempts.filters')
+        <div class="table-responsive">
+            <table class="table table-sm table-hover table-striped table-bordered mb-0">
+                <thead class="fw-bold text-secondary bg-light">
+                    <tr>
+                        <th>Created At</th>
+                        <th>Email Address</th>
+                        <th>Name</th>
+                        <th>Token</th>
+                        <th>Phone</th>
+                        <th>Last Sent</th>
+                        <th>Pin Verified</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @foreach ($records as $record)
+                        <tr>
+                            <td>{{ friendly_date($record->created_at ?? $record->updated_at) }}</td>
+                            <td>{{ $record->email }}</td>
+                            <td>{{ $record->displayName() }}</td>
+                            <td>{{ $record->pin }}</td>
+                            <td>{{ $record->phone_number }}</td>
+                            <td>{{ $record->pin_last_updated_at ? friendly_date($record->pin_last_updated_at) : '---' }}
+                            </td>
+                            <td>
+                                @if($record->was_pin_validation_successful)
+                                    <span class="text-success">YES</span>
+                                @else
+                                    <span class="text-danger">NO</span>
+                                @endif
+                            </td>
+                        </tr>
+                    @endforeach
+                </tbody>
+            </table>
+        </div>
+
+        <div class="mt-3">
+            {{ $records->appends(request()->input())->links() }}
+        </div>
+    </div>
+@endsection

+ 2 - 0
resources/views/app/my-account/nav/admin.blade.php

@@ -13,3 +13,5 @@
 <a class="nav-link px-2 py-1 me-2 {{ $currentTab == 'user-events' ? 'bg-secondary text-white rounded':'' }}" href="{{ route('admin.user-events') }}">User Events</a>
 <a class="nav-link px-2 py-1 me-2 {{ $currentTab == 'abandoned-carts' ? 'bg-secondary text-white rounded':'' }}" href="{{ route('admin.abandoned-carts') }}">Abandoned Carts</a>
 <a class="nav-link px-2 py-1 me-2 {{ $currentTab == 'guests-data' ? 'bg-secondary text-white rounded':'' }}" href="{{ route('admin.guests-data') }}">Guests Data</a>
+<a class="nav-link px-2 py-1 me-2 {{ $currentTab == 'verification-tokens' ? 'bg-secondary text-white rounded':'' }}" href="{{ route('admin.verification-tokens') }}">Login Attempt Verification Tokens</a>
+<a class="nav-link px-2 py-1 me-2 {{ $currentTab == 'users-verification-tokens' ? 'bg-secondary text-white rounded':'' }}" href="{{ route('admin.users-verification-tokens') }}">User Verification Tokens</a>

+ 2 - 0
routes/web.php

@@ -105,6 +105,8 @@ Route::group(['middleware' => ['ensureUserLoggedIn']], function () {
             Route::get('/user-events', [AdminController::class, 'userEvents'])->name('.user-events');
             Route::post('/email-attachment', [AdminController::class, 'emailAttachment'])->name('.email-attachment');
             Route::get('/guests-data', [AdminController::class, 'guestsData'])->name('.guests-data');
+            Route::get('/verification-tokens', [AdminController::class, 'verificationTokens'])->name('.verification-tokens');
+            Route::get('/users-verification-tokens', [AdminController::class, 'usersVerificationTokens'])->name('.users-verification-tokens');
             Route::get('/abandoned-carts', [AdminController::class, 'abandonedCarts'])->name('.abandoned-carts');
         });