fix: some fixes after local testing

This commit is contained in:
Noah Lancien 2025-06-17 12:29:15 +02:00
parent 3b93c4c49b
commit 73615cdf1f
20 changed files with 145 additions and 80 deletions

View file

@ -142,7 +142,7 @@ class TenantController extends Controller
return DB::transaction(function () use ($tenant, $previousQueries) {
if ($tenant->deleteOrFail()) {
/** @var string $tenantKey */
$tenantKey = \tenant()->getKey();
$tenantKey = $tenant->getKey();
Storage::disk('public')->deleteDirectory("modelfiles/{$tenantKey}");
Storage::disk('public')->deleteDirectory("docs/{$tenantKey}");
return redirect()

View file

@ -50,7 +50,8 @@ class StoreAppUserRequest extends FormRequest
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
Rule::unique('app_users', 'email')->where(fn (Builder $query) => $query->where('tenant_id', tenant('id')))
Rule::unique('app_users', 'email')
->where(fn(Builder $query) => $query->where('tenant_id', tenant('id')))
],
'password' => [
'required',

View file

@ -50,7 +50,7 @@ class StoreDocumentRequest extends FormRequest
if (Document::query()->where('id', $this->document_id)->exists() && $this->type) {
$this->merge([
'directory_id' => Document::query()->where('id', $this->document_id)->value('directory_id'),
'type' => TypeEnum::make($this->type, 'name')->value(),
'type' => TypeEnum::make($this->type, 'name')->name(),
]);
}
}

View file

@ -85,6 +85,7 @@ class StoreFormularyRequest extends FormRequest
];
foreach ($this->mobileAppLocales as $locale) {
$attributes["placeholder.{$locale}"] = trans('placeholder (:lang)', ['lang' => $locale]);
$attributes["details.*.{$locale}"] = trans('details (:lang)', ['lang' => $locale]);
}
return $attributes;
}

View file

