refactor: migrate general settings to spatie/laravel-settings and rename properties for consistency
This commit is contained in:
parent
044dffefa8
commit
3fcdaa7475
@ -8,7 +8,6 @@
|
||||
use App\Enums\PriceType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\Manage\Order\OrderRequest;
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Product;
|
||||
@ -122,7 +121,6 @@ public function show(Order $order): Response
|
||||
|
||||
return Inertia::render('admin/manage/order/show', [
|
||||
'order' => $order,
|
||||
'setting' => GeneralSetting::first(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\System\GeneralSettingRequest;
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
@ -13,37 +14,47 @@ class GeneralSettingController extends Controller
|
||||
{
|
||||
public function index(): Response
|
||||
{
|
||||
return Inertia::render('admin/system/settings/index', [
|
||||
'setting' => GeneralSetting::first(),
|
||||
]);
|
||||
return Inertia::render('admin/system/settings/index');
|
||||
}
|
||||
|
||||
public function update(GeneralSettingRequest $request): RedirectResponse
|
||||
public function update(GeneralSettingRequest $request, GeneralSettings $settings): RedirectResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
$setting = GeneralSetting::first();
|
||||
if ($setting) {
|
||||
$setting->update($validated);
|
||||
} else {
|
||||
$setting = GeneralSetting::create($validated);
|
||||
}
|
||||
$settings->site_name = $validated['site_name'];
|
||||
$settings->site_description = $validated['site_description'];
|
||||
$settings->site_address = $validated['site_address'];
|
||||
$settings->site_phone = $validated['site_phone'];
|
||||
|
||||
if ($request->hasFile('logo_light')) {
|
||||
$setting->addMediaFromRequest('logo_light')->toMediaCollection('logo_light');
|
||||
if ($settings->logo_light) {
|
||||
Storage::disk('public')->delete($settings->logo_light);
|
||||
}
|
||||
$settings->logo_light = $request->file('logo_light')->store('settings', 'public');
|
||||
}
|
||||
|
||||
if ($request->hasFile('logo_dark')) {
|
||||
$setting->addMediaFromRequest('logo_dark')->toMediaCollection('logo_dark');
|
||||
if ($settings->logo_dark) {
|
||||
Storage::disk('public')->delete($settings->logo_dark);
|
||||
}
|
||||
$settings->logo_dark = $request->file('logo_dark')->store('settings', 'public');
|
||||
}
|
||||
|
||||
if ($request->hasFile('icon_light')) {
|
||||
$setting->addMediaFromRequest('icon_light')->toMediaCollection('icon_light');
|
||||
if ($settings->icon_light) {
|
||||
Storage::disk('public')->delete($settings->icon_light);
|
||||
}
|
||||
$settings->icon_light = $request->file('icon_light')->store('settings', 'public');
|
||||
}
|
||||
|
||||
if ($request->hasFile('icon_dark')) {
|
||||
$setting->addMediaFromRequest('icon_dark')->toMediaCollection('icon_dark');
|
||||
if ($settings->icon_dark) {
|
||||
Storage::disk('public')->delete($settings->icon_dark);
|
||||
}
|
||||
$settings->icon_dark = $request->file('icon_dark')->store('settings', 'public');
|
||||
}
|
||||
|
||||
$settings->save();
|
||||
|
||||
return redirect()->back()->with('success', 'Pengaturan berhasil diperbarui');
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Models\Product;
|
||||
|
||||
class HomepageController extends Controller
|
||||
@ -14,7 +13,6 @@ public function __invoke()
|
||||
$categorySlug = request('category');
|
||||
|
||||
return inertia('homepage', [
|
||||
'setting' => GeneralSetting::first(),
|
||||
'categories' => Category::active()->get(),
|
||||
'bestSellers' => Product::with(['categories' => fn ($q) => $q->active(), 'prices'])
|
||||
->when($search, function ($query, $search) {
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\GeneralSetting;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
@ -39,7 +38,6 @@ public function share(Request $request): array
|
||||
return [
|
||||
...parent::share($request),
|
||||
'name' => config('app.name'),
|
||||
'setting' => GeneralSetting::first(),
|
||||
'auth' => [
|
||||
'user' => $request->user() ? $request->user()->load('profile') : null,
|
||||
'roles' => $request->user() ? $request->user()->getRoleNames() : [],
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Requests\Admin\System;
|
||||
|
||||
use App\Models\GeneralSetting;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@ -23,16 +22,14 @@ public function authorize(): bool
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$settingExists = GeneralSetting::exists();
|
||||
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:50'],
|
||||
'description' => ['required', 'string'],
|
||||
'address' => ['required', 'string'],
|
||||
'phone' => ['required', 'string', 'max:20'],
|
||||
'logo_light' => [$settingExists ? 'nullable' : 'required', 'image', 'max:2048'],
|
||||
'site_name' => ['required', 'string', 'max:50'],
|
||||
'site_description' => ['required', 'string'],
|
||||
'site_address' => ['required', 'string'],
|
||||
'site_phone' => ['required', 'string', 'max:20'],
|
||||
'logo_light' => ['nullable', 'image', 'max:2048'],
|
||||
'logo_dark' => ['nullable', 'image', 'max:2048'],
|
||||
'icon_light' => [$settingExists ? 'nullable' : 'required', 'image', 'max:1024'],
|
||||
'icon_light' => ['nullable', 'image', 'max:1024'],
|
||||
'icon_dark' => ['nullable', 'image', 'max:1024'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends(['logo_light_url', 'logo_dark_url', 'icon_light_url', 'icon_dark_url'])]
|
||||
class GeneralSetting extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory, InteractsWithMedia, LogsActivity;
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('logo_light')
|
||||
->singleFile();
|
||||
|
||||
$this->addMediaCollection('logo_dark')
|
||||
->singleFile();
|
||||
|
||||
$this->addMediaCollection('icon_light')
|
||||
->singleFile();
|
||||
|
||||
$this->addMediaCollection('icon_dark')
|
||||
->singleFile();
|
||||
}
|
||||
|
||||
protected function logoLightUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->getFirstMediaUrl('logo_light') ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function logoDarkUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->getFirstMediaUrl('logo_dark') ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function iconLightUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->getFirstMediaUrl('icon_light') ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function iconDarkUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->getFirstMediaUrl('icon_dark') ?: null,
|
||||
);
|
||||
}
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
{
|
||||
return LogOptions::defaults()
|
||||
->logOnly(['name', 'description', 'address', 'phone'])
|
||||
->logOnlyDirty()
|
||||
->useLogName('Pengaturan Umum');
|
||||
}
|
||||
|
||||
public function tapActivity(Activity $activity, string $eventName)
|
||||
{
|
||||
$activity->description = match ($eventName) {
|
||||
'created' => 'TAMBAH',
|
||||
'updated' => 'UBAH',
|
||||
'deleted' => 'HAPUS',
|
||||
default => $activity->description,
|
||||
};
|
||||
|
||||
if (isset($activity->properties['attributes'])) {
|
||||
$attributeMap = [
|
||||
'name' => 'Nama Aplikasi',
|
||||
'description' => 'Deskripsi',
|
||||
'address' => 'Alamat',
|
||||
'phone' => 'No. Telepon',
|
||||
];
|
||||
|
||||
$properties = $activity->properties->toArray();
|
||||
|
||||
$localizeValues = function ($attrs) use ($attributeMap) {
|
||||
$newAttrs = [];
|
||||
foreach ($attrs as $key => $value) {
|
||||
$label = $attributeMap[$key] ?? $key;
|
||||
$newAttrs[$label] = $value;
|
||||
}
|
||||
|
||||
return $newAttrs;
|
||||
};
|
||||
|
||||
$properties['attributes'] = $localizeValues($properties['attributes']);
|
||||
|
||||
if (isset($properties['old'])) {
|
||||
$properties['old'] = $localizeValues($properties['old']);
|
||||
}
|
||||
|
||||
$activity->properties = collect($properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
app/Providers/ViewServiceProvider.php
Normal file
75
app/Providers/ViewServiceProvider.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* View Service Provider
|
||||
*
|
||||
* This provider handles global data sharing for both Inertia and Blade views.
|
||||
*/
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class ViewServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
if ($this->app->runningInConsole() && ! $this->app->environment('testing')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Share settings with Inertia
|
||||
Inertia::share('setting', function () {
|
||||
try {
|
||||
$settings = app(GeneralSettings::class);
|
||||
|
||||
return [
|
||||
'site_name' => $settings->site_name,
|
||||
'site_description' => $settings->site_description,
|
||||
'site_address' => $settings->site_address,
|
||||
'site_phone' => $settings->site_phone,
|
||||
'logo_light_url' => $settings->logo_light ? Storage::url($settings->logo_light) : null,
|
||||
'logo_dark_url' => $settings->logo_dark ? Storage::url($settings->logo_dark) : null,
|
||||
'icon_light_url' => $settings->icon_light ? Storage::url($settings->icon_light) : null,
|
||||
'icon_dark_url' => $settings->icon_dark ? Storage::url($settings->icon_dark) : null,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
// Share settings with Blade views
|
||||
View::composer('*', function ($view) {
|
||||
try {
|
||||
$settings = app(GeneralSettings::class);
|
||||
$view->with('setting', [
|
||||
'site_name' => $settings->site_name,
|
||||
'site_description' => $settings->site_description,
|
||||
'site_address' => $settings->site_address,
|
||||
'site_phone' => $settings->site_phone,
|
||||
'logo_light_url' => $settings->logo_light ? Storage::url($settings->logo_light) : null,
|
||||
'logo_dark_url' => $settings->logo_dark ? Storage::url($settings->logo_dark) : null,
|
||||
'icon_light_url' => $settings->icon_light ? Storage::url($settings->icon_light) : null,
|
||||
'icon_dark_url' => $settings->icon_dark ? Storage::url($settings->icon_dark) : null,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$view->with('setting', []);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
app/Settings/GeneralSettings.php
Normal file
29
app/Settings/GeneralSettings.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Settings;
|
||||
|
||||
use Spatie\LaravelSettings\Settings;
|
||||
|
||||
class GeneralSettings extends Settings
|
||||
{
|
||||
public string $site_name;
|
||||
|
||||
public string $site_description;
|
||||
|
||||
public string $site_address;
|
||||
|
||||
public string $site_phone;
|
||||
|
||||
public ?string $logo_light;
|
||||
|
||||
public ?string $logo_dark;
|
||||
|
||||
public ?string $icon_light;
|
||||
|
||||
public ?string $icon_dark;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
return 'general';
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,10 @@
|
||||
|
||||
use App\Providers\AppServiceProvider;
|
||||
use App\Providers\FortifyServiceProvider;
|
||||
use App\Providers\ViewServiceProvider;
|
||||
|
||||
return [
|
||||
AppServiceProvider::class,
|
||||
FortifyServiceProvider::class,
|
||||
ViewServiceProvider::class,
|
||||
];
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
"spatie/laravel-activitylog": "^4.12",
|
||||
"spatie/laravel-medialibrary": "^11.21",
|
||||
"spatie/laravel-permission": "^7.3",
|
||||
"spatie/laravel-settings": "^3.8",
|
||||
"spatie/laravel-sluggable": "^3.8"
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
405
composer.lock
generated
405
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": "1e97290615923580b9a565671ca7bfae",
|
||||
"content-hash": "a42046c1187875d3d873b061a01fa885",
|
||||
"packages": [
|
||||
{
|
||||
"name": "archtechx/enums",
|
||||
@ -445,6 +445,54 @@
|
||||
},
|
||||
"time": "2024-07-08T12:26:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "1.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/deprecations.git",
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<=7.5 || >=14"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9 || ^12 || ^14",
|
||||
"phpstan/phpstan": "1.4.10 || 2.1.30",
|
||||
"phpstan/phpstan-phpunit": "^1.0 || ^2",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Deprecations\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/deprecations/issues",
|
||||
"source": "https://github.com/doctrine/deprecations/tree/1.1.6"
|
||||
},
|
||||
"time": "2026-02-07T07:09:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
"version": "2.1.0",
|
||||
@ -3976,6 +4024,117 @@
|
||||
},
|
||||
"time": "2025-09-24T15:06:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-2.x": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jaap van Otterdijk",
|
||||
"email": "opensource@ijaap.nl"
|
||||
}
|
||||
],
|
||||
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
|
||||
"homepage": "http://www.phpdoc.org",
|
||||
"keywords": [
|
||||
"FQSEN",
|
||||
"phpDocumentor",
|
||||
"phpdoc",
|
||||
"reflection",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
|
||||
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
|
||||
},
|
||||
"time": "2020-06-27T09:03:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9",
|
||||
"reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0",
|
||||
"phpstan/phpdoc-parser": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"psalm/phar": "^4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-1.x": "1.x-dev",
|
||||
"dev-2.x": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike van Riel",
|
||||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0"
|
||||
},
|
||||
"time": "2026-01-06T21:53:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.5",
|
||||
@ -5316,6 +5475,91 @@
|
||||
],
|
||||
"time": "2026-04-07T15:19:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-settings",
|
||||
"version": "3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-settings.git",
|
||||
"reference": "09b788ee96d205699420dedb8a6aa8c4c8af84fe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-settings/zipball/09b788ee96d205699420dedb8a6aa8c4c8af84fe",
|
||||
"reference": "09b788ee96d205699420dedb8a6aa8c4c8af84fe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/database": "^11.0|^12.0|^13.0",
|
||||
"php": "^8.2",
|
||||
"phpdocumentor/type-resolver": "^1.5|^2.0",
|
||||
"spatie/temporary-directory": "^1.3|^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-redis": "*",
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^9.0|^10.0|^11.0",
|
||||
"pestphp/pest": "^2.0|^3.0|^4.0",
|
||||
"pestphp/pest-plugin-laravel": "^2.0|^3.0|^4.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"spatie/laravel-data": "^2.0.0|^4.0.0",
|
||||
"spatie/pest-plugin-snapshots": "^2.0",
|
||||
"spatie/phpunit-snapshot-assertions": "^4.2|^5.0",
|
||||
"spatie/ray": "^1.36"
|
||||
},
|
||||
"suggest": {
|
||||
"spatie/data-transfer-object": "Allows for DTO casting to settings. (deprecated)"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Spatie\\LaravelSettings\\LaravelSettingsServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\LaravelSettings\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ruben Van Assche",
|
||||
"email": "ruben@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Store your application settings",
|
||||
"homepage": "https://github.com/spatie/laravel-settings",
|
||||
"keywords": [
|
||||
"laravel-settings",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/laravel-settings/issues",
|
||||
"source": "https://github.com/spatie/laravel-settings/tree/3.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://spatie.be/open-source/support-us",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/spatie",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-04-28T07:05:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-sluggable",
|
||||
"version": "3.8.1",
|
||||
@ -8245,54 +8489,6 @@
|
||||
],
|
||||
"time": "2026-03-29T15:46:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "1.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/deprecations.git",
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<=7.5 || >=14"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9 || ^12 || ^14",
|
||||
"phpstan/phpstan": "1.4.10 || 2.1.30",
|
||||
"phpstan/phpstan-phpunit": "^1.0 || ^2",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Deprecations\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/deprecations/issues",
|
||||
"source": "https://github.com/doctrine/deprecations/tree/1.1.6"
|
||||
},
|
||||
"time": "2026-02-07T07:09:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fakerphp/faker",
|
||||
"version": "v1.24.1",
|
||||
@ -9630,59 +9826,6 @@
|
||||
},
|
||||
"time": "2022-02-21T01:04:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-2.x": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jaap van Otterdijk",
|
||||
"email": "opensource@ijaap.nl"
|
||||
}
|
||||
],
|
||||
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
|
||||
"homepage": "http://www.phpdoc.org",
|
||||
"keywords": [
|
||||
"FQSEN",
|
||||
"phpDocumentor",
|
||||
"phpdoc",
|
||||
"reflection",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
|
||||
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
|
||||
},
|
||||
"time": "2020-06-27T09:03:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "6.0.3",
|
||||
@ -9748,64 +9891,6 @@
|
||||
},
|
||||
"time": "2026-03-18T20:49:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9",
|
||||
"reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0",
|
||||
"phpstan/phpdoc-parser": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/extension-installer": "^1.4",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"psalm/phar": "^4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-1.x": "1.x-dev",
|
||||
"dev-2.x": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\Reflection\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike van Riel",
|
||||
"email": "me@mikevanriel.com"
|
||||
}
|
||||
],
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0"
|
||||
},
|
||||
"time": "2026-01-06T21:53:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "12.5.6",
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create(config('settings.repositories.database.table') ?? 'settings', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
|
||||
$table->string('group');
|
||||
$table->string('name');
|
||||
$table->boolean('locked')->default(false);
|
||||
$table->json('payload');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['group', 'name']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
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('general_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 50);
|
||||
$table->text('description');
|
||||
$table->text('address');
|
||||
$table->string('phone', 20);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('general_settings');
|
||||
}
|
||||
};
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class GeneralSettingSeeder extends Seeder
|
||||
@ -12,11 +12,11 @@ class GeneralSettingSeeder extends Seeder
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
GeneralSetting::updateOrCreate([
|
||||
'name' => 'VN Grup',
|
||||
'description' => 'VN Grup adalah Toko Baju Daster Online yang menghadirkan koleksi baju daster berkualitas tinggi untuk wanita yang mengutamakan kenyamanan tanpa mengorbankan gaya. Kami menyediakan berbagai macam daster dengan desain yang beragam, mulai dari yang sederhana dan klasik hingga yang modern dan trendi, cocok untuk berbagai kebutuhan, baik di rumah maupun saat bersantai di luar.',
|
||||
'address' => 'Jl. Raya Pabuaran-Cipeunduy, Kec. Pabuaran, Kab. Subang, Jawa Barat',
|
||||
'phone' => '089679965828',
|
||||
]);
|
||||
$settings = app(GeneralSettings::class);
|
||||
$settings->site_name = 'VN Grup';
|
||||
$settings->site_description = 'VN Grup adalah Toko Baju Daster Online yang menghadirkan koleksi baju daster berkualitas tinggi untuk wanita yang mengutamakan kenyamanan tanpa mengorbankan gaya. Kami menyediakan berbagai macam daster dengan desain yang beragam, mulai dari yang sederhana dan klasik hingga yang modern dan trendi, cocok untuk berbagai kebutuhan, baik di rumah maupun saat bersantai di luar.';
|
||||
$settings->site_address = 'Jl. Raya Pabuaran-Cipeunduy, Kec. Pabuaran, Kab. Subang, Jawa Barat';
|
||||
$settings->site_phone = '089679965828';
|
||||
$settings->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||
|
||||
return new class extends SettingsMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$this->migrator->add('general.site_name', 'VN Grup');
|
||||
$this->migrator->add('general.site_description', 'VN Grup adalah Toko Baju Daster Online yang menghadirkan koleksi baju daster berkualitas tinggi untuk wanita yang mengutamakan kenyamanan tanpa mengorbankan gaya.');
|
||||
$this->migrator->add('general.site_address', 'Jl. Raya Pabuaran-Cipeunduy, Kec. Pabuaran, Kab. Subang, Jawa Barat');
|
||||
$this->migrator->add('general.site_phone', '089679965828');
|
||||
$this->migrator->add('general.logo_light', null);
|
||||
$this->migrator->add('general.logo_dark', null);
|
||||
$this->migrator->add('general.icon_light', null);
|
||||
$this->migrator->add('general.icon_dark', null);
|
||||
}
|
||||
};
|
||||
@ -9,20 +9,20 @@ export default function AppLogo() {
|
||||
{setting?.logo_light_url ? (
|
||||
<img
|
||||
src={setting.logo_light_url}
|
||||
alt={setting.name}
|
||||
alt={setting.site_name}
|
||||
className="max-h-8 w-auto dark:hidden"
|
||||
/>
|
||||
) : null}
|
||||
{setting?.logo_dark_url ? (
|
||||
<img
|
||||
src={setting.logo_dark_url}
|
||||
alt={setting.name}
|
||||
alt={setting.site_name}
|
||||
className="hidden max-h-8 w-auto dark:block"
|
||||
/>
|
||||
) : null}
|
||||
{!setting?.logo_light_url && !setting?.logo_dark_url && (
|
||||
<span className="truncate font-semibold tracking-tight">
|
||||
{setting?.name || 'VN Grup'}
|
||||
{setting?.site_name || 'VN Grup'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -62,17 +62,17 @@ export default function OrderShow({ order, setting }: ShowProps) {
|
||||
<img src={setting.logo_light_url} alt="Logo" className="h-10 w-auto" />
|
||||
) : (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary text-2xl font-bold text-primary-foreground">
|
||||
{setting?.name?.charAt(0) || 'V'}
|
||||
{setting?.site_name?.charAt(0) || 'V'}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-foreground">{setting?.name || 'VN Grup'}</h2>
|
||||
<h2 className="text-2xl font-bold text-foreground">{setting?.site_name || 'VN Grup'}</h2>
|
||||
<p className="text-sm text-muted-foreground italic">Your Style, Our Passion</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-xs space-y-1 text-sm text-muted-foreground">
|
||||
<p>{setting?.address || 'Alamat Toko Belum Diatur'}</p>
|
||||
<p>{setting?.phone || '-'}</p>
|
||||
<p>{setting?.site_address || 'Alamat Toko Belum Diatur'}</p>
|
||||
<p>{setting?.site_phone || '-'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -147,7 +147,7 @@ export default function OrderShow({ order, setting }: ShowProps) {
|
||||
<div className="rounded-xl bg-muted/30 p-4 space-y-2 border border-dashed">
|
||||
<h4 className="text-xs font-bold uppercase tracking-wider text-muted-foreground">Catatan:</h4>
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||
Terima kasih telah berbelanja di {setting?.name || 'VN Grup'}.
|
||||
Terima kasih telah berbelanja di {setting?.site_name || 'VN Grup'}.
|
||||
Barang yang sudah dibeli tidak dapat ditukar atau dikembalikan kecuali ada perjanjian sebelumnya.
|
||||
Simpan invoice ini sebagai bukti pembelian yang sah.
|
||||
</p>
|
||||
@ -181,7 +181,7 @@ export default function OrderShow({ order, setting }: ShowProps) {
|
||||
|
||||
<div className="pt-10 text-center text-sm text-muted-foreground print:pt-20">
|
||||
<p className="font-medium">Semoga hari Anda menyenangkan!</p>
|
||||
<p className="text-[10px] mt-1 opacity-50">Generated by {setting?.name || 'VN Grup'} System</p>
|
||||
<p className="text-[10px] mt-1 opacity-50">Generated by {setting?.site_name || 'VN Grup'} System</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -17,10 +17,10 @@ export default function SettingIndex({
|
||||
setting: GeneralSetting | null;
|
||||
}) {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
name: setting?.name || '',
|
||||
description: setting?.description || '',
|
||||
address: setting?.address || '',
|
||||
phone: setting?.phone || '',
|
||||
site_name: setting?.site_name || '',
|
||||
site_description: setting?.site_description || '',
|
||||
site_address: setting?.site_address || '',
|
||||
site_phone: setting?.site_phone || '',
|
||||
logo_light: null as File | null,
|
||||
logo_dark: null as File | null,
|
||||
icon_light: null as File | null,
|
||||
@ -107,41 +107,41 @@ export default function SettingIndex({
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||
<Field>
|
||||
<Label htmlFor="phone" required>
|
||||
<Label htmlFor="site_phone" required>
|
||||
No. Telepon
|
||||
</Label>
|
||||
<Input
|
||||
id="phone"
|
||||
value={data.phone || ''}
|
||||
id="site_phone"
|
||||
value={data.site_phone || ''}
|
||||
onChange={(e) =>
|
||||
setData('phone', e.target.value)
|
||||
setData('site_phone', e.target.value)
|
||||
}
|
||||
placeholder="0812xxxx"
|
||||
/>
|
||||
<FieldError
|
||||
error={errors.phone}
|
||||
error={errors.site_phone}
|
||||
label="No. Telepon"
|
||||
className="text-xs"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="name" required>
|
||||
<Label htmlFor="site_name" required>
|
||||
Nama Aplikasi
|
||||
</Label>
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
value={data.name}
|
||||
id="site_name"
|
||||
name="site_name"
|
||||
value={data.site_name}
|
||||
onChange={(e) =>
|
||||
setData('name', e.target.value)
|
||||
setData('site_name', e.target.value)
|
||||
}
|
||||
autoComplete="off"
|
||||
placeholder="Contoh: VN Grup Dress"
|
||||
maxLength={100}
|
||||
/>
|
||||
<FieldError
|
||||
error={errors.name}
|
||||
error={errors.site_name}
|
||||
label="Nama Aplikasi"
|
||||
className="text-xs"
|
||||
/>
|
||||
@ -149,15 +149,15 @@ export default function SettingIndex({
|
||||
</div>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="description" required>
|
||||
<Label htmlFor="site_description" required>
|
||||
Deskripsi
|
||||
</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={data.description || ''}
|
||||
id="site_description"
|
||||
value={data.site_description || ''}
|
||||
onChange={(e) =>
|
||||
setData(
|
||||
'description',
|
||||
'site_description',
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
@ -165,27 +165,27 @@ export default function SettingIndex({
|
||||
rows={4}
|
||||
/>
|
||||
<FieldError
|
||||
error={errors.description}
|
||||
error={errors.site_description}
|
||||
label="Deskripsi"
|
||||
className="mt-1 text-xs"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="address" required>
|
||||
<Label htmlFor="site_address" required>
|
||||
Alamat
|
||||
</Label>
|
||||
<Textarea
|
||||
id="address"
|
||||
value={data.address || ''}
|
||||
id="site_address"
|
||||
value={data.site_address || ''}
|
||||
onChange={(e) =>
|
||||
setData('address', e.target.value)
|
||||
setData('site_address', e.target.value)
|
||||
}
|
||||
placeholder="Alamat lengkap toko"
|
||||
rows={3}
|
||||
/>
|
||||
<FieldError
|
||||
error={errors.address}
|
||||
error={errors.site_address}
|
||||
label="Alamat"
|
||||
className="mt-1 text-xs"
|
||||
/>
|
||||
|
||||
@ -141,11 +141,11 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
{setting?.logo_light_url ? (
|
||||
<img
|
||||
src={setting.logo_light_url}
|
||||
alt={setting.name}
|
||||
alt={setting.site_name}
|
||||
className="max-h-8 w-auto"
|
||||
/>
|
||||
) : (
|
||||
setting?.name || 'VN Grup'
|
||||
setting?.site_name || 'VN Grup'
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -334,7 +334,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
<p className="mb-4 text-sm font-medium opacity-60">{formatCurrency(item.retail_price)}</p>
|
||||
|
||||
<a
|
||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo ${setting?.name || 'VN Grup'}, saya ingin memesan ${item.name} seharga ${formatCurrency(item.retail_price)}. Mohon info detail pembayarannya.`)}`}
|
||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo ${setting?.site_name || 'VN Grup'}, saya ingin memesan ${item.name} seharga ${formatCurrency(item.retail_price)}. Mohon info detail pembayarannya.`)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 border border-[#1e4a6d] px-6 py-2 text-[8px] font-bold uppercase tracking-widest transition-all hover:bg-[#1e4a6d] hover:text-white"
|
||||
@ -412,10 +412,10 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
{/* Brand Info */}
|
||||
<div className="lg:col-span-6">
|
||||
<div className="mb-6 flex items-center gap-2 text-xl font-bold uppercase tracking-tighter">
|
||||
{setting?.logo_light_url ? <img src={setting.logo_light_url} alt={setting.name} className="max-h-8" /> : (setting?.name || 'VN Grup')}
|
||||
{setting?.logo_light_url ? <img src={setting.logo_light_url} alt={setting.site_name} className="max-h-8" /> : (setting?.site_name || 'VN Grup')}
|
||||
</div>
|
||||
<p className="mb-8 text-sm leading-relaxed opacity-60">
|
||||
{setting?.description || 'Menghadirkan produk esensial yang tak lekang oleh waktu dengan fokus pada kualitas, keberlanjutan, dan gaya yang abadi.'}
|
||||
{setting?.site_description || 'Menghadirkan produk esensial yang tak lekang oleh waktu dengan fokus pada kualitas, keberlanjutan, dan gaya yang abadi.'}
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<Facebook size={18} className="cursor-pointer hover:opacity-50" />
|
||||
@ -443,13 +443,13 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
<Search size={14} className="rotate-45" />
|
||||
</div>
|
||||
<p className="leading-relaxed whitespace-pre-line">
|
||||
{setting?.address || 'Jl. Raya Utama No. 123, \n Kec. Lengkong, Kota Bandung, \n Jawa Barat 40262'}
|
||||
{setting?.site_address || 'Jl. Raya Utama No. 123, \n Kec. Lengkong, Kota Bandung, \n Jawa Barat 40262'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Phone size={14} />
|
||||
<a href={`https://wa.me/${(setting?.phone || '+628123456789').replace(/\D/g, '')}`} target="_blank" className="hover:underline">
|
||||
{setting?.phone || '+62 812 3456 789'}
|
||||
<a href={`https://wa.me/${(setting?.site_phone || '+628123456789').replace(/\D/g, '')}`} target="_blank" className="hover:underline">
|
||||
{setting?.site_phone || '+62 812 3456 789'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -458,7 +458,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="px-6 py-6 text-center text-[10px] font-bold uppercase tracking-widest lg:px-12 lg:text-left">
|
||||
<p className="opacity-50">© {new Date().getFullYear()} {setting?.name || 'VN Grup'}.</p>
|
||||
<p className="opacity-50">© {new Date().getFullYear()} {setting?.site_name || 'VN Grup'}.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@ -564,7 +564,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
||||
|
||||
<div className="mt-auto pt-8 border-t border-[#1e4a6d]/10">
|
||||
<a
|
||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo ${setting?.name || 'VN Grup'}, saya tertarik dengan produk ${selectedProduct?.name} seharga ${formatCurrency(selectedProduct?.retail_price || 0)}. Bisa tanya-tanya dulu?`)}`}
|
||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo ${setting?.site_name || 'VN Grup'}, saya tertarik dengan produk ${selectedProduct?.name} seharga ${formatCurrency(selectedProduct?.retail_price || 0)}. Bisa tanya-tanya dulu?`)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full bg-[#1e4a6d] py-4 text-[10px] font-bold uppercase tracking-[0.2em] text-white transition-all hover:bg-[#15344e] flex items-center justify-center gap-2"
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
export interface GeneralSetting {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
address: string | null;
|
||||
phone: string | null;
|
||||
site_name: string;
|
||||
site_description: string | null;
|
||||
site_address: string | null;
|
||||
site_phone: string | null;
|
||||
logo_light_url: string | null;
|
||||
logo_dark_url: string | null;
|
||||
icon_light_url: string | null;
|
||||
icon_dark_url: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
@ -31,12 +31,8 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
@php
|
||||
$setting = \App\Models\GeneralSetting::first();
|
||||
@endphp
|
||||
|
||||
@if ($setting && $setting->icon_light_url)
|
||||
<link rel="icon" href="{{ $setting->icon_light_url }}" sizes="any">
|
||||
@if ($setting['icon_light_url'] ?? null)
|
||||
<link rel="icon" href="{{ $setting['icon_light_url'] }}" sizes="any">
|
||||
@else
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
@endif
|
||||
@ -47,7 +43,7 @@
|
||||
@viteReactRefresh
|
||||
@vite(['resources/css/app.css', 'resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
|
||||
<x-inertia::head>
|
||||
<title>{{ config('app.name', 'VN Grup') }}</title>
|
||||
<title>{{ $setting['site_name'] ?? config('app.name', 'VN Grup') }}</title>
|
||||
</x-inertia::head>
|
||||
</head>
|
||||
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
use App\Enums\OrderStatus;
|
||||
use App\Enums\PaymentMethod;
|
||||
use App\Enums\PriceType;
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Product;
|
||||
use App\Settings\GeneralSettings;
|
||||
|
||||
use function Pest\Laravel\actingAs;
|
||||
use function Pest\Laravel\assertDatabaseHas;
|
||||
@ -47,12 +47,12 @@
|
||||
]);
|
||||
actingAs($user);
|
||||
|
||||
GeneralSetting::create([
|
||||
'name' => 'VN Grup',
|
||||
'phone' => '08123456789',
|
||||
'address' => 'Jl. Test No. 1',
|
||||
'description' => 'Test Description',
|
||||
]);
|
||||
$settings = app(GeneralSettings::class);
|
||||
$settings->site_name = 'VN Grup';
|
||||
$settings->site_phone = '08123456789';
|
||||
$settings->site_address = 'Jl. Test No. 1';
|
||||
$settings->site_description = 'Test Description';
|
||||
$settings->save();
|
||||
});
|
||||
|
||||
it('can access order index page', function () {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
@ -49,19 +49,11 @@
|
||||
it('can update general settings with logo and icon variants', function () {
|
||||
Storage::fake('public');
|
||||
|
||||
// Ensure a setting exists first for testing update logic in Controller
|
||||
$setting = GeneralSetting::create([
|
||||
'name' => 'Old Name',
|
||||
'description' => 'Old Description',
|
||||
'address' => 'Old Address',
|
||||
'phone' => '000',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => 'VNGrup Dress',
|
||||
'description' => 'Toko baju premium',
|
||||
'address' => 'Jakarta, Indonesia',
|
||||
'phone' => '081234567890',
|
||||
'site_name' => 'VNGrup Dress',
|
||||
'site_description' => 'Toko baju premium',
|
||||
'site_address' => 'Jakarta, Indonesia',
|
||||
'site_phone' => '081234567890',
|
||||
'logo_light' => UploadedFile::fake()->image('logo_light.png'),
|
||||
'logo_dark' => UploadedFile::fake()->image('logo_dark.png'),
|
||||
'icon_light' => UploadedFile::fake()->image('icon_light.png'),
|
||||
@ -72,26 +64,34 @@
|
||||
->assertRedirect()
|
||||
->assertSessionHas('success');
|
||||
|
||||
assertDatabaseHas('general_settings', [
|
||||
'id' => $setting->id,
|
||||
'name' => 'VNGrup Dress',
|
||||
'phone' => '081234567890',
|
||||
assertDatabaseHas('settings', [
|
||||
'group' => 'general',
|
||||
'name' => 'site_name',
|
||||
'payload' => json_encode('VNGrup Dress'),
|
||||
]);
|
||||
|
||||
$setting->refresh();
|
||||
expect($setting->getFirstMediaUrl('logo_light'))->not->toBeEmpty();
|
||||
expect($setting->getFirstMediaUrl('logo_dark'))->not->toBeEmpty();
|
||||
expect($setting->getFirstMediaUrl('icon_light'))->not->toBeEmpty();
|
||||
expect($setting->getFirstMediaUrl('icon_dark'))->not->toBeEmpty();
|
||||
assertDatabaseHas('settings', [
|
||||
'group' => 'general',
|
||||
'name' => 'site_phone',
|
||||
'payload' => json_encode('081234567890'),
|
||||
]);
|
||||
|
||||
$settings = app(GeneralSettings::class);
|
||||
expect($settings->logo_light)->not->toBeNull();
|
||||
expect($settings->logo_dark)->not->toBeNull();
|
||||
expect($settings->icon_light)->not->toBeNull();
|
||||
expect($settings->icon_dark)->not->toBeNull();
|
||||
|
||||
Storage::disk('public')->assertExists($settings->logo_light);
|
||||
Storage::disk('public')->assertExists($settings->logo_dark);
|
||||
Storage::disk('public')->assertExists($settings->icon_light);
|
||||
Storage::disk('public')->assertExists($settings->icon_dark);
|
||||
});
|
||||
|
||||
it('validates settings update with new variants', function () {
|
||||
// If setting doesn't exist, logo_light and icon_light are required
|
||||
GeneralSetting::truncate();
|
||||
|
||||
postJson(route('system.settings.update'), [])
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors(['name', 'description', 'address', 'phone', 'logo_light', 'icon_light']);
|
||||
->assertJsonValidationErrors(['site_name', 'site_description', 'site_address', 'site_phone']);
|
||||
});
|
||||
});
|
||||
|
||||
@ -101,7 +101,7 @@
|
||||
});
|
||||
|
||||
it('cannot update settings without permission', function () {
|
||||
postJson(route('system.settings.update'), ['name' => 'Unauthorized'])
|
||||
postJson(route('system.settings.update'), ['site_name' => 'Unauthorized'])
|
||||
->assertStatus(403);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user