feat: Implement testimonial management with dedicated UI abnd database
This commit is contained in:
parent
cb9e61ee7f
commit
9bd6997359
51
app/Livewire/Datatable/Information/TestimonialsTable.php
Normal file
51
app/Livewire/Datatable/Information/TestimonialsTable.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Datatable\Information;
|
||||||
|
|
||||||
|
use App\Enums\IsShow;
|
||||||
|
use App\Models\Testimonial;
|
||||||
|
use App\Traits\Datatable\WithConfiguration;
|
||||||
|
use App\Traits\Datatable\WithPrependColumn;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Facades\Blade;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
||||||
|
use Rappasoft\LaravelLivewireTables\Views\Column;
|
||||||
|
|
||||||
|
class TestimonialsTable extends DataTableComponent
|
||||||
|
{
|
||||||
|
use WithConfiguration, WithPrependColumn;
|
||||||
|
|
||||||
|
protected $model = Testimonial::class;
|
||||||
|
|
||||||
|
public function columns(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Column::make('Nama', 'user.customer.name')
|
||||||
|
->searchable(),
|
||||||
|
|
||||||
|
Column::make('Konten', 'content')
|
||||||
|
->searchable()
|
||||||
|
->format(fn ($value) => Str::limit($value, 50)),
|
||||||
|
|
||||||
|
Column::make('Rating', 'rating')
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Column::make('Visibilitas', 'is_show')
|
||||||
|
->label(fn ($row, $column) => Blade::render('<flux:switch wire:change="toggleShow('.$row->id.')" :checked="$checked" />', ['checked' => $row->is_show === IsShow::SHOW]))
|
||||||
|
->html(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleShow(Testimonial $testimonial): void
|
||||||
|
{
|
||||||
|
$testimonial->update([
|
||||||
|
'is_show' => $testimonial->is_show === IsShow::SHOW ? IsShow::HIDDEN : IsShow::SHOW,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function builder(): Builder
|
||||||
|
{
|
||||||
|
return Testimonial::select('testimonials.id', 'rating', 'content', 'is_show', 'testimonials.created_at')->with(['user', 'user.customer']);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/Livewire/Studio/Information/Testimonial.php
Normal file
18
app/Livewire/Studio/Information/Testimonial.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Studio\Information;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Attributes\Title;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
#[Title('Testimoni')]
|
||||||
|
class Testimonial extends Component
|
||||||
|
{
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.studio.information.testimonials', [
|
||||||
|
'pageTitle' => 'Testimoni',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Models/Testimonial.php
Normal file
32
app/Models/Testimonial.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\IsShow;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class Testimonial extends Model
|
||||||
|
{
|
||||||
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'is_show' => IsShow::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Scope]
|
||||||
|
public function show(Builder $query): void
|
||||||
|
{
|
||||||
|
$query->where('is_show', IsShow::SHOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -229,6 +229,13 @@ public function boot(): void
|
|||||||
'match' => 'studio.information.faq.*',
|
'match' => 'studio.information.faq.*',
|
||||||
'can' => 'view faq',
|
'can' => 'view faq',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => 'Testimonial',
|
||||||
|
'icon' => 'chat-bubble-left-ellipsis',
|
||||||
|
'route' => 'studio.information.testimonial.index',
|
||||||
|
'match' => 'studio.information.testimonial.*',
|
||||||
|
'can' => 'view testimonial',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\IsShow;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('testimonials', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->text('content');
|
||||||
|
$table->integer('rating');
|
||||||
|
$table->enum('is_show', IsShow::values())->default(IsShow::SHOW)->comment(IsShow::comment());
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('testimonials');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -32,6 +32,7 @@ public function run(): void
|
|||||||
FaqSeeder::class,
|
FaqSeeder::class,
|
||||||
PerfumeFeatureSeeder::class,
|
PerfumeFeatureSeeder::class,
|
||||||
ChooseUsSeeder::class,
|
ChooseUsSeeder::class,
|
||||||
|
TestimonialSeeder::class,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,6 +137,11 @@ public function run(): void
|
|||||||
'update faq',
|
'update faq',
|
||||||
'delete faq',
|
'delete faq',
|
||||||
|
|
||||||
|
'view testimonial',
|
||||||
|
'create testimonial',
|
||||||
|
'update testimonial',
|
||||||
|
'delete testimonial',
|
||||||
|
|
||||||
'view broadcast',
|
'view broadcast',
|
||||||
'create broadcast',
|
'create broadcast',
|
||||||
'delete broadcast',
|
'delete broadcast',
|
||||||
|
|||||||
69
database/seeders/TestimonialSeeder.php
Normal file
69
database/seeders/TestimonialSeeder.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Enums\IsShow;
|
||||||
|
use App\Models\Testimonial;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class TestimonialSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$testimonials = [
|
||||||
|
[
|
||||||
|
'content' => 'Parfumnya wangi banget, tahan lama seharian! Recommended banget pokoknya.',
|
||||||
|
'rating' => 5,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Pengiriman cepat, packing aman. Wanginya juga sesuai ekspektasi. Terima kasih!',
|
||||||
|
'rating' => 5,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Wanginya enak, tapi sayang kurang tahan lama di luar ruangan yg panas. Tapi worth it lah harga segini.',
|
||||||
|
'rating' => 4,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Suka banget sama varian vanilla-nya. Manis tapi nggak bikin pusing. Bakal repeat order sih ini.',
|
||||||
|
'rating' => 5,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Botolnya pecah dikit pas nyampe, mungkin dari ekspedisinya. Tapi parfumnya masih oke.',
|
||||||
|
'rating' => 3,
|
||||||
|
'is_show' => IsShow::HIDDEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Pelayanan ramah, admin fast respon. Parfumnya juga original. Top marketop!',
|
||||||
|
'rating' => 5,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Aromanya mewah, cocok buat kondangan. Temen-temen pada nanyain pake parfum apa.',
|
||||||
|
'rating' => 5,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'content' => 'Lumayan lah buat sehari-hari. Gak terlalu menyengat.',
|
||||||
|
'rating' => 4,
|
||||||
|
'is_show' => IsShow::SHOW,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($testimonials as $testimonial) {
|
||||||
|
Testimonial::create([
|
||||||
|
'user_id' => User::whereHas('customer')->inRandomOrder()->first()->id,
|
||||||
|
'content' => $testimonial['content'],
|
||||||
|
'rating' => $testimonial['rating'],
|
||||||
|
'is_show' => $testimonial['is_show'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<flux:main>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<livewire:datatable.information.testimonials-table />
|
||||||
|
</div>
|
||||||
|
</flux:main>
|
||||||
@ -24,6 +24,7 @@
|
|||||||
use App\Livewire\Studio\Information\Broadcast as BroadcastComponent;
|
use App\Livewire\Studio\Information\Broadcast as BroadcastComponent;
|
||||||
use App\Livewire\Studio\Information\Faq as FaqComponent;
|
use App\Livewire\Studio\Information\Faq as FaqComponent;
|
||||||
use App\Livewire\Studio\Information\PriceRequest as PriceRequestComponent;
|
use App\Livewire\Studio\Information\PriceRequest as PriceRequestComponent;
|
||||||
|
use App\Livewire\Studio\Information\Testimonial as TestimonialComponent;
|
||||||
use App\Livewire\Studio\Loyalty\Customer as CustomerComponent;
|
use App\Livewire\Studio\Loyalty\Customer as CustomerComponent;
|
||||||
use App\Livewire\Studio\Loyalty\RedemptionVerification;
|
use App\Livewire\Studio\Loyalty\RedemptionVerification;
|
||||||
use App\Livewire\Studio\Loyalty\Reward as RewardComponent;
|
use App\Livewire\Studio\Loyalty\Reward as RewardComponent;
|
||||||
@ -184,6 +185,10 @@
|
|||||||
Route::prefix('faqs')->name('faq.')->group(function () {
|
Route::prefix('faqs')->name('faq.')->group(function () {
|
||||||
Route::get('/', FaqComponent::class)->name('index')->middleware('can:view faq');
|
Route::get('/', FaqComponent::class)->name('index')->middleware('can:view faq');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::prefix('testimonials')->name('testimonial.')->group(function () {
|
||||||
|
Route::get('/', TestimonialComponent::class)->name('index')->middleware('can:view testimonial');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::prefix('feature')->name('studio.feature.')->group(function () {
|
Route::prefix('feature')->name('studio.feature.')->group(function () {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user