Merge branch 'develop' into trunk

This commit is contained in:
Jérémy Munsch 2021-07-19 15:27:11 +02:00
commit 8096ebaa4a
42 changed files with 121 additions and 1044 deletions

View file

@ -36,6 +36,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\View;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use Maatwebsite\Excel\Excel as LExcel;
use Maatwebsite\Excel\Facades\Excel;
@ -82,7 +83,7 @@ class EmployeesController extends Controller
public function importExit()
{
$this->authorize('importExit', Employee::class);
self::shareCrudModelsClassesToView();
View::share('modelsClasses', static::getCrudModels());
return view('back.employees.import-exit');
}
@ -144,7 +145,7 @@ class EmployeesController extends Controller
public function import()
{
$this->authorize('import', Employee::class);
self::shareCrudModelsClassesToView();
View::share('modelsClasses', CrudController::getCrudModels());
return view('back.employees.import');
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use Illuminate\Support\Facades\View;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
class HomeController extends Controller
@ -10,7 +11,7 @@ class HomeController extends Controller
public function home()
{
CrudController::shareCrudModelsClassesToView();
View::share('modelsClasses', CrudController::getCrudModels());
return view('back.home');
}
}

View file

@ -7,6 +7,7 @@ use App\Imports\PlanningImport;
use App\Models\Planning;
use App\Models\Site;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\View;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\Excel as LExcel;
@ -27,7 +28,7 @@ class PlanningController extends Controller
public function import()
{
$this->authorize('import', Planning::class);
CrudController::shareCrudModelsClassesToView();
View::share('modelsClasses', CrudController::getCrudModels());
return view('back.plannings.import');
}

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Enums\Role;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
@ -167,12 +168,15 @@ class Site extends Model
'belongsTo' => Societe::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
CrudAction::view()
]
]
];
if (auth()->user() and auth()->user()->role->equals(Role::admin())) {
self::$editableProperties['societe_id']['actions'][] = CrudAction::create();
self::$editableProperties['societe_id']['actions'][] = CrudAction::update();
}
parent::__construct($attributes);
}

View file

@ -99,7 +99,18 @@ class User extends Authenticatable
'nullable' => false,
'validate' => [
'max:255',
'unique:users,email'
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
$q = User::query();
if ($this->id) {
$q = $q->where('id', '!=', $this->id);
}
if ($q->where('email', $value)->exists()) {
$fail(__(':attribute est déjà utilisé', [
'attribute' => $attribute
]));
}
}
],
'actions' => [
CrudAction::viewAny(),

View file

@ -2,9 +2,50 @@
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
use App\Policies\Rules\UserStaticRules as USR;
use App\Policies\Rules\UserRoleRules as URR;
use App\Policies\Rules\EmployeeStaticRules as SaSR;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
class EmployeePolicy extends AppPolicies
class EmployeePolicy extends RootPolicy
{
use HandlesAuthorization;
/**
* Init policies on call
*
* @param User $user
* @param Model $model
* @return void
*/
public function setPolicies(User $user, Model $model = null)
{
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'employee' => [
'viewAny' => USR::make('everyOne'),
'view' => StaticRules::or([
URR::make('userIsAdmin', $user),
SaSR::make('employeeIsOwnedByUser', $user, $model)
]),
'update' => StaticRules::or([
URR::make('userIsAdmin', $user),
SaSR::make('employeeIsOwnedByUser', $user, $model)
]),
'create' => USR::make('everyOne'),
'delete' => StaticRules::or([
URR::make('userIsAdmin', $user),
SaSR::make('employeeIsOwnedByUser', $user, $model)
]),
'import' => USR::make('everyOne'),
'importExit' => USR::make('everyOne')
]
];
}
}

View file

@ -12,7 +12,7 @@
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"htmlmin/htmlmin": "^8.0",
"kwaadpepper/crud-policies": "^0.0.1",
"kwaadpepper/crud-policies": "^0.1.1",
"kwaadpepper/enum": "^0.1.4",
"kwaadpepper/image-resizer": "^0.0.3",
"laravel/framework": "^8.12",

16
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0c397e537654e64d226ffcb97dce406b",
"content-hash": "24d9aa36e131e7867c72fa78c51cfcb5",
"packages": [
{
"name": "asm89/stack-cors",
@ -1683,16 +1683,16 @@
},
{
"name": "kwaadpepper/crud-policies",
"version": "0.0.1",
"version": "0.1.1",
"source": {
"type": "git",
"url": "https://github.com/Kwaadpepper/crud-policies.git",
"reference": "9fb83c3696f2e2bf5b9c06a4ed098b3d089843cc"
"reference": "cb3bce79d646c8530add808f04d4406ded910fc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/9fb83c3696f2e2bf5b9c06a4ed098b3d089843cc",
"reference": "9fb83c3696f2e2bf5b9c06a4ed098b3d089843cc",
"url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/cb3bce79d646c8530add808f04d4406ded910fc9",
"reference": "cb3bce79d646c8530add808f04d4406ded910fc9",
"shasum": ""
},
"require": {
@ -1710,7 +1710,7 @@
"extra": {
"laravel": {
"providers": [
"Kwaadpepper\\CrudPolicies\\CrudPoliciesServiceProvider"
"Kwaadpepper\\CrudPolicies\\Providers\\CrudPoliciesServiceProvider"
]
}
},
@ -1740,9 +1740,9 @@
],
"support": {
"issues": "https://github.com/Kwaadpepper/crud-policies/issues",
"source": "https://github.com/Kwaadpepper/crud-policies/tree/0.0.1"
"source": "https://github.com/Kwaadpepper/crud-policies/tree/0.1.1"
},
"time": "2021-07-14T23:54:49+00:00"
"time": "2021-07-19T13:20:33+00:00"
},
{
"name": "kwaadpepper/enum",

33
config/crud-policies.php Normal file
View file

@ -0,0 +1,33 @@
<?php
return [
/**
* The url prefix of Omen file manager
** default is 'crud-policies'
*! The url prefix must not match any file in public folder
*/
'urlPrefix' => 'crud-policies',
/**
* The public path where are stored
* crud-policies asset files
*
* Change this if you want to modify
* published assets folder under /public
** default is 'vendor/crud-policies'
*/
'assetPath' => 'vendor/crud-policies',
/**
* This is the package layout that will be used
* to display crud elements
*
* You can publish views to know what sections it
* has to display
*/
'viewLayout' => 'layouts.backend',
// Dont touch this
'isLoaded' => true
];

View file

@ -1,2 +1 @@
require('./bootstrap');
require('./crud');

View file

@ -1,112 +0,0 @@
<template>
<div>
<v-select
:id="name"
ref="select"
:loading="loading"
v-model="value"
:clearable="false"
label="label"
:options='data'
@search="search"
:filterable="true"
:disabled="dReadonly||dDisabled"
:searchable="true">
<li slot="list-footer" class="pagination">
<button
@click="$event.preventDefault(); page -= 1"
:disabled="!hasPrevPage"
class="btn btn-sm btn-primary mt-2 mb-2 ms-2 me-2">{{ __('Précédent') }}</button>
<button
@click="$event.preventDefault(); page += 1"
:disabled="!hasNextPage"
class="btn btn-sm btn-primary mt-2 mb-2">{{ __('Suivant') }}</button>
</li>
</v-select>
<input type="hidden" v-model="value.value" :name="name" />
</div>
</template>
<script>
export default {
mounted() {
this.dDisabled = !!this.disabled
this.dReadonly = !!this.readonly
this.value = JSON.parse(this.dbValue)
if(!this.value) {
this.value = {
label: null,
value: null
}
}
this.search(false)
},
props: [
'disabled',
'readonly',
'name',
'dbValue'
],
data() {
return {
dDisabled : false,
dReadonly : false,
value: {
label: null,
value: null
},
loading: false,
searchText: '',
data: [],
page: 1,
perPage: 15,
hasNextPage: true,
hasPrevPage: false
}
},
watch: {
page() {
if(this.page < 1) {
this.page = 1
}
if(this.page > 1) {
this.hasPrevPage = true
} else {
this.hasPrevPage = false
}
this.search()
},
data() {
if(this.data.length == this.perPage) {
this.hasNextPage = true
} else {
this.hasNextPage = false
}
}
},
methods: {
search (search = null) {
if(search !== false && search !== null) {
this.searchText = search
}
let init = search === false ? true : false
let route = this.route('model.index')
route += '?page='+this.page
route += '&s='+this.searchText
this.loading = true
axios.get(route)
.then(response => {
this.data = response.data
if (init && this.data.length && this.value.value == null) {
this.value = this.data[0]
}
})
.catch(error => {
console.log(error);
}).then(() => {
this.loading = false
})
},
}
}
</script>

View file

@ -1,108 +0,0 @@
<template>
<div>
<v-select
:id="name"
ref="select"
:loading="loading"
v-model="values"
:clearable="true"
label="label"
:options='data'
@search="search"
multiple
:disabled="dReadonly||dDisabled"
:filterable="true"
:searchable="true">
<li slot="list-footer" class="pagination">
<button
@click="$event.preventDefault(); page -= 1"
:disabled="!hasPrevPage"
class="btn btn-sm btn-primary mt-2 mb-2 ms-2 me-2">{{ __('Précédent') }}</button>
<button
@click="$event.preventDefault(); page += 1"
:disabled="!hasNextPage"
class="btn btn-sm btn-primary mt-2 mb-2">{{ __('Suivant') }}</button>
</li>
</v-select>
<input
type="hidden"
:name="name+'[]'"
v-for="item in values" :key="item.value"
:value="item.value"/>
</div>
</template>
<script>
export default {
mounted() {
this.dDisabled = !!this.disabled
this.dReadonly = !!this.readonly
let values = JSON.parse(this.dbValue)
for(let k in values) {
this.values.push(values[k])
}
this.search(false)
},
props: [
'disabled',
'readonly',
'name',
'dbValue'
],
data() {
return {
dDisabled : false,
dReadonly : false,
values: [],
loading: false,
searchText: '',
data: [],
page: 1,
perPage: 15,
hasNextPage: true,
hasPrevPage: false
}
},
watch: {
page() {
if(this.page < 1) {
this.page = 1
}
if(this.page > 1) {
this.hasPrevPage = true
} else {
this.hasPrevPage = false
}
this.search()
},
data() {
if(this.data.length == this.perPage) {
this.hasNextPage = true
} else {
this.hasNextPage = false
}
}
},
methods: {
search (search = null) {
if(search !== false && search !== null) {
this.searchText = search
}
let init = search === false ? true : false
let route = this.route('model.index')
route += '?page='+this.page
route += '&s='+this.searchText
this.loading = true
axios.get(route)
.then(response => {
this.data = response.data
})
.catch(error => {
console.log(error);
}).then(() => {
this.loading = false
})
},
}
}
</script>

21
resources/js/crud.js vendored
View file

@ -1,21 +0,0 @@
require('./vuejs')
window.__CRUD = {
confirmDelete: (e) => {
let self = e.target
e.preventDefault();
Swal.fire({
title: 'Confirmez',
text: 'Voulez-vous vraiment supprimer cet élément ?',
icon: 'question',
confirmButtonText: 'Supprimer',
showCancelButton: true,
cancelButtonText: 'Annuler',
confirmButtonColor: '#dc3545'
}).then((result) => {
if (result.isConfirmed) {
self.submit()
}
})
}
}

View file

@ -1,3 +1,2 @@
@import "variables";
@import "classes";
@import "crud";

View file

@ -1,12 +0,0 @@
@import "vue-select/src/scss/vue-select.scss";
// readonly checkboxes
select[readonly],input[type='checkbox'][readonly]{
pointer-events: none;
}
//bootstrap overload
.btn-group > form:not(:first-child) > .btn-group:not(:last-child) > .btn, .btn-group > form:not(:first-child) > .btn:last-child:not(.dropdown-toggle) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">{{ __('Salaries - Import Fichier de Sortie Employés') }}</div>
<div class="card-body">
<form action="{{ route('bo.employees.importExitSubmit') }}" method="POST" enctype="multipart/form-data">

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">{{ __('Salaries - Import') }}</div>
<div class="card-body">
<form action="{{ route('bo.employees.importSubmit') }}" method="POST" enctype="multipart/form-data">

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">{{ __('Back office') }}</div>
<div class="card-body">
<h4>Portail Salarie</h4>

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">{{ __('Plannings - Import') }}</div>
<div class="card-body">
<form action="{{ route('bo.plannings.importSubmit') }}" method="POST" enctype="multipart/form-data">

View file

@ -1,47 +0,0 @@
@extends('layouts.backend')
@section('title', sprintf('%s - Ajouter', Str::plural(class_basename($modelClass))))
@section('description', sprintf('Ajouter un %s', class_basename($modelClass)))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card">
@include('modules.flashMessage')
<div class="card-header">
@can('viewAny', $modelClass)
<a href="{{route("bo.$modelTable.index")}}">{{ trans(Str::plural(class_basename($modelClass))) }}</a>&nbsp;-&nbsp;{{ __('Ajouter') }}
@else
{{ Str::plural(class_basename($modelClass)) }}&nbsp;-&nbsp;{{ __('Ajouter') }}
@endcan
</div>
<div class="card-body">
<form action="{{ route("bo.$modelTable.store") }}" method="POST" @if($hasImage) enctype="multipart/form-data" @endif>
@csrf
<table class="table table-sm table-bordered border-primary align-middle table-striped text-center">
<thead class="thead-dark">
<tr>
<th scope="col" class="w-25">{{ __('Propriété') }}</th>
<th scope="col" class="w-75">{{ __('Valeur') }}</th>
</tr>
</thead>
<tbody>
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@php
// create form cannot accept readonly field
$prop['readonly'] = false;
@endphp
@if(in_array(CrudAction::create(), $prop['actions']))
<tr>
@include('crud.formfield')
</tr>
@endif
@endforeach
</tbody>
</table>
<div class="col text-center">
<button class="btn btn-info" type="submit">{{ __('Créer') }}</button>
</div>
</form>
</div>
</div>
@endsection

View file

@ -1,56 +0,0 @@
@extends('layouts.backend')
@section('title', sprintf('%s - Editer', trans(Str::plural(class_basename($modelClass)))))
@section('description', sprintf('Editer un %s', trans(class_basename($modelClass))))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card">
@include('modules.flashMessage')
<div class="card-header">
@can('viewAny', $modelClass)
<a href="{{route("bo.$modelTable.index")}}">{{ trans(Str::plural(class_basename($modelClass))) }}</a>&nbsp;-&nbsp;{{ __('Editer') }}
@else
{{ trans(Str::plural(class_basename($modelClass))) }}&nbsp;-&nbsp;{{ __('Editer') }}
@endcan
<div class="btn-group float-end" role="group" aria-label="{{ __('Actions') }}">
@can('view', $model)
<a href="{{route("bo.$modelTable.show", $model)}}" class="btn btn-primary ">{{ __('Voir') }}</a>
@endcan
@can('delete', $model)
<form action="{{route("bo.$modelTable.destroy", $model)}}" method="POST" onsubmit="__CRUD.confirmDelete(event)">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">{{ __('Supprimer') }}</button>
</form>
@endcan
</div>
</div>
<div class="card-body">
<form action="{{ route("bo.$modelTable.update", $model) }}" method="POST" @if($hasImage) enctype="multipart/form-data" @endif>
@method('put')
@csrf
<table class="table table-sm table-bordered border-primary align-middle table-striped text-center">
<thead class="thead-dark">
<tr>
<th scope="col" class="w-25">{{ __('Propriété') }}</th>
<th scope="col" class="w-75">{{ __('Valeur') }}</th>
</tr>
</thead>
<tbody>
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@if(in_array(CrudAction::update(), $prop['actions']))
<tr>
@include('crud.formfield')
</tr>
@endif
@endforeach
</tbody>
</table>
<div class="col text-center">
<button class="btn btn-info" type="submit">{{ __('Modifier') }}</button>
</div>
</form>
</div>
</div>
@endsection

View file

@ -1,41 +0,0 @@
@php
$readonly = is_callable($prop['readonly']) ? $prop['readonly']() : $prop['readonly'];
@endphp
@switch($prop['type'])
@case(CrudType::password())
@include('crud.formfields.password')
@break
@case(CrudType::boolean())
@include('crud.formfields.boolean')
@break
@case(CrudType::int())
@include('crud.formfields.int')
@break
@case(CrudType::float())
@include('crud.formfields.float')
@break
@case(CrudType::email())
@include('crud.formfields.email')
@break
@case(CrudType::text())
@include('crud.formfields.text')
@break
@case(CrudType::json())
@include('crud.formfields.json')
@break
@case(CrudType::image())
@include('crud.formfields.image')
@break
@case(CrudType::enum())
@include('crud.formfields.enum')
@break
@case(CrudType::belongsTo())
@include('crud.formfields.belongsTo')
@break
@case(CrudType::belongsToMany())
@include('crud.formfields.belongsToMany')
@break
@case(CrudType::string())
@default
@include('crud.formfields.string')
@endswitch

View file

@ -1,30 +0,0 @@
@php
$related = isset($model) ? $props[$fieldName]['belongsTo']::find($model->{$fieldName}) : null;
if($related) {
$related = [
'label' => $related->crudLabel,
'value' => $related->crudValue
];
}
@endphp
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<belongs-to
readonly="{{ $readonly }}"
name="{{ $fieldName }}"
db-value='@json($related)'>
</belongs-to>
</td>
@push('scriptsConstants')
<script>
window._locale = '{{ app()->getLocale() }}';
window._translations = @json(cache(sprintf('translations.%s', app()->getLocale())));
window._asset = '{{ asset('') }}';
window._routes = {
'model.index': '{{ route('bo.'.(new $props[$fieldName]['belongsTo'])->getTable().'.index') }}'
};
</script>
@endpush

View file

@ -1,29 +0,0 @@
@php
$related = isset($model) ? $model->{$fieldName}->mapWithKeys(function ($model) use($prop) {
return [$model->{'id'} => [
'label' => $model->{$prop['belongsToManyLabel']},
'value' => $model->{'id'}
]];
}) : [];
@endphp
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<belongs-to-many
readonly="{{ $readonly }}"
name="{{ $fieldName }}"
db-value='@json($related)'>
</belongs-to-many>
</td>
@push('scriptsConstants')
<script>
window._locale = '{{ app()->getLocale() }}';
window._translations = @json(cache(sprintf('translations.%s', app()->getLocale())));
window._asset = '{{ asset('') }}';
window._routes = {
'model.index': '{{ route('bo.'.(new $props[$fieldName]['belongsToMany'])->getTable().'.index') }}'
};
</script>
@endpush

View file

@ -1,20 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<label for="{{ $fieldName }}" class="row">
<div class="col">
<div class="form-check form-switch ms-2">
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="checkbox"
class="ml-2 form-check-input"
value="1"
@if(old($fieldName) ?? ((isset($model) and $model->{$fieldName}) ? $model->{$fieldName} : false))checked @endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif>
</div>
</div>
</label>
</td>

View file

@ -1,33 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
@if(strlen($prop['placeholder']))
<div class="form-floating">
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="email"
class="form-control"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? '' }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
<label for="{{ $fieldName }}" class="form-label">{{ __('ex: :placeholder', ['placeholder' => $prop['placeholder']]) }}</label>
</div>
@else
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="email"
class="form-control"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? '' }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
@endif
</td>

View file

@ -1,13 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<select id="{{ $fieldName }}" class="form-control" name="{{ $fieldName }}" @if($prop['readonly']) readonly @endif @if($prop['disabled']) disabled @endif>
{{-- TODO handle enum nullable ? --}}
{{-- @if($prop['nullable'])
@endif --}}
@foreach ($prop['enum']::toArray() as $enum)
<option value="{{$enum->value}}" @if(isset($model) and $enum->equals($model->{$fieldName}))selected @endif>{{__($enum->label)}}</option>
@endforeach
</select>
</td>

View file

@ -1,18 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<input
id="{{ $fieldName }}"
class="form-control"
name="{{ $fieldName }}"
type="number"
placeholder="{{ $prop['rules']['min'] }}"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? $prop['rules']['min'] }}"
@if(isset($prop['rules']['max']))max="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))min="{{ $prop['rules']['min'] }}"@endif
@if(isset($prop['rules']['step']))step="{{ $prop['rules']['step'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
</td>

View file

@ -1,26 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
@if(strlen($prop['placeholder']))
<div class="form-floating">
@endif
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="file"
class="form-control"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? '' }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if(($prop['required'] or (!$prop['required'] and !$prop['nullable'])) and !(isset($model) and $model->{$fieldName}))required @endif>
@if(strlen($prop['placeholder']))
<label for="{{ $fieldName }}" class="form-label">{{ __('ex: :placeholder', ['placeholder' => $prop['placeholder']]) }}</label>
</div>
@endif
@if(isset($model) and $model->{$fieldName})
<img class="img-responsive" src="{{ sprintf('/storage/%s', $model->{$fieldName}) }}" alt="{{ sprintf('/storage/%s', $model->{$fieldName}) }}">
@endif
</td>

View file

@ -1,18 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<input
id="{{ $fieldName }}"
class="form-control"
name="{{ $fieldName }}"
type="number"
placeholder="{{ $prop['rules']['min'] }}"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? $prop['rules']['min'] }}"
@if(isset($prop['rules']['max']))max="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))min="{{ $prop['rules']['min'] }}"@endif
@if(isset($prop['rules']['step']))step="{{ $prop['rules']['step'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
</td>

View file

@ -1,22 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<div class="mb-3">
<textarea id="{{ $fieldName }}" name="{{ $fieldName }}" rows="3" class="form-control" @if($readonly) readonly @endif>{{
old($fieldName) ?? ((isset($model) and $model->{$fieldName}) ? (is_array($model->{$fieldName}) ? json_encode($model->{$fieldName}) : $model->{$fieldName}) : '')
}}</textarea>
</div>
</td>
@push('scripts')
<script src="https://cdn.ckeditor.com/ckeditor5/27.1.0/classic/ckeditor.js"></script>
<script>
ClassicEditor
.create(
document.querySelector( '#{{ $fieldName }}' )
)
.catch( error => {
console.error( error );
} );
</script>
@endpush

View file

@ -1,67 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
@if(strlen($prop['placeholder']))
<div class="form-floating">
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="password"
class="form-control"
placeholder="{{ $prop['placeholder'] }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or !$prop['nullable'])required @endif>
<label for="{{ $fieldName }}" class="form-label">{{ __('ex: :placeholder', ['placeholder' => $prop['placeholder']]) }}</label>
</div>
@else
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="password"
class="form-control"
placeholder="{{ $prop['placeholder'] }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or !$prop['nullable'])required @endif>
@endif
</td>
</tr><tr>
<td>
<label for="{{ $fieldName }}-confirm" class="form-label">{{ __('Confirmation')}}&nbsp;{{ $prop['label'] }}</label>
</td>
<td>
@if(strlen($prop['placeholder']))
<div class="form-floating">
<input
id="{{ $fieldName }}-confirm"
name="{{ $fieldName }}-confirm"
type="password"
class="form-control"
placeholder="{{ $prop['placeholder'] }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or !$prop['nullable'])required @endif>
<label for="{{ $fieldName }}-confirm" class="form-label">{{ __('ex: :placeholder', ['placeholder' => $prop['placeholder']]) }}</label>
</div>
@else
<input
id="{{ $fieldName }}-confirm"
name="{{ $fieldName }}-confirm"
type="password"
class="form-control"
placeholder="{{ $prop['placeholder'] }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or !$prop['nullable'])required @endif>
@endif
</td>

