Merge branch 'issue-33' into 'develop'
feat: add team slide in BO See merge request ldc-web/projets/volena!30
This commit is contained in:
commit
546f955171
16 changed files with 668 additions and 478 deletions
|
|
@ -8,9 +8,8 @@ enum CkEditorConfigName: int
|
|||
{
|
||||
use BaseEnum;
|
||||
|
||||
case minimal = 1;
|
||||
case base = 2;
|
||||
case advanced = 3;
|
||||
case minimal = 1;
|
||||
case basic = 2;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition.
|
||||
|
|
@ -19,7 +18,6 @@ enum CkEditorConfigName: int
|
|||
*/
|
||||
private const LABELS = [
|
||||
self::minimal->name => 'minimal',
|
||||
self::base->name => 'base',
|
||||
self::advanced->name => 'advanced',
|
||||
self::basic->name => 'basic',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ enum SliderTypeEnum: int
|
|||
case home = 1;
|
||||
case better_life = 2;
|
||||
case respect_earth = 3;
|
||||
case team = 4;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition.
|
||||
|
|
@ -21,5 +22,6 @@ enum SliderTypeEnum: int
|
|||
self::home->name => 'Accueil',
|
||||
self::better_life->name => 'Mieux vivre',
|
||||
self::respect_earth->name => 'Respecter la terre',
|
||||
self::team->name => 'Nous rejoindre',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class StaticPageController extends Controller
|
|||
$ambitionSection = $sectionModels->where('section', PageSectionEnum::home_ambition)->first();
|
||||
$teamSection = $sectionModels->where('section', PageSectionEnum::home_team)->first();
|
||||
$lifeSection = $sectionModels->where('section', PageSectionEnum::home_life)->first();
|
||||
$slideTeams = Slide::query()->where('type', SliderTypeEnum::team)->get();
|
||||
|
||||
return view('fo.pages.home', compact(
|
||||
'staticPageModel',
|
||||
|
|
@ -59,6 +60,7 @@ class StaticPageController extends Controller
|
|||
'ambitionSection',
|
||||
'teamSection',
|
||||
'lifeSection',
|
||||
'slideTeams',
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Requests\Bo\Slides;
|
||||
|
||||
use App\Enums\Slides\SliderTypeEnum;
|
||||
use App\Rules\WysiwygLength;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
|
@ -30,7 +31,7 @@ class StoreSlideRequest extends FormRequest
|
|||
{
|
||||
$this->merge([
|
||||
'button_status' => $this->boolean('button_status'),
|
||||
'button_new_tab' => $this->boolean('button_new_tab')
|
||||
'button_new_tab' => $this->boolean('button_new_tab'),
|
||||
]);
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
|
@ -54,19 +55,31 @@ class StoreSlideRequest extends FormRequest
|
|||
'between:3,255'
|
||||
],
|
||||
'content' => [
|
||||
'required_if:type,' . SliderTypeEnum::home->value(),
|
||||
'required_if:type,' . SliderTypeEnum::home->value() . ', ' . SliderTypeEnum::team->value(),
|
||||
'nullable',
|
||||
'string',
|
||||
new WysiwygLength(3, 275),
|
||||
'max:500'
|
||||
],
|
||||
'video_id' => 'nullable|string|regex:%([^"&?\/\s]{11})%i',
|
||||
'picture_alt' => [
|
||||
'required_unless:type,' . SliderTypeEnum::team->value(),
|
||||
'nullable',
|
||||
'string',
|
||||
'between:3,255'
|
||||
],
|
||||
'picture_title' => [
|
||||
'required_unless:type,' . SliderTypeEnum::team->value(),
|
||||
'nullable',
|
||||
'string',
|
||||
'between:3,255'
|
||||
],
|
||||
'video_id' => 'nullable|string|regex:%([^"&?\/\s]{11})%i',
|
||||
'picture_alt' => 'required|string|between:3,255',
|
||||
'picture_title' => 'required|string|between:3,255',
|
||||
'button_label' => 'required_if:button_status,true|nullable|string|between:3,35',
|
||||
'button_url' => 'required_if:button_status,true|nullable|string|between:3,255',
|
||||
'button_status' => [
|
||||
'required_unless:type,' . SliderTypeEnum::home->value(),
|
||||
'nullable', 'boolean'
|
||||
'required_unless:type,' . SliderTypeEnum::home->value() . ', ' . SliderTypeEnum::team->value(),
|
||||
'nullable',
|
||||
'boolean'
|
||||
],
|
||||
'button_new_tab' => 'required|boolean',
|
||||
];
|
||||
|
|
@ -106,18 +119,22 @@ class StoreSlideRequest extends FormRequest
|
|||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'title' => [
|
||||
'required_unless' => __('Le champ :attribute est obligatoire si le slider n\'est pas :name.', [
|
||||
'attribute' => trans('validation.attributes.title'),
|
||||
'name' => SliderTypeEnum::home->label()
|
||||
]),
|
||||
],
|
||||
'content' => [
|
||||
'required_unless' => __('Le champ :attribute est obligatoire si le slider n\'est pas :name.', [
|
||||
'title.required_unless' => __('Le champ :attribute est obligatoire si le slider n\'est pas :name.', [
|
||||
'attribute' => trans('validation.attributes.title'),
|
||||
'name' => SliderTypeEnum::home->label()
|
||||
]),
|
||||
'content.required_if' => __('Le champ :attribute est obligatoire si le slider est :name1 ou :name2.', [
|
||||
'attribute' => trans('validation.attributes.content'),
|
||||
'name1' => SliderTypeEnum::home->label(),
|
||||
'name2' => SliderTypeEnum::team->label()
|
||||
]),
|
||||
'content.max' => __(
|
||||
'Le champ :attribute ne peut pas contenir plus de :max caractères (balises incluses).',
|
||||
[
|
||||
'attribute' => trans('validation.attributes.content'),
|
||||
'name' => SliderTypeEnum::home->label()
|
||||
]),
|
||||
]
|
||||
'max' => '500',
|
||||
]
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Lib\Helpers\FileStorageHelper;
|
|||
use App\Traits\Models\ActivityLog;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
/**
|
||||
* @property \App\Enums\Slides\SliderTypeEnum $type Slide type.
|
||||
|
|
@ -30,6 +31,12 @@ class Slide extends Model
|
|||
use ActivityLog;
|
||||
use HasFactory;
|
||||
|
||||
/** @var integer Minimum number of published slides */
|
||||
private static int $min = 1;
|
||||
|
||||
/** @var integer Maximum number of published slides */
|
||||
private static int $max = 3;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
|
|
@ -68,6 +75,14 @@ class Slide extends Model
|
|||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
if (
|
||||
$model->type === SliderTypeEnum::team &&
|
||||
self::query()->where('type', SliderTypeEnum::team)->count() + 1 > $model::getMaxPublished()
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'slide' => 'Il ne peut y avoir que 3 slides en même temps.'
|
||||
]);
|
||||
}
|
||||
self::manageFields($model);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
|
|
@ -76,6 +91,16 @@ class Slide extends Model
|
|||
static::updated(function (self $model) {
|
||||
FileStorageHelper::removeOldFile($model, 'picture');
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
if (
|
||||
$model->type === SliderTypeEnum::team &&
|
||||
self::query()->where('type', SliderTypeEnum::team)->count() - 1 < $model::getMinPublished()
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'slide' => 'Il doit y avoir au mininmum 1 slide d\'active.'
|
||||
]);
|
||||
}
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
|
|
@ -93,4 +118,24 @@ class Slide extends Model
|
|||
{
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the $min property.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getMinPublished(): int
|
||||
{
|
||||
return self::$min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the $max property.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getMaxPublished(): int
|
||||
{
|
||||
return self::$max;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"kitloong/laravel-migrations-generator": "^6.8",
|
||||
"krlove/eloquent-model-generator": "dev-l10-compatibility",
|
||||
"larastan/larastan": "^2.9",
|
||||
"laravel-lang/common": "^4.0",
|
||||
"laravel/pint": "^1.0",
|
||||
|
|
|
|||
90
composer.lock
generated
90
composer.lock
generated
|
|
@ -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": "c06d9627a9b3a3aedb38f936fcf50b9a",
|
||||
"content-hash": "7e2effeafd1cbae360198092b1705a40",
|
||||
"packages": [
|
||||
{
|
||||
"name": "axlon/laravel-postal-code-validation",
|
||||
|
|
@ -8759,90 +8759,6 @@
|
|||
],
|
||||
"time": "2024-02-06T14:42:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "krlove/code-generator",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/krlove/code-generator.git",
|
||||
"reference": "1ac521f5ef79a376282e3315ba6a13a83c63fb9a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/krlove/code-generator/zipball/1ac521f5ef79a376282e3315ba6a13a83c63fb9a",
|
||||
"reference": "1ac521f5ef79a376282e3315ba6a13a83c63fb9a",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Krlove\\CodeGenerator\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Code Generator",
|
||||
"support": {
|
||||
"issues": "https://github.com/krlove/code-generator/issues",
|
||||
"source": "https://github.com/krlove/code-generator/tree/1.0.1"
|
||||
},
|
||||
"time": "2022-02-20T12:24:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "krlove/eloquent-model-generator",
|
||||
"version": "dev-l10-compatibility",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel-shift/eloquent-model-generator.git",
|
||||
"reference": "d8f0280de04d85cf2991e5a5dd317f5dea239d95"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel-shift/eloquent-model-generator/zipball/d8f0280de04d85cf2991e5a5dd317f5dea239d95",
|
||||
"reference": "d8f0280de04d85cf2991e5a5dd317f5dea239d95",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/dbal": "^3.5",
|
||||
"illuminate/config": "^10.0",
|
||||
"illuminate/console": "^10.0",
|
||||
"illuminate/database": "^10.0",
|
||||
"illuminate/support": "^10.0",
|
||||
"krlove/code-generator": "^1.0",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Krlove\\EloquentModelGenerator\\Provider\\GeneratorServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Krlove\\EloquentModelGenerator\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Krlove\\Tests\\Integration\\": "tests/integration"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Eloquent Model Generator",
|
||||
"support": {
|
||||
"source": "https://github.com/laravel-shift/eloquent-model-generator/tree/l10-compatibility"
|
||||
},
|
||||
"time": "2023-01-30T20:15:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v2.9.12",
|
||||
|
|
@ -12374,9 +12290,7 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"krlove/eloquent-model-generator": 20
|
||||
},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Slides\SliderTypeEnum;
|
||||
use App\Models\Slide;
|
||||
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(): void
|
||||
{
|
||||
Schema::table('slides', function (Blueprint $table) {
|
||||
$table->string('content', 500)->nullable()->comment('Slide content.')->change();
|
||||
$table->string('picture')->nullable()->comment('Slide picture path.')->change();
|
||||
$table->string('picture_alt')->nullable()->comment('Slide picture alt attribute.')->change();
|
||||
$table->string('picture_title')->nullable()->comment('Slide picture title attribute.')->change();
|
||||
$table->boolean('button_status')->default(0)->comment('Slide button status.')->change();
|
||||
$table->boolean('button_new_tab')->default(0)->comment('Open the slide button url in a new tab.')->change();
|
||||
});
|
||||
|
||||
if (Slide::query()->where('type', SliderTypeEnum::team)->count() === 0) {
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$datas = [
|
||||
[
|
||||
'title' => 'Culture d’entreprise inclusive et collaborative :',
|
||||
'content' => 'Notre entreprise valorise la diversité des perspectives et favorise un environnement où chacun se sent écouté et respecté. Nous croyons en la force de la collaboration et encourageons l’innovation à tous les niveaux.',
|
||||
],
|
||||
[
|
||||
'title' => 'Opportunités de développement professionnel :',
|
||||
'content' => 'En nous rejoignant, vous aurez accès à des programmes de formation et de développement pour renforcer vos compétences et progresser dans votre carrière. Nous encourageons la croissance personnelle et professionnelle de nos employés.',
|
||||
],
|
||||
[
|
||||
'title' => 'Implication dans des projets innovants :',
|
||||
'content' => 'Nous travaillons sur des projets stimulants et innovants qui offre l’opportunité de faire une réelle différence pour les consommateurs. Rejoindre notre équipe vous permettra de contribuer à des initiatives passionnantes et impactantes.<br><br>Et en plus, on est sympas.',
|
||||
]
|
||||
];
|
||||
//phpcs:enable
|
||||
|
||||
foreach ($datas as $data) {
|
||||
$slide = new Slide();
|
||||
$slide->type = SliderTypeEnum::team;
|
||||
$slide->title = $data['title'];
|
||||
$slide->content = $data['content'];
|
||||
$slide->button_status = 0;
|
||||
$slide->saveOrFail();
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Slide::query()->where('type', SliderTypeEnum::team)->delete();
|
||||
|
||||
Schema::table('slides', function (Blueprint $table) {
|
||||
$table->string('content', 255)->nullable()->comment('Slide content.')->change();
|
||||
$table->string('picture')->comment('Slide picture path.')->change();
|
||||
$table->string('picture_alt')->comment('Slide picture alt attribute.')->change();
|
||||
$table->string('picture_title')->comment('Slide picture title attribute.')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
61
database/seeders/Mandatory/TeamSlideSeeder.php
Normal file
61
database/seeders/Mandatory/TeamSlideSeeder.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders\Mandatory;
|
||||
|
||||
use App\Enums\Slides\SliderTypeEnum;
|
||||
use App\Models\Slide;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* @package Database\Seeders\Development
|
||||
*/
|
||||
class TeamSlideSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Slide::query()->updateOrCreate(
|
||||
[
|
||||
'type' => SliderTypeEnum::team,
|
||||
'title' => 'Culture d’entreprise inclusive et collaborative :',
|
||||
],
|
||||
[
|
||||
'title' => 'Culture d’entreprise inclusive et collaborative :',
|
||||
'content' => 'Notre entreprise valorise la diversité des perspectives et favorise un environnement où
|
||||
chacun se sent écouté et respecté. Nous croyons en la force de la collaboration et encourageons
|
||||
l’innovation à tous les niveaux.',
|
||||
]
|
||||
);
|
||||
Slide::query()->updateOrCreate(
|
||||
[
|
||||
'type' => SliderTypeEnum::team,
|
||||
'title' => 'Opportunités de développement professionnel :',
|
||||
],
|
||||
[
|
||||
'title' => 'Culture d’entreprise inclusive et collaborative :',
|
||||
'content' => 'En nous rejoignant, vous aurez accès à des programmes de formation et de développement
|
||||
pour renforcer vos compétences et progresser dans votre carrière. Nous encourageons la croissance
|
||||
personnelle et professionnelle de nos employés.',
|
||||
]
|
||||
);
|
||||
Slide::query()->updateOrCreate(
|
||||
[
|
||||
'type' => SliderTypeEnum::team,
|
||||
'title' => 'Implication dans des projets innovants :',
|
||||
],
|
||||
[
|
||||
'title' => 'Culture d’entreprise inclusive et collaborative :',
|
||||
'content' => 'Nous travaillons sur des projets stimulants et innovants qui offre l’opportunité de faire
|
||||
une réelle différence pour les consommateurs. Rejoindre notre équipe vous permettra de contribuer à des
|
||||
initiatives passionnantes et impactantes.
|
||||
<br>
|
||||
<br>
|
||||
Et en plus, on est sympas.',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,342 +1,390 @@
|
|||
<template>
|
||||
<div class="ck-editor-component">
|
||||
<div
|
||||
ref="ckEditorComponent"
|
||||
class="ck-editor-component"
|
||||
>
|
||||
<div ref="wordCounter" />
|
||||
<div ref="ckeditor" />
|
||||
<div ref="ckEditor" />
|
||||
<input
|
||||
:id="internalId"
|
||||
:id="id"
|
||||
class="form-control"
|
||||
type="hidden"
|
||||
:name="intName"
|
||||
:value="internalText"
|
||||
:required="internalRequired"
|
||||
:name="name"
|
||||
:value="text"
|
||||
:required="required"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineOptions, onMounted, ref, useAttrs, type PropType } from "vue";
|
||||
import "../../../js/vendor/ckeditor";
|
||||
import parseLaravelErrorMessages from "../../modules/parseLaravelErrorMessages";
|
||||
import trans from "../../modules/trans";
|
||||
// @ts-ignore
|
||||
import { CkUploadAdapterPlugin } from "../../../js/CKUploadAdapter";
|
||||
import "./../../../js/vendor/ckeditor";
|
||||
import trans from "./../../modules/trans";
|
||||
|
||||
export default defineComponent({
|
||||
name: "CkEditorComponent",
|
||||
emits: ["input"],
|
||||
mixins: [trans],
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: String(Math.pow(10, 16) / Math.random()),
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
dbtext: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
defineOptions({
|
||||
name: "CkEditorComponent"
|
||||
});
|
||||
|
||||
// * EMITS
|
||||
const emits = defineEmits<{
|
||||
input: [string: string],
|
||||
}>();
|
||||
|
||||
// * ATTRIBUTES
|
||||
const attrs = useAttrs();
|
||||
|
||||
// * REFS
|
||||
const ckEditorComponent = ref<HTMLDivElement|null>(null);
|
||||
const ckEditor = ref<HTMLDivElement|null>(null);
|
||||
const wordCounter = ref<HTMLDivElement|null>(null);
|
||||
|
||||
// * PROPS
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: String(Math.pow(10, 16) / Math.random())
|
||||
},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.intName = data.name;
|
||||
this.internalText = data.text ? String(data.text) : "";
|
||||
this.internalId = data.id ? data.id : "";
|
||||
this.internalRequired = Boolean(data.required);
|
||||
this.makeSuperBuild();
|
||||
name: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
data(): {
|
||||
intName: string;
|
||||
internalText: string;
|
||||
internalId: string;
|
||||
internalRequired: boolean;
|
||||
// @ts-ignore
|
||||
editor: ClassicEditor | null;
|
||||
words: number;
|
||||
chars: number;
|
||||
} {
|
||||
return {
|
||||
intName: "",
|
||||
internalText: "",
|
||||
internalId: "",
|
||||
internalRequired: false,
|
||||
editor: null,
|
||||
words: 0,
|
||||
chars: 0,
|
||||
};
|
||||
dbtext: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
watch: {
|
||||
id() {
|
||||
this.internalId = this.id;
|
||||
},
|
||||
name() {
|
||||
this.intName = this.name;
|
||||
},
|
||||
required() {
|
||||
this.internalRequired = this.required;
|
||||
},
|
||||
dbtext() {
|
||||
if (this.internalText != this.dbtext) {
|
||||
this.internalText = this.dbtext ? this.dbtext : "";
|
||||
if (this.editor) {
|
||||
const text = this.internalText;
|
||||
Object.freeze(text);
|
||||
this.editor.setData(text);
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
methods: {
|
||||
makeSuperBuild() {
|
||||
const self = this,
|
||||
ckeditor = this.$refs.ckeditor as HTMLElement,
|
||||
wordCounter = this.$refs.wordCounter as HTMLElement,
|
||||
// * Top Menu
|
||||
verticalTopMenuOffset = document.querySelector(
|
||||
"body > #top-navbar"
|
||||
) as HTMLElement | null;
|
||||
|
||||
if (!verticalTopMenuOffset) {
|
||||
console.warn("Could not find bo #top-navbar");
|
||||
}
|
||||
|
||||
const ui = verticalTopMenuOffset ? {
|
||||
viewportOffset: {
|
||||
top: verticalTopMenuOffset.getBoundingClientRect().height,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
},
|
||||
} : undefined;
|
||||
|
||||
// @ts-ignore
|
||||
ClassicEditor.create(ckeditor, {
|
||||
// https://github.com/ckeditor/ckeditor5-react/issues/267
|
||||
extraPlugins: [ CkUploadAdapterPlugin ],
|
||||
wordCount: {
|
||||
container: wordCounter,
|
||||
// @ts-ignore
|
||||
onUpdate: (stats) => {
|
||||
this.chars = stats.characters;
|
||||
this.words = stats.words;
|
||||
},
|
||||
},
|
||||
ui: ui,
|
||||
link: {
|
||||
// Automatically add target="_blank" and rel="noopener noreferrer" to all external links.
|
||||
addTargetToExternalLinks: true,
|
||||
|
||||
// Let the users control the "download" attribute of each link.
|
||||
decorators: {
|
||||
openInNewTab: {
|
||||
mode: "manual",
|
||||
label: "Ouvrir dans un nouvel onglet",
|
||||
attributes: {
|
||||
target: "_blank",
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
typing: {
|
||||
transformations: {
|
||||
extra: [
|
||||
{ from: ":)", to: "😊" },
|
||||
{ from: ":-P", to: "😛" },
|
||||
{ from: "8-)", to: "😎" },
|
||||
{ from: ";-)", to: "😉" },
|
||||
{ from: ":-D", to: "😄" },
|
||||
{ from: ":twisted:", to: "😈" },
|
||||
{ from: ":mrgreen:", to: "😁" },
|
||||
{ from: ":lol:", to: "😆" },
|
||||
{ from: ":roll:", to: "🙄" },
|
||||
{ from: ":-?", to: "😕" },
|
||||
{ from: ":cry:", to: "😢" },
|
||||
{ from: ":-o", to: "😮" },
|
||||
{ from: ":evil:", to: "😡" },
|
||||
{ from: ":-|", to: "😐" },
|
||||
{ from: ":oops:", to: "😳" },
|
||||
{ from: ":-x", to: "😠" },
|
||||
{ from: "8-O", to: "😲" },
|
||||
{ from: ":-(", to: "😧" },
|
||||
{ from: ":arrow:", to: "➡" },
|
||||
{ from: ":idea:", to: "💡" },
|
||||
],
|
||||
},
|
||||
},
|
||||
mediaEmbed: {
|
||||
previewsInData: false,
|
||||
removeProviders: [
|
||||
"dailymotion",
|
||||
"spotify",
|
||||
"youtube",
|
||||
"vimeo",
|
||||
"instagram",
|
||||
"twitter",
|
||||
"googleMaps",
|
||||
"flickr",
|
||||
"facebook",
|
||||
],
|
||||
// // see https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-media-embed/src/mediaembedediting.js
|
||||
// providers: [
|
||||
// {
|
||||
// // display local videos in oembed tags
|
||||
// name: "localVideo",
|
||||
// url: /^(\/(?:content|storage|uploads)\/.*)/,
|
||||
// html: (match: string[]) => {
|
||||
// const path = match[1];
|
||||
|
||||
// return (
|
||||
// "<video controls allowfullscreen style=\"max-width: 90%; margin: 0 auto; display: block;\">" +
|
||||
// `<source src="${path}" type="video/mp4">` +
|
||||
// `<p>Votre navigateur ne prend pas en charge les vidéos HTML5. Voici <a href="${path}">un lien pour télécharger la vidéo</a>.</p>` +
|
||||
// "</video>"
|
||||
// );
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
toolbar: {
|
||||
items: [
|
||||
"heading",
|
||||
"|",
|
||||
"findAndReplace",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"strikethrough",
|
||||
"link",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
// "code",
|
||||
// "codeBlock",
|
||||
"|",
|
||||
"outdent",
|
||||
"indent",
|
||||
"alignment",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
"selectAll",
|
||||
"|",
|
||||
"imageUpload",
|
||||
// "laravelFileManager",
|
||||
"blockQuote",
|
||||
"|",
|
||||
// "insertTable",
|
||||
// "tableColumn",
|
||||
// "tableRow",
|
||||
// "mergeTableCells",
|
||||
"|",
|
||||
// "highlight",
|
||||
// "removeHighlight",
|
||||
// "fontColor",
|
||||
// "fontBackgroundColor",
|
||||
// "fontFamily",
|
||||
"fontSize",
|
||||
"horizontalLine",
|
||||
// "specialCharacters",
|
||||
"|",
|
||||
"removeFormat",
|
||||
"|",
|
||||
"sourceEditing",
|
||||
],
|
||||
},
|
||||
// table: {
|
||||
// contentToolbar: [
|
||||
// "tableColumn",
|
||||
// "tableRow",
|
||||
// "mergeTableCells",
|
||||
// "tableProperties",
|
||||
// "tableCellProperties",
|
||||
// "toggleTableCaption",
|
||||
// ],
|
||||
|
||||
// Configuration of the TableProperties plugin.
|
||||
// tableProperties: {
|
||||
// ...
|
||||
// },
|
||||
|
||||
// Configuration of the TableCellProperties plugin.
|
||||
// tableCellProperties: {
|
||||
// ...
|
||||
// },
|
||||
// },
|
||||
width: "auto",
|
||||
language: "fr",
|
||||
image: {
|
||||
toolbar: [
|
||||
"toggleImageCaption",
|
||||
"imageTextAlternative",
|
||||
"imageStyle:inline",
|
||||
"imageStyle:alignLeft",
|
||||
"imageStyle:alignRight",
|
||||
"imageStyle:alignCenter",
|
||||
"imageStyle:side",
|
||||
"imageResize",
|
||||
"linkImage",
|
||||
],
|
||||
},
|
||||
})
|
||||
// @ts-ignore
|
||||
.then((editor: ClassicEditor) => {
|
||||
let timer = null as number|null;
|
||||
const updateContent = () => {
|
||||
const data = editor.getData();
|
||||
self.internalText = data;
|
||||
self.$emit("input", data);
|
||||
timer = null;
|
||||
};
|
||||
// Display all toolbar elements
|
||||
// console.log(Array.from(editor.ui.componentFactory.names()));
|
||||
self.editor = Object.freeze(editor);
|
||||
editor.setData(this.internalText);
|
||||
editor.model.document.on("change:data", () => {
|
||||
if(timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
// * Keep track of internal editor
|
||||
timer = setTimeout(updateContent, 1000) as never as number;
|
||||
});
|
||||
})
|
||||
// @ts-ignore
|
||||
.catch((error) => {
|
||||
console.error("Oops, something went wrong!");
|
||||
console.error(
|
||||
"Please, report the following error on https://github.com/ckeditor/ckeditor5/issues with the build id and the error stack trace:"
|
||||
);
|
||||
console.warn("Build id: x0tjc6omqq4d-iydt761tuj51");
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
errors: {
|
||||
type: Object as PropType<LaravelValidationErrors>,
|
||||
default: () => { return []; }
|
||||
},
|
||||
editorConfig: {
|
||||
type: String,
|
||||
default: "basic"
|
||||
},
|
||||
});
|
||||
|
||||
// * DATA
|
||||
const id = ref<string>(props.id);
|
||||
const name = ref<string>(props.name);
|
||||
const text = ref<string>(props.dbtext);
|
||||
const placeholder = ref<string>(props.placeholder);
|
||||
const required = ref<boolean>(props.required);
|
||||
const allErrors = ref<LaravelValidationErrors>(props.errors);
|
||||
const editorConfig = ref<string>(props.editorConfig);
|
||||
|
||||
// * COMPUTED
|
||||
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
|
||||
|
||||
// * LIFECYCLE
|
||||
onMounted(() => {
|
||||
const json = String(attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
if (!isUsedWithProps.value) {
|
||||
(data.id) ? id.value = String(data.id) : "";
|
||||
name.value = String(data.name);
|
||||
text.value = String(data.text);
|
||||
placeholder.value = String(data.placeholder ?? trans.methods.__("Votre texte ici."));
|
||||
allErrors.value = data.errors;
|
||||
(data.required) ? required.value = Boolean(data.required) : "";
|
||||
editorConfig.value = String(data.editorConfig);
|
||||
}
|
||||
setTimeout(() => {
|
||||
customNodes();
|
||||
}, 100);
|
||||
|
||||
// Top Menu
|
||||
const verticalTopMenuOffset = document.querySelector(
|
||||
"body > #top-navbar"
|
||||
) as HTMLElement | null;
|
||||
if (!verticalTopMenuOffset) {
|
||||
console.warn("Could not find bo #top-navbar");
|
||||
}
|
||||
|
||||
// * CkEditor Global Parts.
|
||||
// WordCounter
|
||||
const ckEditorGlobals = {
|
||||
wordCount: {
|
||||
container: wordCounter.value,
|
||||
// @ts-ignore
|
||||
onUpdate: (stats) => {
|
||||
/** Hack to stylize words/caracters counter */
|
||||
/** Add word counter */
|
||||
const wordCounterNode = wordCounter.value?.querySelector(".ck-word-count__words");
|
||||
const spanWords = document.createElement("span");
|
||||
spanWords.classList.add("fw-bold");
|
||||
spanWords.innerHTML = stats.words;
|
||||
wordCounterNode!.textContent = "Mots : ";
|
||||
wordCounterNode!.appendChild(spanWords);
|
||||
/** Add character counter */
|
||||
const characterCounterNode = wordCounter.value?.querySelector(".ck-word-count__characters");
|
||||
const spanCharacters = document.createElement("span");
|
||||
spanCharacters.classList.add("fw-bold");
|
||||
spanCharacters.innerHTML = stats.characters;
|
||||
characterCounterNode!.textContent = "Caractères : ";
|
||||
characterCounterNode!.appendChild(spanCharacters);
|
||||
},
|
||||
},
|
||||
ui: verticalTopMenuOffset ? {
|
||||
viewportOffset: {
|
||||
top: verticalTopMenuOffset.getBoundingClientRect().height,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
},
|
||||
} : undefined,
|
||||
typing: {
|
||||
transformations: {
|
||||
extra: [
|
||||
{ from: ":)", to: "😊" },
|
||||
{ from: ":-P", to: "😛" },
|
||||
{ from: "8-)", to: "😎" },
|
||||
{ from: ";-)", to: "😉" },
|
||||
{ from: ":-D", to: "😄" },
|
||||
{ from: ":twisted:", to: "😈" },
|
||||
{ from: ":mrgreen:", to: "😁" },
|
||||
{ from: ":lol:", to: "😆" },
|
||||
{ from: ":roll:", to: "🙄" },
|
||||
{ from: ":-?", to: "😕" },
|
||||
{ from: ":cry:", to: "😢" },
|
||||
{ from: ":-o", to: "😮" },
|
||||
{ from: ":evil:", to: "😡" },
|
||||
{ from: ":-|", to: "😐" },
|
||||
{ from: ":oops:", to: "😳" },
|
||||
{ from: ":-x", to: "😠" },
|
||||
{ from: "8-O", to: "😲" },
|
||||
{ from: ":-(", to: "😧" },
|
||||
{ from: ":arrow:", to: "➡" },
|
||||
{ from: ":idea:", to: "💡" },
|
||||
],
|
||||
},
|
||||
},
|
||||
link: {
|
||||
// Automatically add target="_blank" and rel="noopener noreferrer" to all external links.
|
||||
addTargetToExternalLinks: true,
|
||||
|
||||
// Let the users control the "download" attribute of each link.
|
||||
decorators: {
|
||||
openInNewTab: {
|
||||
mode: "manual",
|
||||
label: "Ouvrir dans un nouvel onglet",
|
||||
attributes: {
|
||||
target: "_blank",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// * Differents CkEditor configs.
|
||||
const minimalConfig = {
|
||||
wordCount: ckEditorGlobals.wordCount,
|
||||
ui: ckEditorGlobals.ui,
|
||||
placeholder: placeholder.value,
|
||||
link: ckEditorGlobals.link,
|
||||
typing: ckEditorGlobals.typing,
|
||||
mediaEmbed: {
|
||||
previewsInData: false,
|
||||
removeProviders: [
|
||||
"dailymotion",
|
||||
"spotify",
|
||||
"vimeo",
|
||||
"youtube",
|
||||
"instagram",
|
||||
"twitter",
|
||||
"googleMaps",
|
||||
"flickr",
|
||||
"facebook",
|
||||
],
|
||||
},
|
||||
toolbar: {
|
||||
items: [
|
||||
"findAndReplace",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"strikethrough",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
"selectAll",
|
||||
"|",
|
||||
"removeFormat",
|
||||
],
|
||||
},
|
||||
image: {
|
||||
toolbar: []
|
||||
},
|
||||
width: "auto",
|
||||
language: "fr",
|
||||
};
|
||||
|
||||
const basicConfig = {
|
||||
placeholder: placeholder.value,
|
||||
wordCount: ckEditorGlobals.wordCount,
|
||||
ui: ckEditorGlobals.ui,
|
||||
link: ckEditorGlobals.link,
|
||||
typing: ckEditorGlobals.typing,
|
||||
mediaEmbed: {
|
||||
previewsInData: false,
|
||||
removeProviders: [
|
||||
"dailymotion",
|
||||
"spotify",
|
||||
"youtube",
|
||||
"vimeo",
|
||||
"instagram",
|
||||
"twitter",
|
||||
"googleMaps",
|
||||
"flickr",
|
||||
"facebook",
|
||||
],
|
||||
},
|
||||
toolbar: {
|
||||
items: [
|
||||
"heading",
|
||||
"|",
|
||||
"findAndReplace",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"strikethrough",
|
||||
"link",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"|",
|
||||
"outdent",
|
||||
"indent",
|
||||
"alignment",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
"selectAll",
|
||||
"|",
|
||||
"imageUpload",
|
||||
"blockQuote",
|
||||
"|",
|
||||
"fontSize",
|
||||
"horizontalLine",
|
||||
"|",
|
||||
"removeFormat",
|
||||
"|",
|
||||
"sourceEditing",
|
||||
],
|
||||
},
|
||||
width: "auto",
|
||||
language: "fr",
|
||||
image: {
|
||||
toolbar: [
|
||||
"toggleImageCaption",
|
||||
"imageTextAlternative",
|
||||
"imageStyle:inline",
|
||||
"imageStyle:alignLeft",
|
||||
"imageStyle:alignRight",
|
||||
"imageStyle:alignCenter",
|
||||
"imageStyle:side",
|
||||
"imageResize",
|
||||
"linkImage",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
let config = basicConfig;
|
||||
switch (editorConfig.value) {
|
||||
case "minimal":
|
||||
config = minimalConfig;
|
||||
break;
|
||||
case "basic":
|
||||
config = basicConfig;
|
||||
break;
|
||||
default:
|
||||
console.warn(editorConfig.value + " is not a correct configuration. The basic config is loaded.");
|
||||
}
|
||||
|
||||
// Generate Editor.
|
||||
makeEditorBuild(config);
|
||||
});
|
||||
|
||||
// * METHODS
|
||||
|
||||
// @ts-ignore
|
||||
function makeEditorBuild(config: Config<EditorConfig>): void {
|
||||
// @ts-ignore
|
||||
ClassicEditor.create(ckEditor.value, config)
|
||||
// @ts-ignore
|
||||
.then((editor: ClassicEditor) => {
|
||||
let timer = null as number|null;
|
||||
const updateContent = () => {
|
||||
const data = editor.getData();
|
||||
text.value = data;
|
||||
emits("input", data);
|
||||
timer = null;
|
||||
};
|
||||
// Display all toolbar elements
|
||||
ckEditor.value = Object.freeze(editor);
|
||||
editor.setData(text.value);
|
||||
editor.model.document.on("change:data", () => {
|
||||
if(timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
// * Keep track of internal editor
|
||||
timer = setTimeout(updateContent, 300) as never as number;
|
||||
});
|
||||
})
|
||||
// @ts-ignore
|
||||
.catch((error) => {
|
||||
console.error("Oops, something went wrong!");
|
||||
console.error(
|
||||
"Please, report the following error on https://github.com/ckeditor/ckeditor5/issues with the build id and the error stack trace:"
|
||||
);
|
||||
console.warn("Build id: x0tjc6omqq4d-iydt761tuj51");
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
/** Customize input. */
|
||||
function customNodes(): void {
|
||||
if (getErrorMessage() !== null) {
|
||||
const fakeInput = ckEditorComponent.value?.querySelector(".ck.ck-editor__main") as HTMLDivElement|null;
|
||||
fakeInput!.classList.add("ck-invalid");
|
||||
}
|
||||
}
|
||||
|
||||
/** Return error message or null. */
|
||||
function getErrorMessage(): string|null {
|
||||
return parseLaravelErrorMessages.methods.parseValidationInput(
|
||||
`${name.value}`,
|
||||
allErrors.value
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scopped>
|
||||
@import url("./../../../js/vendor/ckeditor/build/styles.css");
|
||||
.ck-editor-component{
|
||||
// 2. Include functions first (so you can manipulate colors, SVGs, calc, etc)
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/variables";
|
||||
|
||||
// 3. Include remainder of required Bootstrap stylesheets
|
||||
@import "bootstrap/scss/variables";
|
||||
.ck-editor-component{
|
||||
.ck-rounded-corners {
|
||||
border-radius: 0 0 0.375rem 0.375rem !important;
|
||||
}
|
||||
.ck-word-count {
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #c4c4c4;
|
||||
background-color: rgb(var(--bs-secondary-bg-rgb));
|
||||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||||
border-bottom: 0px;
|
||||
border-top-left-radius: 0.375rem;
|
||||
border-top-right-radius: 0.375rem;
|
||||
color: initial;
|
||||
color: var(--bs-body-color);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around !important;
|
||||
|
|
@ -345,13 +393,26 @@ export default defineComponent({
|
|||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
.ck-focused {
|
||||
border-color: #86b7fe !important;
|
||||
box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%) !important;
|
||||
border-color: var(--bs-primary-border-subtle) !important;
|
||||
box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)) !important;
|
||||
outline: 0 !important;
|
||||
}
|
||||
.ck-editor__main{
|
||||
color: $body-color;
|
||||
}
|
||||
.ck.ck-editor__main.ck-invalid .ck-blurred, .ck.ck-editor__main.ck-invalid .ck-focused {
|
||||
border-color: var(--bs-form-invalid-border-color) !important;
|
||||
}
|
||||
.ck.ck-editor__main.ck-invalid .ck-focused {
|
||||
box-shadow: 0 0 0 .25rem rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)) !important;
|
||||
}
|
||||
}
|
||||
.ck.ck-reset.ck-rounded-corners .ck-toolbar {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||||
}
|
||||
.ck.ck-powered-by-balloon {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
1
resources/ts/index.d.ts
vendored
1
resources/ts/index.d.ts
vendored
|
|
@ -70,6 +70,7 @@ declare global {
|
|||
type LaravelPaginator = Paginator;
|
||||
type LaravelPaginatorLink = PaginatorLink;
|
||||
type ValueOf<T> = T[keyof T];
|
||||
type LaravelValidationErrors = Record<string, string[]>;
|
||||
|
||||
type Enum = EnumInterface;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
@extends('bo.layout', ['brParam' => $slideModel])
|
||||
|
||||
@section('title', __('Création d\'un :model', ['model' => trans_choice('models.classes.site', 1)]))
|
||||
@section('description', __('Création d\'un tout nouveau :model', ['model' => trans_choice('models.classes.site', 1)]))
|
||||
@section('title', __('Création d\'un :model', ['model' => trans_choice('models.classes.slide', 1)]))
|
||||
@section('description', __('Création d\'un tout nouveau :model', ['model' => trans_choice('models.classes.slide', 1)]))
|
||||
@section('breadcrumb', request()->route()->getName())
|
||||
|
||||
@section('content')
|
||||
<div class="d-flex justify-content-between flex-md-nowrap align-items-center flex-wrap pb-3">
|
||||
<div class="d-flex align-items-start flex-row">
|
||||
<a class="btn btn-primary text-decoration-none m-0" data-bs-toggle="tooltip" href="{{ route('bo.slides.index') }}"
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.site', 1)]) }}">
|
||||
title="{{ __('crud.helpers.list_all', ['model' => trans_choice('models.classes.slide', 1)]) }}">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</a>
|
||||
@include('modules.breadcrumbs.breadcrumb-body', ['brParam' => $slideModel])
|
||||
</div>
|
||||
<div class="mb-md-0 mb-2">
|
||||
<button class="btn btn-primary" id="formSubmitClone" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.site', 1)]) }}">
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.slide', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<div class="row mt-3">
|
||||
<div class="col text-center">
|
||||
<button class="btn btn-primary" id="formSubmit" data-bs-toggle="tooltip" type="submit"
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.site', 1)]) }}">
|
||||
title="{{ __('crud.helpers.save_model', ['model' => trans_choice('models.classes.slide', 1)]) }}">
|
||||
{{ Str::ucfirst(__('crud.actions.save')) }}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.title')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'title',
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
'attribute' => __(':field :inter:model', [
|
||||
'field' => __('validation.attributes.title'),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'min' => 3,
|
||||
'max' => 100,
|
||||
|
|
@ -51,30 +51,30 @@
|
|||
</fieldset>
|
||||
</div>
|
||||
{{-- CONTENT --}}
|
||||
<div class="col-12 col-md-6 mb-3" id="divContent">
|
||||
<div class="col-12 mb-3" id="divContent">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.content')) }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('bo.partials.inputs.varchar-multiline', [
|
||||
@include('bo.partials.inputs.text-html', [
|
||||
'id' => 'slideContent',
|
||||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.attributes.content')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'content',
|
||||
'required' => false,
|
||||
'model' => $slideModel ?? null,
|
||||
'required' => false,
|
||||
'editorConfig' => \App\Enums\CkEditorConfigName::minimal->label(),
|
||||
'helper' => __('validation.between.string', [
|
||||
'attribute' => __(':field :inter:model', [
|
||||
'field' => __('validation.attributes.content'),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'min' => 3,
|
||||
'max' => 255,
|
||||
'max' => 275,
|
||||
]),
|
||||
'placeholder' => __('Contenu textuel de la slide'),
|
||||
])
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
'label' => __(':field :inter:model', [
|
||||
'field' => Str::ucfirst(__('validation.custom.video_id')),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'video_id',
|
||||
|
|
@ -104,7 +104,7 @@
|
|||
'attribute' => __(':field :inter:model', [
|
||||
'field' => __('validation.custom.video_id'),
|
||||
'inter' => __('validation.custom.of_the.female'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'min' => 3,
|
||||
'max' => 11,
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
</fieldset>
|
||||
</div>
|
||||
{{-- PICTURE --}}
|
||||
<div class="col-12 pb-3">
|
||||
<div class="col-12 pb-3" id="divPicture">
|
||||
<fieldset class="h-100 border p-3">
|
||||
<legend>{{ Str::ucfirst(__('validation.attributes.image')) }}</legend>
|
||||
<div class="row mb-3">
|
||||
|
|
@ -129,11 +129,11 @@
|
|||
'inter1' => __('validation.custom.of_the.male'),
|
||||
'field2' => __('validation.attributes.image'),
|
||||
'inter2' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'picture_alt',
|
||||
'required' => true,
|
||||
'required' => false,
|
||||
'model' => $slideModel ?? null,
|
||||
'helper' => __('crud.minicrud.picture_alt_helper'),
|
||||
'placeholder' => __(':field1 :inter1:field2 :inter2:model', [
|
||||
|
|
@ -141,7 +141,7 @@
|
|||
'inter1' => __('validation.custom.of_the.male'),
|
||||
'field2' => __('validation.attributes.image'),
|
||||
'inter2' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
])
|
||||
</div>
|
||||
|
|
@ -154,11 +154,11 @@
|
|||
'inter1' => __('validation.custom.of_the.male'),
|
||||
'field2' => __('validation.attributes.image'),
|
||||
'inter2' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
'hasWordCounter' => true,
|
||||
'name' => 'picture_title',
|
||||
'required' => true,
|
||||
'required' => false,
|
||||
'model' => $slideModel ?? null,
|
||||
'helper' => __('crud.minicrud.picture_title_helper'),
|
||||
'placeholder' => __(':field1 :inter1:field2 :inter2:model', [
|
||||
|
|
@ -166,7 +166,7 @@
|
|||
'inter1' => __('validation.custom.of_the.male'),
|
||||
'field2' => __('validation.attributes.image'),
|
||||
'inter2' => __('validation.custom.of_the.male'),
|
||||
'model' => trans_choice('models.classes.site', 1),
|
||||
'model' => trans_choice('models.classes.slide', 1),
|
||||
]),
|
||||
])
|
||||
</div>
|
||||
|
|
@ -290,6 +290,7 @@
|
|||
const fieldsetVideo = document.getElementById('divVideo');
|
||||
const fieldsetTitle = document.getElementById('divTitle');
|
||||
const fieldsetContent = document.getElementById('divContent');
|
||||
const fieldsetPicture = document.getElementById('divPicture');
|
||||
const fieldsetButton = document.getElementById('divButton');
|
||||
checkSelectValue()
|
||||
sliderSelectType.addEventListener('input', checkSelectValue);
|
||||
|
|
@ -299,11 +300,19 @@
|
|||
fieldsetVideo.hidden = false;
|
||||
fieldsetTitle.required = false;
|
||||
fieldsetContent.hidden = false;
|
||||
fieldsetPicture.hidden = false;
|
||||
fieldsetButton.hidden = false;
|
||||
} else if (sliderSelectType.value === "{{ \App\Enums\Slides\SliderTypeEnum::team->value() }}") {
|
||||
fieldsetVideo.hidden = true;
|
||||
fieldsetTitle.required = true;
|
||||
fieldsetContent.hidden = false;
|
||||
fieldsetPicture.hidden = true;
|
||||
fieldsetButton.hidden = true;
|
||||
} else {
|
||||
fieldsetVideo.hidden = true;
|
||||
fieldsetTitle.required = true;
|
||||
fieldsetContent.hidden = true;
|
||||
fieldsetPicture.hidden = false;
|
||||
fieldsetButton.hidden = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
$data = [
|
||||
'name' => $name,
|
||||
'text' => old($name, $model->{$name} ?? ($value ?? '')),
|
||||
'placeholder' => $placeholder ?? __('Un text riche'),
|
||||
'errors' => $errors->getBag('default')->getMessages() ?? [],
|
||||
'editorConfig' => $editorConfig ?? \App\Enums\CkEditorConfigName::basic->label(),
|
||||
];
|
||||
@endphp
|
||||
<div class="ck-editor" data-json='@json($data)'></div>
|
||||
|
|
|
|||
|
|
@ -141,26 +141,16 @@
|
|||
<div class="col-12 rounded-3 mt-2 bg-white p-4">
|
||||
<div class="swiper swiper-teams p-0" id="swiper-teams">
|
||||
<div class="swiper-wrapper">
|
||||
@php
|
||||
$homeTeamData = [
|
||||
'Culture d’entreprise inclusive et collaborative :' =>
|
||||
'Notre entreprise valorise la diversité des perspectives et favorise un environnement où chacun se sent écouté et respecté. Nous croyons en la force de la collaboration et encourageons l’innovation à tous les niveaux.',
|
||||
'Opportunités de développement professionnel :' =>
|
||||
'En nous rejoignant, vous aurez accès à des programmes de formation et de développement pour renforcer vos compétences et progresser dans votre carrière. Nous encourageons la croissance personnelle et professionnelle de nos employés.',
|
||||
'Implication dans des projets innovants :' =>
|
||||
'Nous travaillons sur des projets stimulants et innovants qui offre l’opportunité de faire une réelle différence pour les consommateurs. Rejoindre notre équipe vous permettra de contribuer à des initiatives passionnantes et impactantes.<br><br>Et en plus, on est sympas.',
|
||||
];
|
||||
@endphp
|
||||
@foreach ($homeTeamData as $title => $text)
|
||||
@foreach ($slideTeams as $slide)
|
||||
<div class="swiper-slide">
|
||||
<p class="text-primary fw-bolder fs-3">{!! $title !!}</p>
|
||||
<p class="text-primary fs-4">{!! $text !!}</p>
|
||||
<p class="text-primary fw-bolder fs-3">{{ $slide->title }}</p>
|
||||
<div class="text-primary fs-4 ck-content">{!! clean($slide->content) !!}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div
|
||||
class="swiper-pagination swiper-teams-pagination d-flex justify-content-between align-items-center w-100">
|
||||
class="swiper-pagination swiper-teams-pagination d-flex justify-content-evenly align-items-center w-100">
|
||||
</div>
|
||||
<button class="swiper-pagination-playpause btn btn-primary btn-sm m-1 ms-3">
|
||||
<i class="fa-solid fa-pause"></i>
|
||||
|
|
|
|||
|
|
@ -41,10 +41,12 @@ Route::prefix('bo')
|
|||
// * USERS.
|
||||
Route::resource('users', UserController::class)->except('show');
|
||||
Route::patch('/users/{user}/change-published', [
|
||||
UserController::class, 'changePublished'
|
||||
UserController::class,
|
||||
'changePublished'
|
||||
])->name('users.change-published');
|
||||
Route::get('/users/{user}/duplicate', [
|
||||
UserController::class, 'duplicate'
|
||||
UserController::class,
|
||||
'duplicate'
|
||||
])->name('users.duplicate');
|
||||
|
||||
// * STATIC PAGES.
|
||||
|
|
@ -63,13 +65,16 @@ Route::prefix('bo')
|
|||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:content,introduction');
|
||||
Route::patch('/news/{news}/change-published', [
|
||||
NewsController::class, 'changePublished'
|
||||
NewsController::class,
|
||||
'changePublished'
|
||||
])->name('news.change-published');
|
||||
Route::patch('/news/{news}/change-order/{direction}', [
|
||||
NewsController::class, 'changeOrder'
|
||||
NewsController::class,
|
||||
'changeOrder'
|
||||
])->where('direction', 'up|down')->name('news.change-order');
|
||||
Route::get('/news/{news}/duplicate', [
|
||||
NewsController::class, 'duplicate'
|
||||
NewsController::class,
|
||||
'duplicate'
|
||||
])->name('news.duplicate');
|
||||
|
||||
// * SITES.
|
||||
|
|
@ -77,29 +82,36 @@ Route::prefix('bo')
|
|||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:director_speach,employee_speach,highlights,content');
|
||||
Route::patch('/sites/{site}/change-order/{direction}', [
|
||||
SiteController::class, 'changeOrder'
|
||||
SiteController::class,
|
||||
'changeOrder'
|
||||
])->where('direction', 'up|down')->name('sites.change-order');
|
||||
Route::get('/sites/{site}/duplicate', [
|
||||
SiteController::class, 'duplicate'
|
||||
SiteController::class,
|
||||
'duplicate'
|
||||
])->name('sites.duplicate');
|
||||
|
||||
// * LOGOS.
|
||||
Route::resource('logos', LogoController::class)->except('show');
|
||||
Route::get('/logos/{logo}/duplicate', [
|
||||
LogoController::class, 'duplicate'
|
||||
LogoController::class,
|
||||
'duplicate'
|
||||
])->name('logos.duplicate');
|
||||
|
||||
// * HISTORICAL MILESTONES.
|
||||
Route::resource('historicalmilestones', HistoricalMilestoneController::class)->except('show')
|
||||
->parameters(['historicalmilestones' => 'historical_milestone']);
|
||||
Route::get('/historicalmilestones/{historical_milestone}/duplicate', [
|
||||
HistoricalMilestoneController::class, 'duplicate'
|
||||
HistoricalMilestoneController::class,
|
||||
'duplicate'
|
||||
])->name('historicalmilestones.duplicate');
|
||||
|
||||
// * SLIDES.
|
||||
Route::resource('slides', SlideController::class)->except('show');
|
||||
Route::resource('slides', SlideController::class)->except('show')
|
||||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:content');
|
||||
Route::get('/slides/{slide}/duplicate', [
|
||||
SlideController::class, 'duplicate'
|
||||
SlideController::class,
|
||||
'duplicate'
|
||||
])->name('slides.duplicate');
|
||||
|
||||
// * STRIPS.
|
||||
|
|
@ -107,10 +119,12 @@ Route::prefix('bo')
|
|||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:text');
|
||||
Route::patch('/strips/{strip}/change-published', [
|
||||
StripController::class, 'changePublished'
|
||||
StripController::class,
|
||||
'changePublished'
|
||||
])->name('strips.change-published');
|
||||
Route::get('/strips/{strip}/duplicate', [
|
||||
StripController::class, 'duplicate'
|
||||
StripController::class,
|
||||
'duplicate'
|
||||
])->name('strips.duplicate');
|
||||
|
||||
// * SECTIONS.
|
||||
|
|
@ -128,7 +142,8 @@ Route::prefix('bo')
|
|||
Route::resource('jobvideos', JobVideoController::class)->except('show')
|
||||
->parameters(['jobvideos' => 'job_video']);
|
||||
Route::get('/jobvideos/{job_video}/duplicate', [
|
||||
JobVideoController::class, 'duplicate'
|
||||
JobVideoController::class,
|
||||
'duplicate'
|
||||
])->name('jobvideos.duplicate');
|
||||
|
||||
// * TESTIMONIALS.
|
||||
|
|
@ -136,7 +151,8 @@ Route::prefix('bo')
|
|||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:content');
|
||||
Route::get('/testimonials/{testimonial}/duplicate', [
|
||||
TestimonialController::class, 'duplicate'
|
||||
TestimonialController::class,
|
||||
'duplicate'
|
||||
])->name('testimonials.duplicate');
|
||||
|
||||
Route::middleware('can:isConceptor')->group(function () {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue