feat: update homepage settings to use image keys and add presigned URL retrieval
This commit is contained in:
parent
a1a73cb222
commit
d07eb3498a
@ -6,6 +6,7 @@
|
|||||||
use App\Http\Requests\Api\PresignedUrlRequest;
|
use App\Http\Requests\Api\PresignedUrlRequest;
|
||||||
use App\Services\S3PresignedService;
|
use App\Services\S3PresignedService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
class PresignedUrlController extends Controller
|
class PresignedUrlController extends Controller
|
||||||
{
|
{
|
||||||
@ -25,4 +26,12 @@ public function store(PresignedUrlRequest $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json($result);
|
return response()->json($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function show(string $key): RedirectResponse
|
||||||
|
{
|
||||||
|
$minutes = (int) request('minutes', 60);
|
||||||
|
$url = $this->service->getTemporaryUrl(urldecode($key), $minutes);
|
||||||
|
|
||||||
|
return redirect($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Requests\Admin\Settings;
|
namespace App\Http\Requests\Admin\Settings;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Override;
|
||||||
|
|
||||||
class UpdateHRRequest extends FormRequest
|
class UpdateHRRequest extends FormRequest
|
||||||
{
|
{
|
||||||
@ -11,6 +12,22 @@ public function authorize(): bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Override]
|
||||||
|
public function prepareForValidation()
|
||||||
|
{
|
||||||
|
if ($this->has('late_penalty_amount')) {
|
||||||
|
$this->merge([
|
||||||
|
'late_penalty_amount' => str_replace('.', '', $this->late_penalty_amount),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->has('absent_penalty_amount')) {
|
||||||
|
$this->merge([
|
||||||
|
'absent_penalty_amount' => str_replace('.', '', $this->absent_penalty_amount),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -14,19 +14,19 @@ public function authorize(): bool
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'hero_image_url' => ['nullable', 'string', 'max:2000'],
|
'hero_image_key' => ['nullable', 'string', 'max:2000'],
|
||||||
'about_image_url' => ['nullable', 'string', 'max:2000'],
|
'about_image_key' => ['nullable', 'string', 'max:2000'],
|
||||||
'gallery_images' => ['nullable', 'array'],
|
'gallery_image_keys' => ['nullable', 'array'],
|
||||||
'gallery_images.*' => ['string', 'max:2000'],
|
'gallery_image_keys.*' => ['string', 'max:2000'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attributes(): array
|
public function attributes(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'hero_image_url' => 'url foto hero',
|
'hero_image_key' => 'foto hero',
|
||||||
'about_image_url' => 'url foto tentang kami',
|
'about_image_key' => 'foto tentang kami',
|
||||||
'gallery_images' => 'gambar galeri',
|
'gallery_image_keys' => 'gambar galeri',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Services\Admin;
|
namespace App\Services\Admin;
|
||||||
|
|
||||||
|
use App\Services\S3PresignedService;
|
||||||
use App\Settings\HRSettings;
|
use App\Settings\HRSettings;
|
||||||
use App\Settings\HomepageSettings;
|
use App\Settings\HomepageSettings;
|
||||||
use App\Settings\MarketplaceSettings;
|
use App\Settings\MarketplaceSettings;
|
||||||
@ -11,6 +12,10 @@
|
|||||||
|
|
||||||
class AdminSettingsService
|
class AdminSettingsService
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private S3PresignedService $s3Service = new S3PresignedService,
|
||||||
|
) {}
|
||||||
|
|
||||||
public function getSystemData(): array
|
public function getSystemData(): array
|
||||||
{
|
{
|
||||||
$settings = app(SystemSettings::class);
|
$settings = app(SystemSettings::class);
|
||||||
@ -29,12 +34,27 @@ public function getHomepageData(): array
|
|||||||
$settings = app(HomepageSettings::class);
|
$settings = app(HomepageSettings::class);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'hero_image_url' => $settings->hero_image_url,
|
'hero_image_url' => $settings->hero_image_url ? $this->getTemporaryUrl($settings->hero_image_url) : null,
|
||||||
'about_image_url' => $settings->about_image_url,
|
'hero_image_key' => $settings->hero_image_url,
|
||||||
'gallery_images' => $settings->gallery_images,
|
'about_image_url' => $settings->about_image_url ? $this->getTemporaryUrl($settings->about_image_url) : null,
|
||||||
|
'about_image_key' => $settings->about_image_url,
|
||||||
|
'gallery_images' => array_map(
|
||||||
|
fn ($key) => $this->getTemporaryUrl($key),
|
||||||
|
$settings->gallery_images ?? [],
|
||||||
|
),
|
||||||
|
'gallery_image_keys' => $settings->gallery_images ?? [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getTemporaryUrl(string $key): string
|
||||||
|
{
|
||||||
|
if (str_starts_with($key, 'http')) {
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->s3Service->getTemporaryUrl($key, 60);
|
||||||
|
}
|
||||||
|
|
||||||
public function getSocialMediaData(): array
|
public function getSocialMediaData(): array
|
||||||
{
|
{
|
||||||
$settings = app(SocialMediaSettings::class);
|
$settings = app(SocialMediaSettings::class);
|
||||||
@ -95,7 +115,11 @@ public function updateSystem(array $data): void
|
|||||||
public function updateHomepage(array $data): void
|
public function updateHomepage(array $data): void
|
||||||
{
|
{
|
||||||
$settings = app(HomepageSettings::class);
|
$settings = app(HomepageSettings::class);
|
||||||
$settings->fill($data);
|
|
||||||
|
$settings->hero_image_url = $data['hero_image_key'] ?? null;
|
||||||
|
$settings->about_image_url = $data['about_image_key'] ?? null;
|
||||||
|
$settings->gallery_images = $data['gallery_image_keys'] ?? [];
|
||||||
|
|
||||||
$settings->save();
|
$settings->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { FileImage, Upload, X } from 'lucide-react';
|
import { FileImage, Upload, X } from 'lucide-react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState, useId } from 'react';
|
||||||
import {
|
import {
|
||||||
Attachment,
|
Attachment,
|
||||||
AttachmentAction,
|
AttachmentAction,
|
||||||
@ -36,6 +36,7 @@ function formatFileSize(bytes: number): string {
|
|||||||
|
|
||||||
export function FileUpload({ value, onChange, folder, accept = 'image/jpeg,image/png,image/webp,image/gif', onUploadingChange, existingUrl, onFileMeta }: FileUploadProps) {
|
export function FileUpload({ value, onChange, folder, accept = 'image/jpeg,image/png,image/webp,image/gif', onUploadingChange, existingUrl, onFileMeta }: FileUploadProps) {
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const uploadId = useId();
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [fileName, setFileName] = useState<string | null>(null);
|
const [fileName, setFileName] = useState<string | null>(null);
|
||||||
@ -106,7 +107,7 @@ export function FileUpload({ value, onChange, folder, accept = 'image/jpeg,image
|
|||||||
accept={accept}
|
accept={accept}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
id="file-upload"
|
id={uploadId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Attachment state={state} orientation="horizontal" className='w-full'>
|
<Attachment state={state} orientation="horizontal" className='w-full'>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { FileUpload } from '@/components/file-upload';
|
||||||
import InputError from '@/components/input-error';
|
import InputError from '@/components/input-error';
|
||||||
import { PhoneNumberInput } from '@/components/phone-number-input';
|
import { PhoneNumberInput } from '@/components/phone-number-input';
|
||||||
import { RupiahInput } from '@/components/rupiah-input';
|
import { RupiahInput } from '@/components/rupiah-input';
|
||||||
@ -10,8 +11,9 @@ import { Separator } from '@/components/ui/separator';
|
|||||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { updateHomepage, updateMarketplace, updateSocialMedia, updateSystem } from '@/routes/admin/settings';
|
import { updateHomepage, updateHr, updateMarketplace, updateSocialMedia, updateSystem } from '@/routes/admin/settings';
|
||||||
import { Form, Head } from '@inertiajs/react';
|
import { Form, Head } from '@inertiajs/react';
|
||||||
|
import { Plus, Trash2 } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
type MarketplaceFeeRule = {
|
type MarketplaceFeeRule = {
|
||||||
@ -30,8 +32,11 @@ type Props = {
|
|||||||
};
|
};
|
||||||
homepage: {
|
homepage: {
|
||||||
hero_image_url: string;
|
hero_image_url: string;
|
||||||
|
hero_image_key: string;
|
||||||
about_image_url: string;
|
about_image_url: string;
|
||||||
|
about_image_key: string;
|
||||||
gallery_images: string[];
|
gallery_images: string[];
|
||||||
|
gallery_image_keys: string[];
|
||||||
};
|
};
|
||||||
socialMedia: {
|
socialMedia: {
|
||||||
instagram_url: string;
|
instagram_url: string;
|
||||||
@ -50,6 +55,124 @@ type Props = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type HomepageSettingsTabProps = {
|
||||||
|
homepage: Props['homepage'];
|
||||||
|
};
|
||||||
|
|
||||||
|
function HomepageSettingsTab({ homepage }: HomepageSettingsTabProps) {
|
||||||
|
const [heroKey, setHeroKey] = useState<string | null>(homepage.hero_image_key ?? null);
|
||||||
|
const [heroUploading, setHeroUploading] = useState(false);
|
||||||
|
const [aboutKey, setAboutKey] = useState<string | null>(homepage.about_image_key ?? null);
|
||||||
|
const [aboutUploading, setAboutUploading] = useState(false);
|
||||||
|
const [galleryKeys, setGalleryKeys] = useState<string[]>(homepage.gallery_image_keys ?? []);
|
||||||
|
const [galleryUploading, setGalleryUploading] = useState(false);
|
||||||
|
|
||||||
|
function addGalleryImage() {
|
||||||
|
setGalleryKeys((prev) => [...prev, '']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeGalleryImage(index: number) {
|
||||||
|
setGalleryKeys((prev) => prev.filter((_, i) => i !== index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGalleryKey(index: number, key: string | null) {
|
||||||
|
setGalleryKeys((prev) => prev.map((k, i) => (i === index ? (key ?? '') : k)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form action={updateHomepage()} options={{ preserveScroll: true }}>
|
||||||
|
{({ processing, errors }) => (
|
||||||
|
<div className="grid gap-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Homepage</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="grid grid-cols-1 gap-4">
|
||||||
|
<input type="hidden" name="hero_image_key" value={heroKey ?? ''} />
|
||||||
|
<input type="hidden" name="about_image_key" value={aboutKey ?? ''} />
|
||||||
|
{galleryKeys.map((key, index) => (
|
||||||
|
<input key={index} type="hidden" name={`gallery_image_keys[${index}]`} value={key} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label>Foto Hero</Label>
|
||||||
|
<FileUpload
|
||||||
|
value={heroKey}
|
||||||
|
onChange={setHeroKey}
|
||||||
|
folder="homepage/hero"
|
||||||
|
existingUrl={homepage.hero_image_url}
|
||||||
|
onUploadingChange={setHeroUploading}
|
||||||
|
/>
|
||||||
|
<InputError message={errors.hero_image_key} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label>Foto Tentang Kami</Label>
|
||||||
|
<FileUpload
|
||||||
|
value={aboutKey}
|
||||||
|
onChange={setAboutKey}
|
||||||
|
folder="homepage/about"
|
||||||
|
existingUrl={homepage.about_image_url}
|
||||||
|
onUploadingChange={setAboutUploading}
|
||||||
|
/>
|
||||||
|
<InputError message={errors.about_image_key} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label>Galeri Foto</Label>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={addGalleryImage}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Tambah
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
{galleryKeys.map((key, index) => (
|
||||||
|
<div key={index} className="flex items-start gap-2">
|
||||||
|
<div className="flex-1">
|
||||||
|
<FileUpload
|
||||||
|
value={key || null}
|
||||||
|
onChange={(k) => updateGalleryKey(index, k)}
|
||||||
|
folder="homepage/gallery"
|
||||||
|
existingUrl={homepage.gallery_images[index] ?? null}
|
||||||
|
onUploadingChange={setGalleryUploading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="mt-1 text-destructive hover:text-destructive"
|
||||||
|
onClick={() => removeGalleryImage(index)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{galleryKeys.length === 0 && (
|
||||||
|
<p className="text-sm text-muted-foreground">Belum ada gambar. Klik "Tambah" untuk menambahkan gambar galeri.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<InputError message={errors.gallery_image_keys} />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Button type="submit" disabled={processing || heroUploading || aboutUploading || galleryUploading}>
|
||||||
|
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sidebarTabs = [
|
const sidebarTabs = [
|
||||||
{ key: 'sistem', label: 'Sistem' },
|
{ key: 'sistem', label: 'Sistem' },
|
||||||
{ key: 'homepage', label: 'Homepage' },
|
{ key: 'homepage', label: 'Homepage' },
|
||||||
@ -249,32 +372,7 @@ export default function AdminSettings({ system, homepage, socialMedia, marketpla
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'homepage' && (
|
{activeTab === 'homepage' && (
|
||||||
<Form action={updateHomepage()} options={{ preserveScroll: true }}>
|
<HomepageSettingsTab homepage={homepage} />
|
||||||
{({ processing, errors }) => (
|
|
||||||
<div className="grid gap-6">
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Homepage</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="grid grid-cols-1 gap-4">
|
|
||||||
<div className="grid gap-2">
|
|
||||||
<Label htmlFor="hero_image_url">URL Foto Hero</Label>
|
|
||||||
<Input id="hero_image_url" name="hero_image_url" placeholder="https://..." defaultValue={homepage.hero_image_url} />
|
|
||||||
<InputError message={errors.hero_image_url} />
|
|
||||||
</div>
|
|
||||||
<div className="grid gap-2">
|
|
||||||
<Label htmlFor="about_image_url">URL Foto Tentang Kami</Label>
|
|
||||||
<Input id="about_image_url" name="about_image_url" placeholder="https://..." defaultValue={homepage.about_image_url} />
|
|
||||||
<InputError message={errors.about_image_url} />
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<Button type="submit" disabled={processing}>{processing ? 'Menyimpan...' : 'Simpan'}</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'media-sosial' && (
|
{activeTab === 'media-sosial' && (
|
||||||
@ -336,7 +434,7 @@ export default function AdminSettings({ system, homepage, socialMedia, marketpla
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'hr' && (
|
{activeTab === 'hr' && (
|
||||||
<Form action="/admin/settings/hr" options={{ preserveScroll: true }}>
|
<Form action={updateHr()} options={{ preserveScroll: true }}>
|
||||||
{({ processing, errors }) => (
|
{({ processing, errors }) => (
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
|
|||||||
@ -6,3 +6,7 @@
|
|||||||
Route::post('/presigned-url', [PresignedUrlController::class, 'store'])
|
Route::post('/presigned-url', [PresignedUrlController::class, 'store'])
|
||||||
->middleware(['web', 'auth', 'verified'])
|
->middleware(['web', 'auth', 'verified'])
|
||||||
->name('presigned-url.store');
|
->name('presigned-url.store');
|
||||||
|
|
||||||
|
Route::get('/presigned-url/{key}', [PresignedUrlController::class, 'show'])
|
||||||
|
->middleware(['web', 'auth', 'verified'])
|
||||||
|
->name('presigned-url.show');
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
test('guest cannot update homepage settings', function () {
|
test('guest cannot update homepage settings', function () {
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'hero_image_url' => 'https://unauthorized.com/image.jpg',
|
'hero_image_key' => 'unauthorized/image.jpg',
|
||||||
]);
|
]);
|
||||||
$response->assertRedirect(route('login'));
|
$response->assertRedirect(route('login'));
|
||||||
});
|
});
|
||||||
@ -257,9 +257,9 @@
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'hero_image_url' => 'https://example.com/hero.jpg',
|
'hero_image_key' => 'homepage/hero/2026/07/31/abc123/hero.jpg',
|
||||||
'about_image_url' => 'https://example.com/about.jpg',
|
'about_image_key' => 'homepage/about/2026/07/31/def456/about.jpg',
|
||||||
'gallery_images' => ['https://example.com/g1.jpg', 'https://example.com/g2.jpg'],
|
'gallery_image_keys' => ['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
@ -267,9 +267,9 @@
|
|||||||
->assertRedirect(route('admin.settings.index'));
|
->assertRedirect(route('admin.settings.index'));
|
||||||
|
|
||||||
$settings = app(HomepageSettings::class);
|
$settings = app(HomepageSettings::class);
|
||||||
expect($settings->hero_image_url)->toBe('https://example.com/hero.jpg');
|
expect($settings->hero_image_url)->toBe('homepage/hero/2026/07/31/abc123/hero.jpg');
|
||||||
expect($settings->about_image_url)->toBe('https://example.com/about.jpg');
|
expect($settings->about_image_url)->toBe('homepage/about/2026/07/31/def456/about.jpg');
|
||||||
expect($settings->gallery_images)->toBe(['https://example.com/g1.jpg', 'https://example.com/g2.jpg']);
|
expect($settings->gallery_images)->toBe(['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('homepage fields are nullable', function () {
|
test('homepage fields are nullable', function () {
|
||||||
@ -277,44 +277,44 @@
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'hero_image_url' => null,
|
'hero_image_key' => null,
|
||||||
'about_image_url' => null,
|
'about_image_key' => null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertSessionHasNoErrors();
|
$response->assertSessionHasNoErrors();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('homepage hero_image_url must not exceed 2000 characters', function () {
|
test('homepage hero_image_key must not exceed 2000 characters', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'hero_image_url' => str_repeat('a', 2001),
|
'hero_image_key' => str_repeat('a', 2001),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertSessionHasErrors('hero_image_url');
|
$response->assertSessionHasErrors('hero_image_key');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('homepage gallery_images must be array', function () {
|
test('homepage gallery_image_keys must be array', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'gallery_images' => 'not-an-array',
|
'gallery_image_keys' => 'not-an-array',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertSessionHasErrors('gallery_images');
|
$response->assertSessionHasErrors('gallery_image_keys');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('homepage gallery_images items must not exceed 2000 characters', function () {
|
test('homepage gallery_image_keys items must not exceed 2000 characters', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->put(route('admin.settings.update-homepage'), [
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
||||||
'gallery_images' => [str_repeat('a', 2001)],
|
'gallery_image_keys' => [str_repeat('a', 2001)],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertSessionHasErrors('gallery_images.0');
|
$response->assertSessionHasErrors('gallery_image_keys.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -851,8 +851,8 @@
|
|||||||
|
|
||||||
// Update homepage
|
// Update homepage
|
||||||
$this->put(route('admin.settings.update-homepage'), [
|
$this->put(route('admin.settings.update-homepage'), [
|
||||||
'hero_image_url' => 'https://example.com/new-hero.jpg',
|
'hero_image_key' => 'homepage/hero/2026/07/31/new-hero.jpg',
|
||||||
'about_image_url' => 'https://example.com/new-about.jpg',
|
'about_image_key' => 'homepage/about/2026/07/31/new-about.jpg',
|
||||||
])->assertSessionHasNoErrors();
|
])->assertSessionHasNoErrors();
|
||||||
|
|
||||||
// Update social media
|
// Update social media
|
||||||
@ -898,7 +898,7 @@
|
|||||||
$hr = app(HRSettings::class);
|
$hr = app(HRSettings::class);
|
||||||
|
|
||||||
expect($system->app_name)->toBe('DST Updated');
|
expect($system->app_name)->toBe('DST Updated');
|
||||||
expect($homepage->hero_image_url)->toBe('https://example.com/new-hero.jpg');
|
expect($homepage->hero_image_url)->toBe('homepage/hero/2026/07/31/new-hero.jpg');
|
||||||
expect($socialMedia->instagram_url)->toBe('https://instagram.com/newdst');
|
expect($socialMedia->instagram_url)->toBe('https://instagram.com/newdst');
|
||||||
expect($marketplace->tiktok_shop_platform_commission->value)->toBe(3.0);
|
expect($marketplace->tiktok_shop_platform_commission->value)->toBe(3.0);
|
||||||
expect($hr->scheduled_check_in_time)->toBe('08:30');
|
expect($hr->scheduled_check_in_time)->toBe('08:30');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user