View file

@ -1,33 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
@if(strlen($prop['placeholder']))
<div class="form-floating">
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="text"
class="form-control"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? '' }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
<label for="{{ $fieldName }}" class="form-label">{{ __('ex: :placeholder', ['placeholder' => $prop['placeholder']]) }}</label>
</div>
@else
<input
id="{{ $fieldName }}"
name="{{ $fieldName }}"
type="text"
class="form-control"
value="{{ old($fieldName) ?? $model->{$fieldName} ?? '' }}"
@if(isset($prop['rules']['max']))maxlength="{{ $prop['rules']['max'] }}"@endif
@if(isset($prop['rules']['min']))minlength="{{ $prop['rules']['min'] }}"@endif
@if($prop['disabled'])disabled @endif
@if($readonly)readonly @endif
@if($prop['required'] or (!$prop['required'] and !$prop['nullable']))required @endif>
@endif
</td>

View file

@ -1,22 +0,0 @@
<td>
<label for="{{ $fieldName }}" class="form-label">{{ $prop['label'] }}</label>
</td>
<td>
<div class="mb-3">
<textarea id="{{ $fieldName }}" name="{{ $fieldName }}" rows="3" class="form-control" @if($readonly) readonly @endif>{{
old($fieldName) ?? $model->{$fieldName} ?? ''
}}</textarea>
</div>
</td>
@push('scripts')
<script src="https://cdn.ckeditor.com/ckeditor5/27.1.0/classic/ckeditor.js"></script>
<script>
ClassicEditor
.create(
document.querySelector( '#{{ $fieldName }}' )
)
.catch( error => {
console.error( error );
} );
</script>
@endpush