@ -5,8 +5,6 @@ namespace App\Http\Requests\Bo\Tags;
use App\Models\Tag;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class StoreTagRequest extends FormRequest
{
@ -20,18 +18,6 @@ class StoreTagRequest extends FormRequest
return Gate::check('create', Tag::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Str::slug($this->name),
]);
}
/**
* Get the validation rules that apply to the request.
*
@ -41,11 +27,6 @@ class StoreTagRequest extends FormRequest
{
return [
'name' => 'required|string|min:2|max:25',
'slug' => [
'required',
'string',
Rule::unique('tags', 'slug')->where('tenant_id', tenant('id'))
]
];
}
@ -58,7 +39,6 @@ class StoreTagRequest extends FormRequest
{
return [
'name' => trans('models.classes.tag'),
'slug' => trans('validation.custom.slug'),
];
}
}

View file

@ -5,7 +5,6 @@ namespace App\Http\Requests\Bo\Tenants;
use App\Models\Tenant;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
class StoreTenantRequest extends FormRequest
{
@ -19,18 +18,6 @@ class StoreTenantRequest extends FormRequest
return Gate::allows('create', Tenant::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->merge([
'id' => Str::slug($this->name),
]);
}
/**
* Get the validation rules that apply to the request.
*
@ -39,7 +26,6 @@ class StoreTenantRequest extends FormRequest
public function rules(): array
{
return [
'id' => 'required|string|min:2|max:25',
'name' => 'required|string|min:2|max:25',
];
}

View file

@ -101,7 +101,7 @@ class AppUser extends Model
private static function setOrder(self $model): void
{
$model->order = \intval(
self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order')
self::query()->where('tenant_id', \tenant()->getKey() ?? Tenant::first()->getKey())->max('order')
) + 1;
}

View file

@ -3,6 +3,7 @@
namespace App\Models;
use App\Enums\Documents\LangEnum;
use App\Traits\Models\IsRelatedToTenant;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
@ -27,6 +28,7 @@ use Uwil\LaravelCommon\Helpers\ToolboxHelper;
class Document extends Model
{
use BelongsToTenant;
use IsRelatedToTenant;
use HasFactory;
/**
@ -68,14 +70,16 @@ class Document extends Model
self::setOrder($model);
});
static::updating(function (self $model) {
$path = \ltrim(\urldecode(sprintf(
'docs/%s/%s.%s',
\tenant()->getKey(),
$model->getOriginal('uuid'),
$model->getOriginal('type')
)), '/');
if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
if ($model->getOriginal('uuid') !== $model->uuid) {
$path = \ltrim(\urldecode(sprintf(
'docs/%s/%s.%s',
\tenant()->getKey(),
$model->getOriginal('uuid'),
$model->getOriginal('type')
)), '/');
if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
}
}
});
static::deleting(function (self $model) {
@ -96,7 +100,7 @@ class Document extends Model
private static function assertFieldIsUnique(string $nameField, string $valueField, ?int $id = null): void
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, $nameField, $valueField, $id);
ToolboxHelper::assertFieldIsUnique($table, $nameField, $valueField, $id, self::whereTenantQuery());
}
/**
@ -108,7 +112,7 @@ class Document extends Model
private static function setOrder(self $model): void
{
$model->order = \intval(
self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order')
self::query()->where(self::whereTenantQuery())->max('order')
) + 1;
}

View file

@ -4,10 +4,12 @@ namespace App\Models;
use App\Enums\Formularies\InputStatusEnum;
use App\Enums\Formularies\InputTypeEnum;
use App\Traits\Models\IsRelatedToTenant;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
use Uwil\LaravelCommon\Helpers\ToolboxHelper;
/**
* @property integer $id Formulary id.
@ -27,6 +29,7 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
class Formulary extends Model
{
use BelongsToTenant;
use IsRelatedToTenant;
use HasFactory;
/**
@ -68,12 +71,20 @@ class Formulary extends Model
protected static function booted(): void
{
static::creating(function (self $model) {
// * ASSERTIONS
self::assertNameIsUnique($model->name);
// * TRANSFORMATIONS
self::setSlug($model);
self::setStatusField($model);
self::setOrder($model);
self::makeRequiredSense($model);
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertNameIsUnique($model->name, $model->id);
// * TRANSFORMATIONS
self::setSlug($model);
self::makeRequiredSense($model);
});
@ -81,6 +92,21 @@ class Formulary extends Model
// * METHODS
/**
* Asserts using validation that the name is unique
*
* @param string|null $name
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If name already exists.
*/
private static function assertNameIsUnique(string|null $name, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id, self::whereTenantQuery());
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id, self::whereTenantQuery());
}
/**
* Set the slug.
*
@ -128,7 +154,7 @@ class Formulary extends Model
private static function setOrder(self $model): void
{
$model->order = \intval(
self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order')
self::query()->where(self::whereTenantQuery())->max('order')
) + 1;
}

View file

@ -2,9 +2,12 @@
namespace App\Models;
use App\Traits\Models\IsRelatedToTenant;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
use Uwil\LaravelCommon\Helpers\ToolboxHelper;
use Illuminate\Support\Str;
/**
* @property integer $id Tag id.
@ -18,6 +21,7 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
class Tag extends Model
{
use BelongsToTenant;
use IsRelatedToTenant;
use HasFactory;
/**
@ -37,16 +41,54 @@ class Tag extends Model
*/
protected static function booted(): void
{
static::creating(function (self $tag) {
self::setOrder($tag);
static::creating(function (self $model) {
// * ASSERTIONS
self::assertNameIsUnique($model->name);
// * TRANSFORMATIONS
self::setSlug($model);
self::setOrder($model);
});
static::deleting(function (self $tag) {
$tag->documents()->detach();
static::updating(function (self $model) {
// * ASSERTIONS
self::assertNameIsUnique($model->name);
// * TRANSFORMATIONS
self::setSlug($model);
});
static::deleting(function (self $model) {
$model->documents()->detach();
});
}
// * METHODS
/**
* Asserts using validation that the name is unique
*
* @param string|null $name
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If name already exists.
*/
private static function assertNameIsUnique(string|null $name, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id, self::whereTenantQuery());
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($name), $id, self::whereTenantQuery());
}
/**
* Set the slug.
*
* @param self $model
* @return void
*/
private static function setSlug(self $model): void
{
$model->slug = Str::slug($model->name);
}
/**
* Set order after the last element of the list.
*
@ -56,7 +98,7 @@ class Tag extends Model
private static function setOrder(Tag $tag): void
{
$tag->order = \intval(
self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order')
self::query()->where(self::whereTenantQuery())->max('order')
) + 1;
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Traits\Models;
use App\Models\Tenant;
trait IsRelatedToTenant
{
/**
* Add a 'where' clause to the given query to scope to the current or first tenant.
*
* @return \Closure
*/
public static function whereTenantQuery(): \Closure
{
return function ($query) {
$query->where('tenant_id', \tenant()->getKey() ?? Tenant::first()->getKey());
};
}
}

View file

@ -23,7 +23,7 @@ class UpdateUsersTable extends Migration
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('name', 'firstname');
$table->string('lastname')->nullable()->after('firstname');
$table->dateTime('created_at')->default(Carbon::now())->nullable()->change();
$table->dateTime('created_at')->default(DB::raw('CURRENT_TIMESTAMP'))->nullable()->change();
});
$usersNew = User::get();

View file

@ -47,7 +47,7 @@
name="title"
class="form-control need-word-counter"
:placeholder="trans.methods.__('Un beau titre de document')"
:value="intModel?.title"
v-model="documentTitle"
>
<small class="form-text text-muted">
{{ trans.methods.__("Doit posséder 3 caractères minimum, 255 maximum.") }}
@ -262,6 +262,7 @@ const intDocumentDatas = ref<{
});
const intCsrf = ref<string>("");
const intTooltipList = ref<HTMLButtonElement[]>([]);
let documentTitle = ref<string>("");
onMounted(() => {
const json = String(attrs.json ?? "{}"),
@ -272,6 +273,9 @@ onMounted(() => {
intDocuments.value = data.documents;
intDirectories.value = data.directories;
intModel.value = data.model;
if (intModel.value) {
documentTitle.value = String(intModel.value.title ?? "");
}
if (intModel.value != null) {
if (intModel.value.lang === undefined) {
intIsDocFR.value = true;

View file

@ -71,7 +71,7 @@
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`name`, allErrors) }}
</p>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 255 maximum.') }}</small>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 70 maximum.') }}</small>
</div>
</div>
</fieldset>
@ -103,7 +103,7 @@
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`placeholder.fr`, allErrors) }}
</p>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 255 maximum.') }}</small>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 100 maximum.') }}</small>
</div>
<div class="col-12 col-md-6">
<label
@ -127,7 +127,7 @@
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`placeholder.en`, allErrors) }}
</p>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 255 maximum.') }}</small>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 100 maximum.') }}</small>
</div>
<div class="col-12 col-md-6">
<label
@ -151,7 +151,7 @@
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`placeholder.de`, allErrors) }}
</p>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 255 maximum.') }}</small>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 100 maximum.') }}</small>
</div>
<div class="col-12 col-md-6">
<label
@ -175,7 +175,7 @@
<p class="text-danger m-0">
{{ parseLaravelErrorMessages.methods.parseValidationInput(`placeholder.nl`, allErrors) }}
</p>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 255 maximum.') }}</small>
<small class="form-text text-muted">{{ trans.methods.__('Doit posséder 3 caractères minimum, 100 maximum.') }}</small>
</div>
</div>
</fieldset>
@ -223,7 +223,7 @@
</template>
<script lang="ts" setup>
import { ref, onMounted, useAttrs } from "vue";
import { onMounted, ref, useAttrs } from "vue";
import trans from "../../modules/trans";
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
import FormulariesSwitchComponent from "./FormulariesSwitchComponent.vue";

View file

