update metronic 8.2
24
app/Actions/GetThemeType.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
class GetThemeType
|
||||||
|
{
|
||||||
|
public array $types = ['primary', 'success', 'info', 'danger', 'warning'];
|
||||||
|
|
||||||
|
public int $seed;
|
||||||
|
|
||||||
|
public function handle($format = '?', $seed = '')
|
||||||
|
{
|
||||||
|
$this->seed = crc32($seed);
|
||||||
|
|
||||||
|
return str_replace('?', $this->randomType(), $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function randomType()
|
||||||
|
{
|
||||||
|
srand($this->seed);
|
||||||
|
|
||||||
|
return $this->types[rand(0, count($this->types) - 1)] ?? '';
|
||||||
|
}
|
||||||
|
}
|
45
app/Core/KTBootstrap.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class KTBootstrap
|
||||||
|
{
|
||||||
|
// Init theme mode option from settings
|
||||||
|
public static function init()
|
||||||
|
{
|
||||||
|
KTBootstrap::initThemeMode();
|
||||||
|
KTBootstrap::initThemeDirection();
|
||||||
|
KTBootstrap::initLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init theme direction option (RTL or LTR) from settings
|
||||||
|
// Init RTL html attributes by checking if RTL is enabled.
|
||||||
|
// This function is being called for the html tag
|
||||||
|
|
||||||
|
public static function initThemeMode()
|
||||||
|
{
|
||||||
|
setModeSwitch(config('settings.KT_THEME_MODE_SWITCH_ENABLED'));
|
||||||
|
setModeDefault(config('settings.KT_THEME_MODE_DEFAULT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init layout html attributes and classes
|
||||||
|
|
||||||
|
public static function initThemeDirection()
|
||||||
|
{
|
||||||
|
setDirection(config('settings.KT_THEME_DIRECTION'));
|
||||||
|
|
||||||
|
if (isRtlDirection()) {
|
||||||
|
addHtmlAttribute('html', 'direction', 'rtl');
|
||||||
|
addHtmlAttribute('html', 'dir', 'rtl');
|
||||||
|
addHtmlAttribute('html', 'style', 'direction: rtl');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main initialization
|
||||||
|
|
||||||
|
public static function initLayout()
|
||||||
|
{
|
||||||
|
addHtmlAttribute('body', 'id', 'kt_app_body');
|
||||||
|
addHtmlAttribute('body', 'data-kt-name', getName());
|
||||||
|
}
|
||||||
|
}
|
85
app/DataTables/PermissionsDataTable.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DataTables;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Yajra\DataTables\EloquentDataTable;
|
||||||
|
use Yajra\DataTables\Html\Builder as HtmlBuilder;
|
||||||
|
use Yajra\DataTables\Html\Column;
|
||||||
|
use Yajra\DataTables\Services\DataTable;
|
||||||
|
|
||||||
|
class PermissionsDataTable extends DataTable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Build the DataTable class.
|
||||||
|
*
|
||||||
|
* @param QueryBuilder $query Results from query() method.
|
||||||
|
*/
|
||||||
|
public function dataTable(QueryBuilder $query): EloquentDataTable
|
||||||
|
{
|
||||||
|
return (new EloquentDataTable($query))
|
||||||
|
->editColumn('name', function (Permission $permission) {
|
||||||
|
return ucwords($permission->name);
|
||||||
|
})
|
||||||
|
->addColumn('assigned_to', function (Permission $permission) {
|
||||||
|
$roles = $permission->roles;
|
||||||
|
return view('pages.apps.user-management.permissions.columns._assign-to', compact('roles'));
|
||||||
|
})
|
||||||
|
->editColumn('created_at', function (Permission $permission) {
|
||||||
|
return $permission->created_at->format('d M Y, h:i a');
|
||||||
|
})
|
||||||
|
->addColumn('actions', function (Permission $permission) {
|
||||||
|
return view('pages.apps.user-management.permissions.columns._actions', compact('permission'));
|
||||||
|
})
|
||||||
|
->setRowId('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query source of dataTable.
|
||||||
|
*/
|
||||||
|
public function query(Permission $model): QueryBuilder
|
||||||
|
{
|
||||||
|
return $model->newQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional method if you want to use the html builder.
|
||||||
|
*/
|
||||||
|
public function html(): HtmlBuilder
|
||||||
|
{
|
||||||
|
return $this->builder()
|
||||||
|
->setTableId('permissions-table')
|
||||||
|
->columns($this->getColumns())
|
||||||
|
->minifiedAjax()
|
||||||
|
->dom('rt' . "<'row'<'col-sm-12 col-md-5'l><'col-sm-12 col-md-7'p>>",)
|
||||||
|
->addTableClass('table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer text-gray-600 fw-semibold')
|
||||||
|
->setTableHeadClass('text-start text-muted fw-bold fs-7 text-uppercase gs-0')
|
||||||
|
->orderBy(0)
|
||||||
|
->drawCallback("function() {" . file_get_contents(resource_path('views/pages//apps/user-management/permissions/columns/_draw-scripts.js')) . "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dataTable columns definition.
|
||||||
|
*/
|
||||||
|
public function getColumns(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Column::make('name'),
|
||||||
|
Column::make('assigned_to'),
|
||||||
|
Column::make('created_at')->addClass('text-nowrap'),
|
||||||
|
Column::computed('actions')
|
||||||
|
->addClass('text-end text-nowrap')
|
||||||
|
->exportable(false)
|
||||||
|
->printable(false),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filename for export.
|
||||||
|
*/
|
||||||
|
protected function filename(): string
|
||||||
|
{
|
||||||
|
return 'Permissions_' . date('YmdHis');
|
||||||
|
}
|
||||||
|
}
|
88
app/DataTables/UsersAssignedRoleDataTable.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DataTables;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UsersAssingedRole;
|
||||||
|
use Illuminate\Contracts\Database\Query\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
|
||||||
|
use Yajra\DataTables\EloquentDataTable;
|
||||||
|
use Yajra\DataTables\Html\Builder as HtmlBuilder;
|
||||||
|
use Yajra\DataTables\Html\Column;
|
||||||
|
use Yajra\DataTables\Services\DataTable;
|
||||||
|
|
||||||
|
class UsersAssignedRoleDataTable extends DataTable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Build the DataTable class.
|
||||||
|
*
|
||||||
|
* @param QueryBuilder $query Results from query() method.
|
||||||
|
*/
|
||||||
|
public function dataTable(QueryBuilder $query): EloquentDataTable
|
||||||
|
{
|
||||||
|
return (new EloquentDataTable($query))
|
||||||
|
->rawColumns(['user'])
|
||||||
|
->editColumn('user', function (User $user) {
|
||||||
|
return view('pages.apps.user-management.roles.columns._user', compact('user'));
|
||||||
|
})
|
||||||
|
->editColumn('created_at', function (User $user) {
|
||||||
|
return $user->created_at->format('d M Y, h:i a');
|
||||||
|
})
|
||||||
|
->addColumn('action', function (User $user) {
|
||||||
|
return view('pages.apps.user-management.roles.columns._actions', compact('user'));
|
||||||
|
})
|
||||||
|
->setRowId('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query source of dataTable.
|
||||||
|
*/
|
||||||
|
public function query(User $model): QueryBuilder
|
||||||
|
{
|
||||||
|
return $model->newQuery()->whereHas('roles', function (Builder $query) {
|
||||||
|
$query->where('role_id', $this->role->getKey());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional method if you want to use the html builder.
|
||||||
|
*/
|
||||||
|
public function html(): HtmlBuilder
|
||||||
|
{
|
||||||
|
return $this->builder()
|
||||||
|
->setTableId('usersassingedrole-table')
|
||||||
|
->columns($this->getColumns())
|
||||||
|
->minifiedAjax()
|
||||||
|
->dom('rt' . "<'row'<'col-sm-12 col-md-5'l><'col-sm-12 col-md-7'p>>",)
|
||||||
|
->addTableClass('table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer text-gray-600 fw-semibold')
|
||||||
|
->setTableHeadClass('text-start text-muted fw-bold fs-7 text-uppercase gs-0')
|
||||||
|
->orderBy(1)
|
||||||
|
->drawCallback("function() {" . file_get_contents(resource_path('views/pages//apps/user-management/users/columns/_draw-scripts.js')) . "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dataTable columns definition.
|
||||||
|
*/
|
||||||
|
public function getColumns(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Column::make('id'),
|
||||||
|
Column::make('user')->addClass('d-flex align-items-center')->name('name'),
|
||||||
|
Column::make('name'),
|
||||||
|
Column::make('created_at')->title('Joined Date')->addClass('text-nowrap'),
|
||||||
|
Column::computed('action')
|
||||||
|
->addClass('text-end text-nowrap')
|
||||||
|
->exportable(false)
|
||||||
|
->printable(false)
|
||||||
|
->width(60),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filename for export.
|
||||||
|
*/
|
||||||
|
protected function filename(): string
|
||||||
|
{
|
||||||
|
return 'UsersAssingedRole_' . date('YmdHis');
|
||||||
|
}
|
||||||
|
}
|
91
app/DataTables/UsersDataTable.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DataTables;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Yajra\DataTables\Html\Column;
|
||||||
|
use Yajra\DataTables\EloquentDataTable;
|
||||||
|
use Yajra\DataTables\Services\DataTable;
|
||||||
|
use Yajra\DataTables\Html\Builder as HtmlBuilder;
|
||||||
|
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
|
||||||
|
|
||||||
|
class UsersDataTable extends DataTable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Build the DataTable class.
|
||||||
|
*
|
||||||
|
* @param QueryBuilder $query Results from query() method.
|
||||||
|
*/
|
||||||
|
public function dataTable(QueryBuilder $query): EloquentDataTable
|
||||||
|
{
|
||||||
|
return (new EloquentDataTable($query))
|
||||||
|
->rawColumns(['user', 'last_login_at'])
|
||||||
|
->editColumn('user', function (User $user) {
|
||||||
|
return view('pages.apps.user-management.users.columns._user', compact('user'));
|
||||||
|
})
|
||||||
|
->editColumn('role', function (User $user) {
|
||||||
|
return ucwords($user->roles->first()?->name);
|
||||||
|
})
|
||||||
|
->editColumn('last_login_at', function (User $user) {
|
||||||
|
return sprintf('<div class="badge badge-light fw-bold">%s</div>', $user->last_login_at ? $user->last_login_at->diffForHumans() : $user->updated_at->diffForHumans());
|
||||||
|
})
|
||||||
|
->editColumn('created_at', function (User $user) {
|
||||||
|
return $user->created_at->format('d M Y, h:i a');
|
||||||
|
})
|
||||||
|
->addColumn('action', function (User $user) {
|
||||||
|
return view('pages.apps.user-management.users.columns._actions', compact('user'));
|
||||||
|
})
|
||||||
|
->setRowId('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query source of dataTable.
|
||||||
|
*/
|
||||||
|
public function query(User $model): QueryBuilder
|
||||||
|
{
|
||||||
|
return $model->newQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional method if you want to use the html builder.
|
||||||
|
*/
|
||||||
|
public function html(): HtmlBuilder
|
||||||
|
{
|
||||||
|
return $this->builder()
|
||||||
|
->setTableId('users-table')
|
||||||
|
->columns($this->getColumns())
|
||||||
|
->minifiedAjax()
|
||||||
|
->dom('rt' . "<'row'<'col-sm-12 col-md-5'l><'col-sm-12 col-md-7'p>>",)
|
||||||
|
->addTableClass('table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer text-gray-600 fw-semibold')
|
||||||
|
->setTableHeadClass('text-start text-muted fw-bold fs-7 text-uppercase gs-0')
|
||||||
|
->orderBy(2)
|
||||||
|
->drawCallback("function() {" . file_get_contents(resource_path('views/pages//apps/user-management/users/columns/_draw-scripts.js')) . "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dataTable columns definition.
|
||||||
|
*/
|
||||||
|
public function getColumns(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Column::make('user')->addClass('d-flex align-items-center')->name('name'),
|
||||||
|
Column::make('role')->searchable(false),
|
||||||
|
Column::make('last_login_at')->title('Last Login'),
|
||||||
|
Column::make('created_at')->title('Joined Date')->addClass('text-nowrap'),
|
||||||
|
Column::computed('action')
|
||||||
|
->addClass('text-end text-nowrap')
|
||||||
|
->exportable(false)
|
||||||
|
->printable(false)
|
||||||
|
->width(60)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filename for export.
|
||||||
|
*/
|
||||||
|
protected function filename(): string
|
||||||
|
{
|
||||||
|
return 'Users_' . date('YmdHis');
|
||||||
|
}
|
||||||
|
}
|
66
app/Http/Controllers/Apps/PermissionManagementController.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Apps;
|
||||||
|
|
||||||
|
use App\DataTables\PermissionsDataTable;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class PermissionManagementController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(PermissionsDataTable $dataTable)
|
||||||
|
{
|
||||||
|
return $dataTable->render('pages.apps.user-management.permissions.list');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
68
app/Http/Controllers/Apps/RoleManagementController.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Apps;
|
||||||
|
|
||||||
|
use App\DataTables\UsersAssignedRoleDataTable;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleManagementController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('pages.apps.user-management.roles.list');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(Role $role, UsersAssignedRoleDataTable $dataTable)
|
||||||
|
{
|
||||||
|
return $dataTable->with('role', $role)
|
||||||
|
->render('pages.apps.user-management.roles.show', compact('role'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(Role $role)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, Role $role)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(Role $role)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
67
app/Http/Controllers/Apps/UserManagementController.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Apps;
|
||||||
|
|
||||||
|
use App\DataTables\UsersDataTable;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class UserManagementController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(UsersDataTable $dataTable)
|
||||||
|
{
|
||||||
|
return $dataTable->render('pages.apps.user-management.users.list');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(User $user)
|
||||||
|
{
|
||||||
|
return view('pages.apps.user-management.users.show', compact('user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(User $user)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, User $user)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(User $user)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
64
app/Http/Controllers/Auth/AuthenticatedSessionController.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Auth\LoginRequest;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class AuthenticatedSessionController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the login view.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
addJavascriptFile('assets/js/custom/authentication/sign-in/general.js');
|
||||||
|
|
||||||
|
return view('pages.auth.login');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming authentication request.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Auth\LoginRequest $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(LoginRequest $request)
|
||||||
|
{
|
||||||
|
$request->authenticate();
|
||||||
|
|
||||||
|
$request->session()->regenerate();
|
||||||
|
|
||||||
|
$request->user()->update([
|
||||||
|
'last_login_at' => Carbon::now()->toDateTimeString(),
|
||||||
|
'last_login_ip' => $request->getClientIp()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->intended(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an authenticated session.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Request $request)
|
||||||
|
{
|
||||||
|
Auth::guard('web')->logout();
|
||||||
|
|
||||||
|
$request->session()->invalidate();
|
||||||
|
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
}
|
44
app/Http/Controllers/Auth/ConfirmablePasswordController.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class ConfirmablePasswordController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Show the confirm password view.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return view('pages.auth.confirm-password');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm the user's password.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
if (! Auth::guard('web')->validate([
|
||||||
|
'email' => $request->user()->email,
|
||||||
|
'password' => $request->password,
|
||||||
|
])) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'password' => __('auth.password'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->session()->put('auth.password_confirmed_at', time());
|
||||||
|
|
||||||
|
return redirect()->intended(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EmailVerificationNotificationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Send a new email verification notification.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
if ($request->user()->hasVerifiedEmail()) {
|
||||||
|
return redirect()->intended(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->user()->sendEmailVerificationNotification();
|
||||||
|
|
||||||
|
return back()->with('status', 'verification-link-sent');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EmailVerificationPromptController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the email verification prompt.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
return $request->user()->hasVerifiedEmail()
|
||||||
|
? redirect()->intended(RouteServiceProvider::HOME)
|
||||||
|
: view('pages.auth.verify-email');
|
||||||
|
}
|
||||||
|
}
|
67
app/Http/Controllers/Auth/NewPasswordController.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Auth\Events\PasswordReset;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Validation\Rules;
|
||||||
|
|
||||||
|
class NewPasswordController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the password reset view.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create(Request $request)
|
||||||
|
{
|
||||||
|
addJavascriptFile('assets/js/custom/authentication/reset-password/new-password.js');
|
||||||
|
|
||||||
|
return view('pages.auth.reset-password', ['request' => $request]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming new password request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'token' => ['required'],
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Here we will attempt to reset the user's password. If it is successful we
|
||||||
|
// will update the password on an actual user model and persist it to the
|
||||||
|
// database. Otherwise we will parse the error and return the response.
|
||||||
|
$status = Password::reset(
|
||||||
|
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||||
|
function ($user) use ($request) {
|
||||||
|
$user->forceFill([
|
||||||
|
'password' => Hash::make($request->password),
|
||||||
|
'remember_token' => Str::random(60),
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
event(new PasswordReset($user));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the password was successfully reset, we will redirect the user back to
|
||||||
|
// the application's home authenticated view. If there is an error we can
|
||||||
|
// redirect them back to where they came from with their error message.
|
||||||
|
return $status == Password::PASSWORD_RESET
|
||||||
|
? redirect()->route('login')->with('status', __($status))
|
||||||
|
: back()->withInput($request->only('email'))
|
||||||
|
->withErrors(['email' => __($status)]);
|
||||||
|
}
|
||||||
|
}
|
49
app/Http/Controllers/Auth/PasswordResetLinkController.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
|
class PasswordResetLinkController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the password reset link request view.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
addJavascriptFile('assets/js/custom/authentication/reset-password/reset-password.js');
|
||||||
|
|
||||||
|
return view('pages.auth.forgot-password');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming password reset link request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// We will send the password reset link to this user. Once we have attempted
|
||||||
|
// to send the link, we will examine the response then see the message we
|
||||||
|
// need to show to the user. Finally, we'll send out a proper response.
|
||||||
|
$status = Password::sendResetLink(
|
||||||
|
$request->only('email')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $status == Password::RESET_LINK_SENT
|
||||||
|
? back()->with('status', __($status))
|
||||||
|
: back()->withInput($request->only('email'))
|
||||||
|
->withErrors(['email' => __($status)]);
|
||||||
|
}
|
||||||
|
}
|
59
app/Http/Controllers/Auth/RegisteredUserController.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Validation\Rules;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Auth\Events\Registered;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
|
||||||
|
class RegisteredUserController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the registration view.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
addJavascriptFile('assets/js/custom/authentication/sign-up/general.js');
|
||||||
|
|
||||||
|
return view('pages.auth.register');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming registration request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||||
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = User::create([
|
||||||
|
'name' => $request->name,
|
||||||
|
'email' => $request->email,
|
||||||
|
'password' => Hash::make($request->password),
|
||||||
|
'last_login_at' => \Illuminate\Support\Carbon::now()->toDateTimeString(),
|
||||||
|
'last_login_ip' => $request->getClientIp()
|
||||||
|
]);
|
||||||
|
|
||||||
|
event(new Registered($user));
|
||||||
|
|
||||||
|
Auth::login($user);
|
||||||
|
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
}
|
54
app/Http/Controllers/Auth/SocialiteController.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\Events\Verified;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
|
||||||
|
class SocialiteController extends Controller
|
||||||
|
{
|
||||||
|
public function redirect($provider)
|
||||||
|
{
|
||||||
|
// redirect from social site
|
||||||
|
if (request()->input('state')) {
|
||||||
|
// already logged in
|
||||||
|
// get user info from social site
|
||||||
|
$user = Socialite::driver($provider)->stateless()->user();
|
||||||
|
|
||||||
|
// check for existing user
|
||||||
|
$existingUser = User::where('email', $user->getEmail())->first();
|
||||||
|
|
||||||
|
if ($existingUser) {
|
||||||
|
auth()->login($existingUser, true);
|
||||||
|
|
||||||
|
return redirect()->to('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newUser = $this->createUser($user);
|
||||||
|
auth()->login($newUser, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// request login from social site
|
||||||
|
return Socialite::driver($provider)->redirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createUser($user)
|
||||||
|
{
|
||||||
|
$user = User::updateOrCreate([
|
||||||
|
'email' => $user->getEmail(),
|
||||||
|
], [
|
||||||
|
'name' => $user->getName(),
|
||||||
|
'password' => '',
|
||||||
|
'avatar' => $user->getAvatar(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($user->markEmailAsVerified()) {
|
||||||
|
event(new Verified($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
30
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Auth\Events\Verified;
|
||||||
|
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||||
|
|
||||||
|
class VerifyEmailController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Mark the authenticated user's email address as verified.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Foundation\Auth\EmailVerificationRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function __invoke(EmailVerificationRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->user()->hasVerifiedEmail()) {
|
||||||
|
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->user()->markEmailAsVerified()) {
|
||||||
|
event(new Verified($request->user()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
|
||||||
|
}
|
||||||
|
}
|
80
app/Http/Livewire/Permission/PermissionModal.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Permission;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
|
||||||
|
class PermissionModal extends Component
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
public Permission $permission;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string',
|
||||||
|
];
|
||||||
|
|
||||||
|
// This is the list of listeners that this component listens to.
|
||||||
|
protected $listeners = [
|
||||||
|
'modal.show.permission_name' => 'mountPermission',
|
||||||
|
'delete_permission' => 'delete'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.permission.permission-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mountPermission($permission_name = '')
|
||||||
|
{
|
||||||
|
if (empty($permission_name)) {
|
||||||
|
// Create new
|
||||||
|
$this->permission = new Permission;
|
||||||
|
$this->name = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the role by name.
|
||||||
|
$permission = Permission::where('name', $permission_name)->first();
|
||||||
|
if (is_null($permission)) {
|
||||||
|
$this->emit('error', 'The selected permission [' . $permission_name . '] is not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->permission = $permission;
|
||||||
|
|
||||||
|
// Set the name and checked permissions properties to the role's values.
|
||||||
|
$this->name = $this->permission->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$this->permission->name = strtolower($this->name);
|
||||||
|
if ($this->permission->isDirty()) {
|
||||||
|
$this->permission->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a success event with a message indicating that the permissions have been updated.
|
||||||
|
$this->emit('success', 'Permission updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($name)
|
||||||
|
{
|
||||||
|
$permission = Permission::where('name', $name)->first();
|
||||||
|
|
||||||
|
if (!is_null($permission)) {
|
||||||
|
$permission->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->emit('success', 'Permission deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hydrate()
|
||||||
|
{
|
||||||
|
$this->resetErrorBag();
|
||||||
|
$this->resetValidation();
|
||||||
|
}
|
||||||
|
}
|
32
app/Http/Livewire/Permission/RoleList.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Permission;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleList extends Component
|
||||||
|
{
|
||||||
|
public array|Collection $roles;
|
||||||
|
|
||||||
|
protected $listeners = ['success' => 'updateRoleList'];
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$this->roles = Role::with('permissions')->get();
|
||||||
|
|
||||||
|
return view('livewire.permission.role-list');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateRoleList()
|
||||||
|
{
|
||||||
|
$this->roles = Role::with('permissions')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hydrate()
|
||||||
|
{
|
||||||
|
$this->resetErrorBag();
|
||||||
|
$this->resetValidation();
|
||||||
|
}
|
||||||
|
}
|
110
app/Http/Livewire/Permission/RoleModal.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Permission;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleModal extends Component
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
public $checked_permissions;
|
||||||
|
public $check_all;
|
||||||
|
|
||||||
|
public Role $role;
|
||||||
|
public Collection $permissions;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string',
|
||||||
|
];
|
||||||
|
|
||||||
|
// This is the list of listeners that this component listens to.
|
||||||
|
protected $listeners = ['modal.show.role_name' => 'mountRole'];
|
||||||
|
|
||||||
|
// This function is called when the component receives the `modal.show.role_name` event.
|
||||||
|
public function mountRole($role_name = '')
|
||||||
|
{
|
||||||
|
if (empty($role_name)) {
|
||||||
|
// Create new
|
||||||
|
$this->role = new Role;
|
||||||
|
$this->name = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the role by name.
|
||||||
|
$role = Role::where('name', $role_name)->first();
|
||||||
|
if (is_null($role)) {
|
||||||
|
$this->emit('error', 'The selected role [' . $role_name . '] is not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->role = $role;
|
||||||
|
|
||||||
|
// Set the name and checked permissions properties to the role's values.
|
||||||
|
$this->name = $this->role->name;
|
||||||
|
$this->checked_permissions = $this->role->permissions->pluck('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called when the component is mounted.
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
// Get all permissions.
|
||||||
|
$this->permissions = Permission::all();
|
||||||
|
|
||||||
|
// Set the checked permissions property to an empty array.
|
||||||
|
$this->checked_permissions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function renders the component's view.
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
// Create an array of permissions grouped by ability.
|
||||||
|
$permissions_by_group = [];
|
||||||
|
foreach ($this->permissions ?? [] as $permission) {
|
||||||
|
$ability = Str::after($permission->name, ' ');
|
||||||
|
|
||||||
|
$permissions_by_group[$ability][] = $permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the view with the permissions_by_group variable passed in.
|
||||||
|
return view('livewire.permission.role-modal', compact('permissions_by_group'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function submits the form and updates the role's permissions.
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$this->role->name = $this->name;
|
||||||
|
if ($this->role->isDirty()) {
|
||||||
|
$this->role->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync the role's permissions with the checked permissions property.
|
||||||
|
$this->role->syncPermissions($this->checked_permissions);
|
||||||
|
|
||||||
|
// Emit a success event with a message indicating that the permissions have been updated.
|
||||||
|
$this->emit('success', 'Permissions for ' . ucwords($this->role->name) . ' role updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function checks all of the permissions.
|
||||||
|
public function checkAll()
|
||||||
|
{
|
||||||
|
// If the check_all property is true, set the checked permissions property to all of the permissions.
|
||||||
|
if ($this->check_all) {
|
||||||
|
$this->checked_permissions = $this->permissions->pluck('name');
|
||||||
|
} else {
|
||||||
|
// Otherwise, set the checked permissions property to an empty array.
|
||||||
|
$this->checked_permissions = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hydrate()
|
||||||
|
{
|
||||||
|
$this->resetErrorBag();
|
||||||
|
$this->resetValidation();
|
||||||
|
}
|
||||||
|
}
|
137
app/Http/Livewire/User/AddUserModal.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\User;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithFileUploads;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
|
class AddUserModal extends Component
|
||||||
|
{
|
||||||
|
use WithFileUploads;
|
||||||
|
|
||||||
|
public $name;
|
||||||
|
public $email;
|
||||||
|
public $role;
|
||||||
|
public $avatar;
|
||||||
|
public $saved_avatar;
|
||||||
|
|
||||||
|
public $edit_mode = false;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string',
|
||||||
|
'email' => 'required|email',
|
||||||
|
'role' => 'required|string',
|
||||||
|
'avatar' => 'nullable|sometimes|image|max:1024',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $listeners = [
|
||||||
|
'delete_user' => 'deleteUser',
|
||||||
|
'update_user' => 'updateUser',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$roles = Role::all();
|
||||||
|
|
||||||
|
$roles_description = [
|
||||||
|
'administrator' => 'Best for business owners and company administrators',
|
||||||
|
'developer' => 'Best for developers or people primarily using the API',
|
||||||
|
'analyst' => 'Best for people who need full access to analytics data, but don\'t need to update business settings',
|
||||||
|
'support' => 'Best for employees who regularly refund payments and respond to disputes',
|
||||||
|
'trial' => 'Best for people who need to preview content data, but don\'t need to make any updates',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($roles as $i => $role) {
|
||||||
|
$roles[$i]->description = $roles_description[$role->name] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('livewire.user.add-user-modal', compact('roles'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
// Validate the form input data
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
DB::transaction(function () {
|
||||||
|
// Prepare the data for creating a new user
|
||||||
|
$data = [
|
||||||
|
'name' => $this->name,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->avatar) {
|
||||||
|
$data['profile_photo_path'] = $this->avatar->store('avatars', 'public');
|
||||||
|
} else {
|
||||||
|
$data['profile_photo_path'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->edit_mode) {
|
||||||
|
$data['password'] = Hash::make($this->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new user record in the database
|
||||||
|
$user = User::updateOrCreate([
|
||||||
|
'email' => $this->email,
|
||||||
|
], $data);
|
||||||
|
|
||||||
|
if ($this->edit_mode) {
|
||||||
|
// Assign selected role for user
|
||||||
|
$user->syncRoles($this->role);
|
||||||
|
|
||||||
|
// Emit a success event with a message
|
||||||
|
$this->emit('success', __('User updated'));
|
||||||
|
} else {
|
||||||
|
// Assign selected role for user
|
||||||
|
$user->assignRole($this->role);
|
||||||
|
|
||||||
|
// Send a password reset link to the user's email
|
||||||
|
Password::sendResetLink($user->only('email'));
|
||||||
|
|
||||||
|
// Emit a success event with a message
|
||||||
|
$this->emit('success', __('New user created'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset the form fields after successful submission
|
||||||
|
$this->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteUser($id)
|
||||||
|
{
|
||||||
|
// Prevent deletion of current user
|
||||||
|
if ($id == Auth::id()) {
|
||||||
|
$this->emit('error', 'User cannot be deleted');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the user record with the specified ID
|
||||||
|
User::destroy($id);
|
||||||
|
|
||||||
|
// Emit a success event with a message
|
||||||
|
$this->emit('success', 'User successfully deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateUser($id)
|
||||||
|
{
|
||||||
|
$this->edit_mode = true;
|
||||||
|
|
||||||
|
$user = User::find($id);
|
||||||
|
|
||||||
|
$this->saved_avatar = $user->profile_photo_url;
|
||||||
|
$this->name = $user->name;
|
||||||
|
$this->email = $user->email;
|
||||||
|
$this->role = $user->roles?->first()->name ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hydrate()
|
||||||
|
{
|
||||||
|
$this->resetErrorBag();
|
||||||
|
$this->resetValidation();
|
||||||
|
}
|
||||||
|
}
|
93
app/Http/Requests/Auth/LoginRequest.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Auth;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Events\Lockout;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class LoginRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => ['required', 'string', 'email'],
|
||||||
|
'password' => ['required', 'string'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to authenticate the request's credentials.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function authenticate()
|
||||||
|
{
|
||||||
|
$this->ensureIsNotRateLimited();
|
||||||
|
|
||||||
|
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
||||||
|
RateLimiter::hit($this->throttleKey());
|
||||||
|
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'email' => trans('auth.failed'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
RateLimiter::clear($this->throttleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the login request is not rate limited.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*/
|
||||||
|
public function ensureIsNotRateLimited()
|
||||||
|
{
|
||||||
|
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event(new Lockout($this));
|
||||||
|
|
||||||
|
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||||
|
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'email' => trans('auth.throttle', [
|
||||||
|
'seconds' => $seconds,
|
||||||
|
'minutes' => ceil($seconds / 60),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the rate limiting throttle key for the request.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function throttleKey()
|
||||||
|
{
|
||||||
|
return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
|
||||||
|
}
|
||||||
|
}
|
60
app/Models/User.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
use Spatie\Permission\Traits\HasRoles;
|
||||||
|
|
||||||
|
class User extends Authenticatable
|
||||||
|
{
|
||||||
|
use HasApiTokens, HasFactory, Notifiable;
|
||||||
|
use HasRoles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'email',
|
||||||
|
'password',
|
||||||
|
'last_login_at',
|
||||||
|
'last_login_ip',
|
||||||
|
'profile_photo_path',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that should be hidden for serialization.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $hidden = [
|
||||||
|
'password',
|
||||||
|
'remember_token',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that should be cast.
|
||||||
|
*
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
protected $casts = [
|
||||||
|
'email_verified_at' => 'datetime',
|
||||||
|
'last_login_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getProfilePhotoUrlAttribute()
|
||||||
|
{
|
||||||
|
if ($this->profile_photo_path) {
|
||||||
|
return asset('storage/' . $this->profile_photo_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->profile_photo_path;
|
||||||
|
}
|
||||||
|
}
|
75
config/breadcrumbs.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| View Name
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Choose a view to display when Breadcrumbs::render() is called.
|
||||||
|
| Built in templates are:
|
||||||
|
|
|
||||||
|
| - 'breadcrumbs::bootstrap5' - Bootstrap 5
|
||||||
|
| - 'breadcrumbs::bootstrap4' - Bootstrap 4
|
||||||
|
| - 'breadcrumbs::bulma' - Bulma
|
||||||
|
| - 'breadcrumbs::foundation6' - Foundation 6
|
||||||
|
| - 'breadcrumbs::json-ld' - JSON-LD Structured Data
|
||||||
|
| - 'breadcrumbs::materialize' - Materialize
|
||||||
|
| - 'breadcrumbs::tailwind' - Tailwind CSS
|
||||||
|
| - 'breadcrumbs::uikit' - UIkit
|
||||||
|
|
|
||||||
|
| Or a custom view, e.g. '_partials/breadcrumbs'.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'view' => 'breadcrumbs::bootstrap5',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Breadcrumbs File(s)
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The file(s) where breadcrumbs are defined. e.g.
|
||||||
|
|
|
||||||
|
| - base_path('routes/breadcrumbs.php')
|
||||||
|
| - glob(base_path('breadcrumbs/*.php'))
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'files' => base_path('routes/breadcrumbs.php'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Exceptions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Determine when to throw an exception.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// When route-bound breadcrumbs are used but the current route doesn't have a name (UnnamedRouteException)
|
||||||
|
'unnamed-route-exception' => true,
|
||||||
|
|
||||||
|
// When route-bound breadcrumbs are used and the matching breadcrumb doesn't exist (InvalidBreadcrumbException)
|
||||||
|
'missing-route-bound-breadcrumb-exception' => true,
|
||||||
|
|
||||||
|
// When a named breadcrumb is used but doesn't exist (InvalidBreadcrumbException)
|
||||||
|
'invalid-named-breadcrumb-exception' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Classes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Subclass the default classes for more advanced customisations.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Manager
|
||||||
|
'manager-class' => Diglactic\Breadcrumbs\Manager::class,
|
||||||
|
|
||||||
|
// Generator
|
||||||
|
'generator-class' => Diglactic\Breadcrumbs\Generator::class,
|
||||||
|
|
||||||
|
];
|
158
config/livewire.php
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Class Namespace
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the root namespace for Livewire component classes in
|
||||||
|
| your application. This value affects component auto-discovery and
|
||||||
|
| any Livewire file helper commands, like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
| After changing this item, run: `php artisan livewire:discover`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'class_namespace' => 'App\\Http\\Livewire',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| View Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path for Livewire component views. This affects
|
||||||
|
| file manipulation helper commands like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'view_path' => resource_path('views/livewire'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Layout
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| The default layout view that will be used when rendering a component via
|
||||||
|
| Route::get('/some-endpoint', SomeComponent::class);. In this case the
|
||||||
|
| the view returned by SomeComponent will be wrapped in "layouts.app"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'layout' => 'layouts.app',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Assets URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to Livewire JavaScript assets, for cases where
|
||||||
|
| your app's domain root is not the correct path. By default, Livewire
|
||||||
|
| will load its JavaScript assets from the app's "relative root".
|
||||||
|
|
|
||||||
|
| Examples: "/assets", "myurl.com/app".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'asset_url' => env('LIVEWIRE_ASSET_URL', null),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire App URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value should be used if livewire assets are served from CDN.
|
||||||
|
| Livewire will communicate with an app through this url.
|
||||||
|
|
|
||||||
|
| Examples: "https://my-app.com", "myurl.com/app".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'app_url' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Endpoint Middleware Group
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the middleware group that will be applied to the main
|
||||||
|
| Livewire "message" endpoint (the endpoint that gets hit everytime
|
||||||
|
| a Livewire component updates). It is set to "web" by default.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware_group' => 'web',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Temporary File Uploads Endpoint Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Livewire handles file uploads by storing uploads in a temporary directory
|
||||||
|
| before the file is validated and stored permanently. All file uploads
|
||||||
|
| are directed to a global endpoint for temporary storage. The config
|
||||||
|
| items below are used for customizing the way the endpoint works.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'temporary_file_upload' => [
|
||||||
|
'disk' => null, // Example: 'local', 's3' Default: 'default'
|
||||||
|
'rules' => null, // Example: ['file', 'mimes:png,jpg'] Default: ['required', 'file', 'max:12288'] (12MB)
|
||||||
|
'directory' => null, // Example: 'tmp' Default 'livewire-tmp'
|
||||||
|
'middleware' => null, // Example: 'throttle:5,1' Default: 'throttle:60,1'
|
||||||
|
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs.
|
||||||
|
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
|
||||||
|
'mov', 'avi', 'wmv', 'mp3', 'm4a',
|
||||||
|
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
|
||||||
|
],
|
||||||
|
'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated.
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Manifest File Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to the Livewire manifest file.
|
||||||
|
| The default should work for most cases (which is
|
||||||
|
| "<app_root>/bootstrap/cache/livewire-components.php"), but for specific
|
||||||
|
| cases like when hosting on Laravel Vapor, it could be set to a different value.
|
||||||
|
|
|
||||||
|
| Example: for Laravel Vapor, it would be "/tmp/storage/bootstrap/cache/livewire-components.php".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'manifest_path' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Back Button Cache
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines whether the back button cache will be used on pages
|
||||||
|
| that contain Livewire. By disabling back button cache, it ensures that
|
||||||
|
| the back button shows the correct state of components, instead of
|
||||||
|
| potentially stale, cached data.
|
||||||
|
|
|
||||||
|
| Setting it to "false" (default) will disable back button cache.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'back_button_cache' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Render On Redirect
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines whether Livewire will render before it's redirected
|
||||||
|
| or not. Setting it to "false" (default) will mean the render method is
|
||||||
|
| skipped when redirecting. And "true" will mean the render method is
|
||||||
|
| run before redirecting. Browsers bfcache can store a potentially
|
||||||
|
| stale view if render is skipped on redirect.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'render_on_redirect' => false,
|
||||||
|
|
||||||
|
];
|
40
database/factories/UserFactory.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||||
|
*/
|
||||||
|
class UserFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => fake()->name(),
|
||||||
|
'email' => fake()->unique()->safeEmail(),
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||||
|
'remember_token' => Str::random(10),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the model's email address should be unverified.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function unverified()
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'email_verified_at' => null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
37
database/migrations/2014_10_12_000000_create_users_table.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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->string('avatar')->nullable();
|
||||||
|
$table->rememberToken();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('users');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
<?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,0 +1,36 @@
|
|||||||
|
<?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,0 +1,37 @@
|
|||||||
|
<?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');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,29 @@
|
|||||||
|
<?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::table('users', function (Blueprint $table) {
|
||||||
|
$table->datetime('last_login_at')->nullable();
|
||||||
|
$table->string('last_login_ip')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->removeColumn('last_login_at');
|
||||||
|
$table->removeColumn('last_login_ip');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Spatie\Permission\PermissionRegistrar;
|
||||||
|
|
||||||
|
class CreatePermissionTables extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
$columnNames = config('permission.column_names');
|
||||||
|
$teams = config('permission.teams');
|
||||||
|
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
|
||||||
|
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::create($tableNames['permissions'], function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id'); // permission id
|
||||||
|
$table->string('name'); // For MySQL 8.0 use string('name', 125);
|
||||||
|
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
|
||||||
|
$table->bigIncrements('id'); // role id
|
||||||
|
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
|
||||||
|
}
|
||||||
|
$table->string('name'); // For MySQL 8.0 use string('name', 125);
|
||||||
|
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
|
||||||
|
$table->timestamps();
|
||||||
|
if ($teams || config('permission.testing')) {
|
||||||
|
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
|
||||||
|
} else {
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
|
||||||
|
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign(PermissionRegistrar::$pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
|
||||||
|
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign(PermissionRegistrar::$pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
|
||||||
|
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
|
||||||
|
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
|
||||||
|
|
||||||
|
$table->foreign(PermissionRegistrar::$pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->foreign(PermissionRegistrar::$pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
|
||||||
|
});
|
||||||
|
|
||||||
|
app('cache')
|
||||||
|
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
|
||||||
|
->forget(config('permission.cache.key'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::drop($tableNames['role_has_permissions']);
|
||||||
|
Schema::drop($tableNames['model_has_roles']);
|
||||||
|
Schema::drop($tableNames['model_has_permissions']);
|
||||||
|
Schema::drop($tableNames['roles']);
|
||||||
|
Schema::drop($tableNames['permissions']);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<?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::table('users', function (Blueprint $table) {
|
||||||
|
$table->string('profile_photo_path', 2048)->nullable()->after('email');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('profile_photo_path');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
72
database/seeders/RolesPermissionsSeeder.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RolesPermissionsSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$abilities = [
|
||||||
|
'read',
|
||||||
|
'write',
|
||||||
|
'create',
|
||||||
|
];
|
||||||
|
|
||||||
|
$permissions_by_role = [
|
||||||
|
'administrator' => [
|
||||||
|
'user management',
|
||||||
|
'content management',
|
||||||
|
'financial management',
|
||||||
|
'reporting',
|
||||||
|
'payroll',
|
||||||
|
'disputes management',
|
||||||
|
'api controls',
|
||||||
|
'database management',
|
||||||
|
'repository management',
|
||||||
|
],
|
||||||
|
'developer' => [
|
||||||
|
'api controls',
|
||||||
|
'database management',
|
||||||
|
'repository management',
|
||||||
|
],
|
||||||
|
'analyst' => [
|
||||||
|
'content management',
|
||||||
|
'financial management',
|
||||||
|
'reporting',
|
||||||
|
'payroll',
|
||||||
|
],
|
||||||
|
'support' => [
|
||||||
|
'reporting',
|
||||||
|
],
|
||||||
|
'trial' => [
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($permissions_by_role['administrator'] as $permission) {
|
||||||
|
foreach ($abilities as $ability) {
|
||||||
|
Permission::create(['name' => $ability . ' ' . $permission]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($permissions_by_role as $role => $permissions) {
|
||||||
|
$full_permissions_list = [];
|
||||||
|
foreach ($abilities as $ability) {
|
||||||
|
foreach ($permissions as $permission) {
|
||||||
|
$full_permissions_list[] = $ability . ' ' . $permission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Role::create(['name' => $role])->syncPermissions($full_permissions_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
User::find(1)->assignRole('administrator');
|
||||||
|
User::find(2)->assignRole('developer');
|
||||||
|
}
|
||||||
|
}
|
33
database/seeders/UsersSeeder.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Faker\Generator;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class UsersSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run(Generator $faker)
|
||||||
|
{
|
||||||
|
$demoUser = User::create([
|
||||||
|
'name' => $faker->name,
|
||||||
|
'email' => 'demo@demo.com',
|
||||||
|
'password' => Hash::make('demo'),
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$demoUser2 = User::create([
|
||||||
|
'name' => $faker->name,
|
||||||
|
'email' => 'admin@demo.com',
|
||||||
|
'password' => Hash::make('demo'),
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
17151
package-lock.json
generated
Normal file
14
public/vendor/livewire/livewire.js
vendored
Normal file
1
public/vendor/livewire/livewire.js.map
vendored
Normal file
1
public/vendor/livewire/manifest.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"/livewire.js":"/livewire.js?id=90730a3b0e7144480175"}
|
BIN
resources/_keenthemes/src/.DS_Store
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Class definition
|
||||||
|
var KTChartMap = function () {
|
||||||
|
// Charts widgets
|
||||||
|
var initChartMap = function() {
|
||||||
|
var element = document.getElementById("chartdiv");
|
||||||
|
|
||||||
|
if ( !element ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create root and chart
|
||||||
|
var root = am5.Root.new("chartdiv");
|
||||||
|
|
||||||
|
// Set themes
|
||||||
|
root.setThemes([
|
||||||
|
am5themes_Animated.new(root)
|
||||||
|
]);
|
||||||
|
|
||||||
|
var chart = root.container.children.push(
|
||||||
|
am5map.MapChart.new(root, {
|
||||||
|
panX: "rotateX",
|
||||||
|
projection: am5map.geoNaturalEarth1()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create polygon series
|
||||||
|
var polygonSeries = chart.series.push(
|
||||||
|
am5map.MapPolygonSeries.new(root, {
|
||||||
|
geoJSON: am5geodata_continentsLow,
|
||||||
|
exclude: ["antarctica"]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
polygonSeries.mapPolygons.template.setAll({
|
||||||
|
tooltipText: "{name}",
|
||||||
|
interactive: true,
|
||||||
|
templateField: "settings"
|
||||||
|
});
|
||||||
|
|
||||||
|
polygonSeries.mapPolygons.template.states.create("hover", {
|
||||||
|
fill: am5.color(0x677935)
|
||||||
|
});
|
||||||
|
|
||||||
|
var colors = am5.ColorSet.new(root, {});
|
||||||
|
|
||||||
|
polygonSeries.data.setAll([{
|
||||||
|
id: "europe",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.LinePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff),
|
||||||
|
rotation: 45,
|
||||||
|
strokeWidth: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "asia",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.RectanglePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff),
|
||||||
|
checkered: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "africa",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.CirclePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff),
|
||||||
|
checkered: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "northAmerica",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.CirclePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "southAmerica",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.LinePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff),
|
||||||
|
rotation: 90,
|
||||||
|
strokeWidth: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "oceania",
|
||||||
|
settings: {
|
||||||
|
fill: colors.next(),
|
||||||
|
fillPattern: am5.LinePattern.new(root, {
|
||||||
|
color: am5.color(0xffffff),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
|
||||||
|
|
||||||
|
// Init chart
|
||||||
|
initChart();
|
||||||
|
|
||||||
|
// Update chart on theme mode change
|
||||||
|
KTThemeMode.on("kt.thememode.change", function() {
|
||||||
|
if (chart.rendered) {
|
||||||
|
chart.self.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
initChart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Public methods
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
// Charts widgets
|
||||||
|
initChartMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Webpack support
|
||||||
|
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||||
|
module.exports = KTChartMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On document ready
|
||||||
|
KTUtil.onDOMContentLoaded(function() {
|
||||||
|
KTChartMap.init();
|
||||||
|
});
|
BIN
resources/_keenthemes/src/media/demo/1600x1200/1.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
resources/_keenthemes/src/media/demo/1600x1200/2.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
resources/_keenthemes/src/media/demo/1600x1200/3.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
resources/_keenthemes/src/media/demo/2600x1200/1.jpg
Normal file
After Width: | Height: | Size: 231 KiB |
BIN
resources/_keenthemes/src/media/features-logos/react-query.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
resources/_keenthemes/src/media/features-logos/vue-pania.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
resources/_keenthemes/src/media/misc/1.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/_keenthemes/src/media/misc/layout/aside-calendar.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.2 KiB |
BIN
resources/_keenthemes/src/media/misc/layout/aside-filters.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
resources/_keenthemes/src/media/misc/layout/aside-segments.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.2 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/1.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/10.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/100.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/101.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/102.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/103.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/104.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/105.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/106.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/107.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/108.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/109.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/11.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/110.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/111.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/112.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/113.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/114.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/115.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/116.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/117.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/118.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/119.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/12.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/120.png
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/121.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/122.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/123.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/124.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/125.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/126.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/127.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/128.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/129.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/13.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/130.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/131.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/132.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/133.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/134.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/135.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/136.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/137.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/138.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/139.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/14.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
resources/_keenthemes/src/media/stock/ecommerce/140.png
Normal file
After Width: | Height: | Size: 37 KiB |