View file

@ -1,105 +0,0 @@
@extends('layouts.backend')
@section('title', sprintf('%s - Liste', trans(Str::plural(class_basename($modelClass)))))
@section('description', sprintf('Liste des %s', trans(Str::plural(class_basename($modelClass)))))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card">
@include('modules.flashMessage')
<div class="card-header">
<a href="{{route("bo.$modelTable.index")}}">{{ trans(Str::plural(class_basename($modelClass))) }}</a>&nbsp;-&nbsp;{{ __('Liste') }}
@can('create', $modelClass)
<a href="{{route("bo.$modelTable.create")}}" class="btn btn-primary float-end">{{ __('Ajouter') }}</a>
@endcan
</div>
<div class="card-body">
<div class="row justify-content-center">
<div class="col">
{{ $models->links() }}
</div>
<div class="col">
<form action="{{ url()->current() }}">
<div class="input-group mb-3">
<input type="search" name="search" class="form-control" value="{{ request()->search }}"/>
<input type="text" name="sort_col" class="d-none" value="{{ request()->sort_col }}"/>
<input type="text" name="sort_way" class="d-none" value="{{ request()->sort_way }}"/>
<a class="btn btn-danger" href="{{ url()->current() }}">Réinitialiser</a>
<button type="submit" class="btn btn-primary">
{{ __('Recherche') }}
</button>
</div>
</div>
</form>
</div>
</div>
<table class="table text-center">
<thead class="thead-dark">
<tr>
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@if(in_array(CrudAction::viewAny(), $prop['actions']))
@switch($prop['type'])
@case(CrudType::password())
{{-- Ignore --}}
@break
@default
<th scope="col">
{{ $prop['label'] }}
@include('crud.modules.sorter', ['colname' => $fieldName])
</th>
@endswitch
@endif
@endforeach
</tr>
</thead>
<tbody>
@if(count($models))
@foreach($models as $model)
<tr>
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@if(in_array(CrudAction::viewAny(), $prop['actions']))
@switch($prop['type'])
@case(CrudType::password())
{{-- Ignore --}}
@break
@default
<td>@include('crud.printfield', [
'action' => CrudAction::viewAny(),
'field' => $model->{$fieldName},
'props' => $prop
])</td>
@endswitch
@endif
@endforeach
<td>
<div class="btn-group" role="group" aria-label="{{ __('Actions') }}">
@can('view', $model)
<a href="{{route("bo.$modelTable.show", $model)}}" class="btn btn-sm btn-primary">{{ __('Voir') }}</a>
@endcan
@can('update', $model)
<a href="{{route("bo.$modelTable.edit", $model)}}" class="btn btn-sm btn-info">{{ __('Éditer') }}</a>
@endcan
@can('delete', $model)
<form action="{{route("bo.$modelTable.destroy", $model)}}" method="POST" onsubmit="__CRUD.confirmDelete(event)">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger">{{ __('Supprimer') }}</button>
</form>
@endcan
</div>
</td>
</tr>
@endforeach
@else
<tr>
<td colspan="{{ count($modelClass::getEditableProperties()) }}">
Rien à afficher
</td>
</tr>
@endif
</tbody>
</table>
{{ $models->links() }}
</div>
</div>
@endsection

View file

@ -1,12 +0,0 @@
@php
$tableName = (new $modelClass())->getTable();
$sortParams = ['sort_col' => $colname];
$sortParams = request()->search ?
array_merge($sortParams, ['search' => request()->search]) : $sortParams;
@endphp
@if(!session("crud.$tableName.sort_way") or session("crud.$tableName.sort_col") !== $colname or (session("crud.$tableName.sort_way") === 'desc' and session("crud.$tableName.sort_col") === $colname))
<a class="col_sort text-secondary lead asc" href="{{ route("bo.$tableName.index", array_merge($sortParams, ['sort_way' => 'asc'])) }}">&nbsp;&darr;&nbsp;</a>
@endif
@if(!session("crud.$tableName.sort_way") or session("crud.$tableName.sort_col") !== $colname or (session("crud.$tableName.sort_way") === 'asc' and session("crud.$tableName.sort_col") === $colname))
<a class="col_sort text-secondary lead desc" href="{{ route("bo.$tableName.index", array_merge($sortParams, ['sort_way' => 'desc'])) }}">&nbsp;&uarr;&nbsp;</a>
@endif

View file

@ -1,57 +0,0 @@
@switch($action)
@case(CrudAction::viewAny())
@switch($props['type'])
@case(CrudType::email())
<a href="mailto:$field">{{ $field }}</a>
@break
@case(CrudType::enum())
{{ $model->{$fieldName}->label }}
@break
@case(CrudType::belongsTo())
{{ $prop['belongsTo']::find($model->{$fieldName})->{$model->crudLabelColumn} }}
@break
@case(CrudType::belongsToMany())
{{ $model->{$fieldName}->implode($prop['belongsToManyLabel'], ',') }}
@break
@case(CrudType::boolean())
@if($model->{$fieldName})OUI @else NON @endif
@break
@case(CrudType::image())
<img class="img-responsive" src="{{ sprintf('/storage/%s', $model->{$fieldName}) }}" alt="{{ sprintf('/storage/%s', $model->{$fieldName}) }}">
@break
@default
{{ $field }}
@endswitch
@break
@case(CrudAction::view())
@switch($props['type'])
@case(CrudType::email())
<a href="mailto:$field">{{ $field }}</a>
@break
@case(CrudType::enum())
{{ $model->{$fieldName}->label }}
@break
@case(CrudType::json())
@json($field)
@break
@case(CrudType::boolean())
@if($model->{$fieldName})OUI @else NON @endif
@break
@case(CrudType::image())
<img class="img-responsive" src="{{ sprintf('/storage/%s', $model->{$fieldName}) }}" alt="{{ sprintf('/storage/%s', $model->{$fieldName}) }}">
@break
@case(CrudType::belongsTo())
{{ $prop['belongsTo']::find($model->{$fieldName})->{$model->crudLabelColumn} }}
@break
@case(CrudType::belongsToMany())
{{ $model->{$fieldName}->implode($prop['belongsToManyLabel'], ',') }}
@break
@default
{{ $field }}
@endswitch
@break
@default
@dd('unknown action')
@endswitch

View file