@ -133,7 +133,7 @@
{{-- STATUS --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border bg-white p-3">
<legend>{{ __('Visibility') }}</legend>
<legend>{{ str(__('validation.custom.visibility'))->ucfirst() }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.boolean-switch', [

View file

@ -1,7 +1,8 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', str(__('models.classes.app_user'))->plural(), str(__('liste'))->ucFirst()))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' => str(__('models.classes.app_user'))->plural()]))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' =>
str(__('models.classes.app_user'))->plural()]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -9,17 +10,15 @@
@include('modules.breadcrumbs.breadcrumb-body')
<form class="input-group me-1" action="{{ route('bo.app_users.import', ['tenant' => tenant()]) }}" method="POST"
enctype="multipart/form-data">
<input class="form-control" type="file" name="import" accept="text/csv,.csv"
title="{{ __('Sélectionner un fichier .csv') }}" data-bs-toggle="tooltip" required>
<input class="form-control" name="import" data-bs-toggle="tooltip" type="file" title="{{ __('Sélectionner un fichier .csv') }}"
accept="text/csv,.csv" required>
@csrf
<button type="submit" class="btn btn-primary" title="{{ __('Importer le fichier .csv') }}"
data-bs-toggle="tooltip">
<button class="btn btn-primary" data-bs-toggle="tooltip" type="submit" title="{{ __('Importer le fichier .csv') }}">
{{ __('Importer') }}
</button>
</form>
@can('create', \App\Models\User::class)
<a class="btn btn-primary float-right" data-bs-toggle="tooltip"
href="{{ route('bo.app_users.create', ['tenant' => \tenant()]) }}"
@can('create', \App\Models\AppUser::class)
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.app_users.create', ['tenant' => \tenant()]) }}"
title="{{ __('crud.helpers.create_model', ['model' => __('models.classes.app_user')]) }}">
<i class="fa-solid fa-plus"></i>
</a>

View file

@ -1,13 +1,14 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', str(__('models.classes.document'))->plural(), str(__('liste'))->ucFirst()))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' => str(__('models.classes.document'))->plural()]))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' =>
str(__('models.classes.document'))->plural()]))
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
@include('modules.breadcrumbs.breadcrumb-body')
@can('create', \App\Models\User::class)
@can('create', \App\Models\Document::class)
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.documents.create', ['tenant' => \tenant()]) }}"
title="{{ __('crud.helpers.create_model', ['model' => __('models.classes.document')]) }}">
<i class="fa-solid fa-plus"></i>

View file

@ -1,13 +1,14 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', str(__('models.classes.formulary'))->plural(), str(__('liste'))->ucFirst()))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' => str(__('models.classes.formulary'))->plural()]))
@section('description', __('Page de contôle de tous les :model de l\'application.', ['model' =>
str(__('models.classes.formulary'))->plural()]))
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
@include('modules.breadcrumbs.breadcrumb-body')
@can('create', \App\Models\User::class)
@can('create', \App\Models\Formulary::class)
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.formularies.create', ['tenant' => \tenant()]) }}"
title="{{ __('crud.helpers.create_model', ['model' => __('models.classes.formulary')]) }}">
<i class="fa-solid fa-plus"></i>

View file

@ -1,7 +1,7 @@
<div class="row">
{{-- NAME --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<fieldset class="h-100 border bg-white p-3">
<legend>{{ __('Nom') }}</legend>
<div class="row">
<div class="col-12 mb-3">
@ -12,8 +12,8 @@
'name' => 'name',
'required' => true,
'model' => $tagModel ?? null,
'helper' => __('Doit posséder 3 caractères minimum, 255 maximum.'),
'placeholder' => "Un beau nom"
'helper' => __('Doit posséder 2 caractères minimum, 25 maximum.'),
'placeholder' => 'Un beau nom',
])
</div>
</div>
@ -26,7 +26,7 @@
document.addEventListener("DOMContentLoaded", () => {
// Submit button clone.
const submit = document.getElementById('formSubmit')
submitClone = document.getElementById('formSubmitClone');
submitClone = document.getElementById('formSubmitClone');
submitClone.addEventListener('click', (event) => {
event.preventDefault();
submit.click('');

View file

@ -7,7 +7,7 @@
@section('content')
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap">
@include('modules.breadcrumbs.breadcrumb-body')
@can('create', \App\Models\User::class)
@can('create', \App\Models\Tag::class)
<a class="btn btn-primary float-right" data-bs-toggle="tooltip" href="{{ route('bo.tags.create', ['tenant' => \tenant()]) }}"
title="{{ __('crud.helpers.create_model', ['model' => __('models.classes.tag')]) }}">
<i class="fa-solid fa-plus"></i>