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.*',
|
||||
'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,
|
||||
PerfumeFeatureSeeder::class,
|
||||
ChooseUsSeeder::class,
|
||||
TestimonialSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +137,11 @@ public function run(): void
|
||||
'update faq',
|
||||
'delete faq',
|
||||
|
||||
'view testimonial',
|
||||
'create testimonial',
|
||||
'update testimonial',
|
||||
'delete testimonial',
|
||||
|
||||
'view broadcast',
|
||||
'create 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\Faq as FaqComponent;
|
||||
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\RedemptionVerification;
|
||||
use App\Livewire\Studio\Loyalty\Reward as RewardComponent;
|
||||
@ -184,6 +185,10 @@
|
||||
Route::prefix('faqs')->name('faq.')->group(function () {
|
||||
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 () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user