@ -1,60 +0,0 @@
@extends('layouts.backend')
@section('title', sprintf('%s - Voir', trans(Str::plural(class_basename($modelClass)))))
@section('description', sprintf('Voir un %s', trans(class_basename($modelClass))))
@section('metaIndex', 'noindex,nofollow')
@section('content')
<div class="card">
@include('modules.flashMessage')
<div class="card-header">
@can('viewAny', $modelClass)
<a href="{{route("bo.$modelTable.index")}}">{{ trans(Str::plural(class_basename($modelClass))) }}</a>&nbsp;-&nbsp;{{ __('Voir') }}
@else
{{ trans(Str::plural(class_basename($modelClass))) }}&nbsp;-&nbsp;{{ __('Voir') }}
@endcan
<div class="btn-group float-end" role="group" aria-label="{{ __('Actions') }}">
@can('update', $model)
<a href="{{route("bo.$modelTable.edit", $model)}}" class="btn btn-info ">{{ __('Modifier') }}</a>
@endcan
@can('delete', $model)
<form action="{{route("bo.$modelTable.destroy", $model)}}" method="POST" onsubmit="__CRUD.confirmDelete(event)">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">{{ __('Supprimer') }}</button>
</form>
@endcan
</div>
</div>
<div class="card-body">
<table class="table table-sm table-bordered border-primary align-top table-striped text-center">
<thead class="thead-dark">
<tr>
<th scope="col" class="w-25">{{ __('Propriété') }}</th>
<th scope="col" class="w-75">{{ __('Valeur') }}</th>
</tr>
</thead>
<tbody>
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@if(in_array(CrudAction::view(), $prop['actions']))
@switch($prop['type'])
@case(CrudType::password())
{{-- Ignore --}}
@break
@default
<tr>
<td>{{ $prop['label'] }}</td>
<td>@include('crud.printfield', [
'action' => CrudAction::view(),
'field' => $model->{$fieldName},
'props' => $prop
])</td>
</tr>
@endswitch
@endif
@endforeach
</tbody>
</table>
</div>
</div>
@endsection

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">
<a href="{{route("bo.$modelTable.index")}}">{{ Str::plural(class_basename($modelClass)) }}</a>&nbsp;-&nbsp;{{ __('Editer') }}</a>
<div class="btn-group float-end" role="group" aria-label="{{ __('Actions') }}">
@ -34,7 +34,7 @@
@foreach ($modelClass::getEditableProperties() as $fieldName => $prop)
@if(in_array(CrudAction::update(), $prop['actions']))
<tr>
@include('crud.formfield')
@include('crud-policies::crud.formfield')
</tr>
@endif
@endforeach
@ -50,14 +50,5 @@
</form>
</div>
</div>
@include('crud-policies::crud.modules.scriptConstants')
@endsection
@push('scriptsConstants')
<script>
window._locale = '{{ app()->getLocale() }}';
window._translations = @json(cache(sprintf('translations.%s', app()->getLocale())));
window._asset = '{{ asset('') }}';
window._routes = {
};
</script>
@endpush

View file

@ -6,7 +6,7 @@
@section('content')
<div class="card">
@include('modules.flashMessage')
@include('crud-policies::crud.modules.flashMessage')
<div class="card-header">
<a href="{{route("bo.$modelTable.index")}}">{{ Str::plural(class_basename($modelClass)) }}</a>&nbsp;-&nbsp;{{ __('Voir') }}</a>
<div class="btn-group float-end" role="group" aria-label="{{ __('Actions') }}">
@ -33,7 +33,7 @@
@default
<tr>
<td>{{ $prop['label'] }}</td>
<td>@include('crud.printfield', [
<td>@include('crud-policies::crud.printfield', [
'action' => CrudAction::view(),
'field' => $model->{$fieldName},
'props' => $prop

View file

@ -3,6 +3,7 @@
<head>
@include('layouts.backend.head')
@stack('styles')
@crudPoliciesCSS()
</head>
<body>
<div id="app">
@ -29,5 +30,6 @@
<script src="//cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<script src="{{ asset('js/preload-back.js') }}"></script>
@stack('scripts')
@crudPoliciesJS()
</body>
</html>

View file

@ -1,44 +0,0 @@
@if ($message = Session::get('success'))
<div class="alert alert-success alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<strong>{!! nl2br(e($message)) !!}</strong>
</div>
@endif
@if ($message = Session::get('error'))
<div class="alert alert-danger alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<strong>{!! nl2br(e($message)) !!}</strong>
</div>
@endif
@if ($message = Session::get('warning'))
<div class="alert alert-warning alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<strong>{!! nl2br(e($message)) !!}</strong>
</div>
@endif
@if ($message = Session::get('info'))
<div class="alert alert-info alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<strong>{!! nl2br(e($message)) !!}</strong>
</div>
@endif
@if ($errors->any())
<div class="alert alert-danger alert-dismissible fade show">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
@if ($errors->any())
<ul class="list-unstyled">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
</div>
@endif