Ajout de la FAQ (thème et question) dans le BO
This commit is contained in:
parent
bdae3a73e8
commit
ea8b431b5b
14 changed files with 1682 additions and 0 deletions
57
app/Http/Controllers/bo/FaqQuestionsController.php
Normal file
57
app/Http/Controllers/bo/FaqQuestionsController.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\FaqQuestions;
|
||||
use App\Models\FaqThemes;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FaqQuestionsController extends Controller
|
||||
{
|
||||
public function index() {
|
||||
$questions = FaqQuestions::all();
|
||||
for ($i = 0; $i < count($questions); $i++) {
|
||||
$questions[$i]['toggle'] = false;
|
||||
}
|
||||
$themes = FaqThemes::all();
|
||||
return view('bo.faq-questions.index', compact('questions', 'themes'));
|
||||
}
|
||||
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
if ($request->id !== null) {
|
||||
$question = FaqQuestions::findOrFail($request->id);
|
||||
$question->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function saveDatas(Request $request) {
|
||||
$questions = $request->questions;
|
||||
|
||||
for ($i = 0; $i < count($questions); $i++) {
|
||||
if (isset($questions[$i]["id"])) {
|
||||
$q = FaqQuestions::findOrFail($questions[$i]["id"]);
|
||||
$q->question = $questions[$i]["question"];
|
||||
$q->answer = $questions[$i]["answer"];
|
||||
$q->positive_vote = $questions[$i]["positive_vote"];
|
||||
$q->negative_vote = $questions[$i]["negative_vote"];
|
||||
$q->theme_id = $questions[$i]["theme_id"];
|
||||
$q->save();
|
||||
} else {
|
||||
$t = FaqQuestions::updateOrCreate(
|
||||
[
|
||||
'question' => $questions[$i]["question"],
|
||||
'answer' => $questions[$i]["answer"],
|
||||
'positive_vote' => $questions[$i]["positive_vote"],
|
||||
'negative_vote' => $questions[$i]["negative_vote"],
|
||||
'theme_id' => $questions[$i]["theme_id"]
|
||||
]
|
||||
);
|
||||
$questions[$i]["id"] = $t->id;
|
||||
}
|
||||
}
|
||||
|
||||
return $questions;
|
||||
}
|
||||
}
|
||||
49
app/Http/Controllers/bo/FaqThemesController.php
Normal file
49
app/Http/Controllers/bo/FaqThemesController.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\FaqQuestions;
|
||||
use App\Models\FaqThemes;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FaqThemesController extends Controller
|
||||
{
|
||||
public function index() {
|
||||
$themes = FaqThemes::all();
|
||||
return view('bo.faq-themes.index', compact('themes'));
|
||||
}
|
||||
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
$deleteOk = 0;
|
||||
|
||||
if (FaqQuestions::where('theme_id', $request->id)->count() > 0) {
|
||||
$deleteOk = 1;
|
||||
} else if ($request->id !== null) {
|
||||
$theme = FaqThemes::findOrFail($request->id);
|
||||
$theme->delete();
|
||||
} else {
|
||||
$deleteOk = 2;
|
||||
}
|
||||
|
||||
return $deleteOk;
|
||||
}
|
||||
|
||||
public function saveDatas(Request $request) {
|
||||
$themes = $request->themes;
|
||||
|
||||
for ($i = 0; $i < count($themes); $i++) {
|
||||
if (isset($themes[$i]["id"])) {
|
||||
$t = FaqThemes::findOrFail($themes[$i]["id"]);
|
||||
$t->name = $themes[$i]["name"];
|
||||
$t->save();
|
||||
} else {
|
||||
$t = FaqThemes::updateOrCreate(['name' => $themes[$i]["name"]]);
|
||||
$themes[$i]["id"] = $t->id;
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
}
|
||||
15
app/Models/FaqQuestions.php
Normal file
15
app/Models/FaqQuestions.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FaqQuestions extends Model
|
||||
{
|
||||
protected $table = "faq_questions";
|
||||
protected $fillable = ["question", "answer", "positive_vote", "negative_vote", "theme_id"];
|
||||
|
||||
public function theme() {
|
||||
return $this->belongsTo('App\Models\FaqThemes', 'theme_id');
|
||||
}
|
||||
}
|
||||
16
app/Models/FaqThemes.php
Normal file
16
app/Models/FaqThemes.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class FaqThemes extends Model
|
||||
{
|
||||
protected $table = "faq_themes";
|
||||
protected $fillable = ["name"];
|
||||
protected $with = ['questions'];
|
||||
|
||||
public function questions() {
|
||||
return $this->hasMany('App\Models\FaqQuestions', 'theme_id');
|
||||
}
|
||||
}
|
||||
32
database/migrations/2020_10_26_093014_create_faq_themes.php
Normal file
32
database/migrations/2020_10_26_093014_create_faq_themes.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateFaqThemes extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('faq_themes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('faq_themes');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateFaqQuestions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('faq_questions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('question');
|
||||
$table->string('answer');
|
||||
$table->unsignedInteger('positive_vote');
|
||||
$table->unsignedInteger('negative_vote');
|
||||
$table->foreignId('theme_id')->constrained('faq_themes');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('faq_questions');
|
||||
}
|
||||
}
|
||||
1147
public/js/app.js
vendored
1147
public/js/app.js
vendored
File diff suppressed because it is too large
Load diff
2
resources/js/app.js
vendored
2
resources/js/app.js
vendored
|
|
@ -29,6 +29,8 @@ Vue.component('prizes', require('./components/Prizes.vue').default);
|
|||
Vue.component('instant-win', require('./components/InstantWin.vue').default);
|
||||
Vue.component('codes-ean', require('./components/CodesEAN.vue').default);
|
||||
Vue.component('banned-ip', require('./components/BannedIP.vue').default);
|
||||
Vue.component('faq-themes', require('./components/FaqThemes.vue').default);
|
||||
Vue.component('faq-questions', require('./components/FaqQuestions.vue').default);
|
||||
|
||||
/**
|
||||
* Next, we will create a fresh Vue application instance and attach it to
|
||||
|
|
|
|||
157
resources/js/components/FaqQuestions.vue
Normal file
157
resources/js/components/FaqQuestions.vue
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
FAQ - Gestion des questions
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<transition name="fade">
|
||||
<span v-show="processing">
|
||||
{{ response }}
|
||||
</span>
|
||||
</transition>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<button class="btn btn-primary btn-sm text-white float-right" @click="saveData()" :disabled="processing">
|
||||
Sauvegarder
|
||||
<div class="spinner-border spinner-border-sm text-light" role="status" v-show="processing">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<div v-for="(item, index) in datas" :key="item.id">
|
||||
<div class="form-inline">
|
||||
<label class="col-form-label col-md-3">Question :</label>
|
||||
<input type="text" v-model="item.question" class="form-control form-control-sm col-md-7" placeholder="Entrer votre question ici" required>
|
||||
<a class="text-decoration-none col-md-2 col-form-label w-100 text-right" href="#_" @click="item.toggle = !item.toggle">
|
||||
Détails
|
||||
<i class="fas" :class="{ 'fa-chevron-down': !item.toggle, 'fa-chevron-up': item.toggle}"></i>
|
||||
</a>
|
||||
</div>
|
||||
<transition name="slide-fade">
|
||||
<div class="form-inline bg-light rounded" v-show="item.toggle">
|
||||
<label class="col-form-label col-md-3">Réponse :</label>
|
||||
<input type="text" v-model="item.answer" class="form-control form-control-sm col-md-7" placeholder="Entrer votre réponse ici" required><br/>
|
||||
<div class="col-md-2 text-right">
|
||||
<button class="btn btn-danger btn-sm" @click="removeItem(index)">Supprimer</button>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="col-form-label">Votes positifs</label>
|
||||
<input type="text" v-model="item.positive_vote" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="col-form-label">Votes négatifs</label>
|
||||
<input type="text" v-model="item.negative_vote" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label class="col-form-label">Thème</label>
|
||||
<select v-model="item.theme_id" class="custom-select custom-select-sm">
|
||||
<option v-for="theme in themes" :key="theme.id" :value="theme.id" :selected="theme.id == item.theme_id">{{theme.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="form-group text-right">
|
||||
<button class="btn btn-primary btn-sm text-white" @click="newItem">Ajouter une question</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
console.log('FaqQuestions Component mounted.');
|
||||
this.datas = JSON.parse(this.dbdatas);
|
||||
this.themes = JSON.parse(this.dbthemes);
|
||||
},
|
||||
props: [
|
||||
'dbdatas',
|
||||
'dbthemes',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
datas: [],
|
||||
themes: [],
|
||||
response: null,
|
||||
processing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
newItem() {
|
||||
this.datas.push({id: null, question: '', answer: '', positive_vote: 0, negative_vote: 0, theme_id: null, toggle: false});
|
||||
},
|
||||
removeItem(index) {
|
||||
if (this.datas[index]['id'] !== null) {
|
||||
this.processing = true;
|
||||
this.response = "Suppression en cours...";
|
||||
axios.post('/bo/ajax/delete-faq-question', {
|
||||
id: this.datas[index]['id'],
|
||||
}).then(
|
||||
(response) => {
|
||||
this.response = "Suppression terminée !";
|
||||
this.processing = false;
|
||||
}
|
||||
)
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
this.response = "Erreur lors de la suppression.";
|
||||
this.processing = false;
|
||||
});
|
||||
this.datas.splice(index, 1);
|
||||
} else {
|
||||
this.datas.splice(index, 1);
|
||||
}
|
||||
},
|
||||
saveData() {
|
||||
this.processing = true;
|
||||
this.response = "En cours de sauvegarde..."
|
||||
axios.post('/bo/ajax/save-faq-question', {
|
||||
questions: this.datas,
|
||||
}).then(
|
||||
(response) => {
|
||||
this.datas = response.data;
|
||||
this.response = "Sauvegarde terminée !";
|
||||
this.processing = false;
|
||||
}
|
||||
)
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
this.response = "Erreur lors de la sauvegarde.";
|
||||
this.processing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.slide-fade-enter-active {
|
||||
transition: all .3s ease;
|
||||
}
|
||||
.slide-fade-leave-active {
|
||||
transition: all .1s cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
||||
}
|
||||
.slide-fade-enter, .slide-fade-leave-to {
|
||||
transform: translateX(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
146
resources/js/components/FaqThemes.vue
Normal file
146
resources/js/components/FaqThemes.vue
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
FAQ - Gestion des thèmes
|
||||
</div>
|
||||
<div class="col-md-6 text-center">
|
||||
<transition name="fade">
|
||||
<span v-show="processing">
|
||||
{{ response }}
|
||||
</span>
|
||||
</transition>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<button class="btn btn-primary btn-sm text-white float-right" @click="saveData()" :disabled="processing">
|
||||
Sauvegarder
|
||||
<div class="spinner-border spinner-border-sm text-light" role="status" v-show="processing">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-md-10 offset-md-1">
|
||||
<table class="table table-bordered text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-75" scope="col">Nom du thème <span class="required-asterisk">*</span></th>
|
||||
<th class="w-50" scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in datas" v-bind:key="item.id">
|
||||
<td><input class="form-control form-control-sm" v-model="item.name" type="text" required></td>
|
||||
<td>
|
||||
<button @click="removeItem(index)" :disabled="processing" class="btn btn-secondary btn-sm">
|
||||
Supprimer
|
||||
<div class="spinner-border spinner-border-sm text-light" role="status" v-show="processing">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="form-group text-right">
|
||||
<button class="btn btn-primary btn-sm text-white" @click="newItem">Ajouter une ligne</button>
|
||||
<input type="hidden" :name="name" :value="JSON.stringify(datas)" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
console.log('FaqThemes Component mounted.');
|
||||
this.datas = JSON.parse(this.dbdatas);
|
||||
},
|
||||
props: [
|
||||
'name',
|
||||
'dbdatas',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
datas: [],
|
||||
response: null,
|
||||
processing: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
newItem() {
|
||||
this.datas.push({id: null, name: ''});
|
||||
},
|
||||
removeItem(index) {
|
||||
if (this.datas[index]['id'] !== null) {
|
||||
this.processing = true;
|
||||
this.response = "Suppression en cours...";
|
||||
axios.post('/bo/ajax/delete-faq-theme', {
|
||||
id: this.datas[index]['id'],
|
||||
}).then(
|
||||
(response) => {
|
||||
let result = response.data;
|
||||
switch (result) {
|
||||
case 0: case 2:
|
||||
// 0 = Supprimé, 2 = Pas encore en base, supprimer juste dans la vue
|
||||
this.response = "Suppression terminée !";
|
||||
this.datas.splice(index, 1);
|
||||
this.processing = false;
|
||||
break;
|
||||
case 1:
|
||||
// 1 = Non supprimer car des questions lui sont lié
|
||||
this.response = "Erreur : des questions sont encore liées à ce thème.";
|
||||
setTimeout(function() {
|
||||
this.processing = false;
|
||||
}.bind(this), 1500);
|
||||
break;
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
this.response = "Erreur lors de la suppression.";
|
||||
this.processing = false;
|
||||
});
|
||||
} else {
|
||||
this.datas.splice(index, 1);
|
||||
}
|
||||
},
|
||||
saveData() {
|
||||
this.processing = true;
|
||||
this.response = "En cours de sauvegarde..."
|
||||
axios.post('/bo/ajax/save-faq-theme', {
|
||||
themes: this.datas,
|
||||
}).then(
|
||||
(response) => {
|
||||
this.datas = response.data;
|
||||
this.response = "Sauvegarde terminée !";
|
||||
this.processing = false;
|
||||
}
|
||||
)
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
this.response = "Erreur lors de la sauvegarde.";
|
||||
this.processing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
5
resources/views/bo/faq-questions/index.blade.php
Normal file
5
resources/views/bo/faq-questions/index.blade.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@extends('layouts.bo')
|
||||
|
||||
@section('content')
|
||||
<faq-questions dbdatas='@json($questions)' dbthemes='@json($themes)'/>
|
||||
@endsection
|
||||
5
resources/views/bo/faq-themes/index.blade.php
Normal file
5
resources/views/bo/faq-themes/index.blade.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@extends('layouts.bo')
|
||||
|
||||
@section('content')
|
||||
<faq-themes dbdatas='@json($themes)'/>
|
||||
@endsection
|
||||
|
|
@ -100,6 +100,13 @@
|
|||
<a class="dropdown-item" href="{{route('bo.content', 3)}}">Règlement</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button">FAQ</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="{{route('bo.faq-themes')}}">Thèmes</a>
|
||||
<a class="dropdown-item" href="{{route('bo.faq-questions')}}">Questions</a>
|
||||
</div>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
|
|
|
|||
|
|
@ -46,4 +46,12 @@ Route::namespace('Bo')
|
|||
Route::get('/banned-ip', 'BannedIPController@index')->name('banned-ip');
|
||||
Route::post('/ajax/delete-banned-ip', 'BannedIPController@destroy');
|
||||
Route::post('/ajax/save-banned-ip', 'BannedIPController@saveDatas');
|
||||
// FAQ - Thèmes
|
||||
Route::get('/faq-themes', 'FaqThemesController@index')->name('faq-themes');
|
||||
Route::post('/ajax/delete-faq-theme', 'FaqThemesController@destroy');
|
||||
Route::post('/ajax/save-faq-theme', 'FaqThemesController@saveDatas');
|
||||
// FAQ - Questions
|
||||
Route::get('/faq-questions', 'FaqQuestionsController@index')->name('faq-questions');
|
||||
Route::post('/ajax/delete-faq-question', 'FaqQuestionsController@destroy');
|
||||
Route::post('/ajax/save-faq-question', 'FaqQuestionsController@saveDatas');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue