feat(deal update and delete): broker can update and delete their deals

- show external links in listings
- refactor image-input.blade.php to display image while update
- refactor image-input.js to show selected image after user clicks submit
- refactor components to accept default value
- add FileService to handle image update and delete
This commit is contained in:
kusowl 2026-01-13 15:10:55 +05:30
parent e2fabcd8b6
commit a248d3fc79
18 changed files with 464 additions and 69 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@
Homestead.json
Homestead.yaml
Thumbs.db
laradumps.yaml

View File

@ -19,11 +19,13 @@ protected function deals()
return Auth::user()
->deals()
->select([
'id',
'title',
'description',
'image',
'active',
'slug',
'link',
'deal_category_id',
])
->with('category:id,name')

View File

@ -5,9 +5,12 @@
use App\Http\Requests\StoreBrokerDeal;
use App\Models\Deal;
use App\Models\DealCategory;
use App\Services\FileService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use const http\Client\Curl\AUTH_ANY;
class BrokerDealController extends Controller
{
@ -38,9 +41,9 @@ public function store(StoreBrokerDeal $request)
$data['user_id'] = $request->user()->id;
$path = '';
if($request->hasFile('image')){
if ($request->hasFile('image')) {
$image = $request->file('image');
$path = $image->storeAs('images/deals', $data['slug'] . '.' . $image->extension(), 'public');
$path = $image->storeAs('images/deals', $data['slug'].'.'.$image->extension(), 'public');
}
$data['image'] = $path;
@ -62,24 +65,61 @@ public function show(string $id)
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
public function edit(Deal $deal)
{
//
return view('dashboards.broker.deals.edit')
->with('deal', $deal)
->with('categories', DealCategory::all('id', 'name'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
public function update(StoreBrokerDeal $request, Deal $deal, FileService $fileService)
{
//
$data = $request->validated();
try {
DB::transaction(function () use ($deal, $data, $fileService, $request) {
$data['slug'] = Str::slug($data['title']);
if ($request->hasFile('image')) {
$image = $request->file('image');
$data['image'] = $fileService->upload($image, 'images/deals',
$data['slug'].'.'.$image->extension());
}
Deal::unguard();
$deal->update($data);
Deal::reguard();
});
} catch (\Throwable $exception) {
Log::error($exception->getMessage(), $exception->getTrace());
return to_route('broker.dashboard')->with('error', 'Something gone wrong.');
}
return to_route('broker.dashboard')->with('success', 'Deal has been updated.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
public function destroy(Deal $deal, FileService $fileService)
{
//
// remove the image of deal
try {
DB::transaction(function () use ($deal, $fileService) {
$fileService->delete($deal->image);
$deal->delete();
});
} catch (\Throwable $exception) {
Log::error($exception->getMessage(), $exception->getTrace());
return to_route('broker.dashboard')->with('error', 'Something gone wrong.');
}
return to_route('broker.dashboard')->with('success', 'Deal has been deleted.');
}
}

View File

@ -24,7 +24,7 @@ public function rules(): array
return [
'title' => 'required|min:10|max:255',
'description' => 'required|min:10|max:300',
'image' => 'required|image|mimes:jpeg,png,jpg|max:10240',
'image' => [$this->isMethod('post') ? 'required' : 'nullable', 'image', 'mimes:jpeg,png,jpg', 'max:10240'],
'link' => 'nullable|url',
'deal_category_id' => 'required|exists:deal_categories,id',
];

View File

@ -0,0 +1,21 @@
<?php
namespace App\Services;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
class FileService
{
public function upload(UploadedFile $file, string $folder, string $filename): string
{
return $file->storeAs($folder, $filename.'.'.$file->extension(), 'public');
}
public function delete(?string $path): void
{
if ($path && Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path);
}
}
}

View File

@ -16,6 +16,7 @@
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laradumps/laradumps": "^5.0",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.24",
"laravel/sail": "^1.41",

229
composer.lock generated
View File

@ -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": "2f3167c1eddf52a1400c85a1ac601d4d",
"content-hash": "d2215c7e3da7601aa624d057baef6449",
"packages": [
{
"name": "blade-ui-kit/blade-heroicons",
@ -1204,16 +1204,16 @@
},
{
"name": "laravel/framework",
"version": "v12.45.1",
"version": "v12.46.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa"
"reference": "9dcff48d25a632c1fadb713024c952fec489c4ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa",
"reference": "1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa",
"url": "https://api.github.com/repos/laravel/framework/zipball/9dcff48d25a632c1fadb713024c952fec489c4ae",
"reference": "9dcff48d25a632c1fadb713024c952fec489c4ae",
"shasum": ""
},
"require": {
@ -1422,7 +1422,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2026-01-07T00:50:24+00:00"
"time": "2026-01-07T23:26:53+00:00"
},
{
"name": "laravel/prompts",
@ -6594,6 +6594,147 @@
},
"time": "2025-03-19T14:43:43+00:00"
},
{
"name": "laradumps/laradumps",
"version": "v5.0.0",
"source": {
"type": "git",
"url": "https://github.com/laradumps/laradumps.git",
"reference": "7bfb9b888ce351ca151e29cacf2e2e74c7b62f72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laradumps/laradumps/zipball/7bfb9b888ce351ca151e29cacf2e2e74c7b62f72",
"reference": "7bfb9b888ce351ca151e29cacf2e2e74c7b62f72",
"shasum": ""
},
"require": {
"illuminate/mail": "^11.0|^12.0",
"illuminate/support": "^11.0|^12.0",
"laradumps/laradumps-core": "^4.0.0",
"nunomaduro/termwind": "^2.3.3",
"php": "^8.2"
},
"require-dev": {
"larastan/larastan": "^3.8",
"laravel/framework": "^11.0|^12.0",
"laravel/pint": "^1.26.0",
"livewire/livewire": "^3.7.1|^4.0",
"mockery/mockery": "^1.6.12",
"orchestra/testbench-core": "^9.4|^10.0",
"pestphp/pest": "^3.7.0|^4.0.0",
"symfony/var-dumper": "^7.1.3|^8.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaraDumps\\LaraDumps\\LaraDumpsServiceProvider"
]
}
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"LaraDumps\\LaraDumps\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Luan Freitas",
"email": "luanfreitas10@protonmail.com",
"role": "Developer"
}
],
"description": "LaraDumps is a friendly app designed to boost your Laravel PHP coding and debugging experience.",
"homepage": "https://github.com/laradumps/laradumps",
"support": {
"issues": "https://github.com/laradumps/laradumps/issues",
"source": "https://github.com/laradumps/laradumps/tree/v5.0.0"
},
"funding": [
{
"url": "https://github.com/luanfreitasdev",
"type": "github"
}
],
"time": "2025-12-13T12:44:16+00:00"
},
{
"name": "laradumps/laradumps-core",
"version": "v4.0.0",
"source": {
"type": "git",
"url": "https://github.com/laradumps/laradumps-core.git",
"reference": "bb57c8fccb785777020b85592d718e8ff0c9a23a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laradumps/laradumps-core/zipball/bb57c8fccb785777020b85592d718e8ff0c9a23a",
"reference": "bb57c8fccb785777020b85592d718e8ff0c9a23a",
"shasum": ""
},
"require": {
"ext-curl": "*",
"nunomaduro/termwind": "^2.0",
"php": "^8.2",
"ramsey/uuid": "^4.9.1",
"spatie/backtrace": "^1.5",
"symfony/console": "^6.4|^7.0|^8.0",
"symfony/finder": "^6.4|^7.0|^8.0",
"symfony/process": "^6.4|^7.0|^8.0",
"symfony/var-dumper": "^6.4|^7.0|^8.0",
"symfony/yaml": "^6.4|^7.0|^8.0"
},
"require-dev": {
"illuminate/support": "^10.46",
"laravel/pint": "^1.26.0",
"pestphp/pest": "^3.0|^4.0",
"phpstan/phpstan": "^1.10.50"
},
"bin": [
"bin/laradumps"
],
"type": "library",
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"LaraDumps\\LaraDumpsCore\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Luan Freitas",
"email": "luanfreitas10@protonmail.com",
"role": "Developer"
}
],
"description": "LaraDumps is a friendly app designed to boost your Laravel / PHP coding and debugging experience.",
"homepage": "https://github.com/laradumps/laradumps-core",
"support": {
"issues": "https://github.com/laradumps/laradumps-core/issues",
"source": "https://github.com/laradumps/laradumps-core/tree/v4.0.0"
},
"funding": [
{
"url": "https://github.com/luanfreitasdev",
"type": "github"
}
],
"time": "2025-12-12T22:10:38+00:00"
},
{
"name": "laravel/pail",
"version": "v1.2.4",
@ -9183,6 +9324,70 @@
],
"time": "2025-02-07T05:00:38+00:00"
},
{
"name": "spatie/backtrace",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110",
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0"
},
"require-dev": {
"ext-json": "*",
"laravel/serializable-closure": "^1.3 || ^2.0",
"phpunit/phpunit": "^9.3 || ^11.4.3",
"spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6",
"symfony/var-dumper": "^5.1 || ^6.0 || ^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Backtrace\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van de Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "A better backtrace",
"homepage": "https://github.com/spatie/backtrace",
"keywords": [
"Backtrace",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/backtrace/issues",
"source": "https://github.com/spatie/backtrace/tree/1.8.1"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2025-08-26T08:22:30+00:00"
},
{
"name": "staabm/side-effects-detector",
"version": "1.0.5",
@ -9422,16 +9627,16 @@
},
{
"name": "webmozart/assert",
"version": "2.0.0",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54"
"reference": "bdbabc199a7ba9965484e4725d66170e5711323b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
"reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bdbabc199a7ba9965484e4725d66170e5711323b",
"reference": "bdbabc199a7ba9965484e4725d66170e5711323b",
"shasum": ""
},
"require": {
@ -9478,9 +9683,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/2.0.0"
"source": "https://github.com/webmozarts/assert/tree/2.1.1"
},
"time": "2025-12-16T21:36:00+00:00"
"time": "2026-01-08T11:28:40+00:00"
}
],
"aliases": [],

View File

@ -1,19 +1,11 @@
const previewImage = document.getElementById('preview-image');
function upload(size) {
const imageInput = document.getElementById("image-input");
const closeModalBtn = document.getElementById("close-modal");
const cancelBtn = document.getElementById("cancel-modal");
const modal = document.getElementById("image-modal");
closeModalBtn.addEventListener('click', () => {
// this closes modal but does not remove the image from file input
modal.close();
})
cancelBtn.addEventListener('click', () => {
// clears the file from image input field and closes the modal
imageInput.value = "";
modal.close();
})
let imageUrl = '';
const image = imageInput.files[0];
@ -32,9 +24,21 @@ function upload(size) {
fileReader.onload = (e) => {
const imagePlaceholder = document.getElementById("image-placeholder");
imagePlaceholder.src = e.target.result;
imagePlaceholder.src = imageUrl = e.target.result;
modal.showModal();
}
closeModalBtn.addEventListener('click', () => {
// this closes then modal and sets the preview image
previewImage.src = imageUrl;
modal.close();
})
cancelBtn.addEventListener('click', () => {
// clears the file from image input field and closes the modal
imageInput.value = "";
modal.close();
})
}
document.upload = upload;

View File

@ -1,15 +1,43 @@
@props(['image' => '', 'title' => '', 'category' => '', 'impressions' => 0, 'likes' => 0, 'clicks' => 0, 'status' => false])
@props(['id' => '', 'image' => '', 'title' => '', 'category' => '', 'impressions' => 0, 'likes' => 0, 'clicks' => 0, 'status' => false, 'external_link' => ''])
<x-ui.image-card :image="$image">
<div class="bg-white pt-8 px-4 space-y-4">
<div class="flex justify-between items-baseline">
<div class="bg-white pt-8 p-4 space-y-4">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-1 mr-2">
<p class="font-bold text-lg">{{$title}}</p>
@if($external_link !== '')
<a href="{{$external_link}}" target="_blank" title="{{$external_link}}"
class="text-xs underline text-accent-601">
<x-heroicon-o-arrow-top-right-on-square class="w-4 stroke-2 "/>
</a>
@endif
</div>
@if($status == 1)
<x-ui.badge title="Active"/>
@else
<x-ui.badge title="Pending" variant="ghost"/>
@endif
</div>
<p class="text-accent-600">{{$category}}</p>
<x-dashboard.listing-stats :impression="$impressions" :likes="$likes" :clicks="$clicks"/>
<div class="flex justify-between space-x-4">
<x-ui.button :link="route('broker.deals.edit', $id)" class="w-full border border-accent-600/30"
icon="pencil-square">Edit
</x-ui.button>
<form class="w-full" method="post" action="{{route('broker.deals.destroy', $id)}}">
@csrf
@method("DELETE")
<x-ui.button variant="red" class="w-full" icon="trash">Delete</x-ui.button>
</form>
</div>
</div>
</x-ui.image-card>

View File

@ -3,9 +3,9 @@
<x-dashboard.card class="bg-white">
<p class="font-bold mb-6">My Listings</p>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
@foreach($deals as $deal)
@forelse($deals as $deal)
<x-dashboard.listing-card
:id="$deal->id"
:image="asset('storage/'.$deal->image)"
:title="$deal->title"
:category="$deal->category->name"
@ -13,8 +13,11 @@
impressions="1245"
likes="89"
class="156"
:external_link="$deal->link"
/>
@endforeach
@empty
<p class="text-center text-xs text-accent-600 lg:col-span-3">No Deals created</p>
@endforelse
</div>
</x-dashboard.card>
</div>

View File

@ -1,11 +1,29 @@
@props(['variant' => ''])
@props(['variant' => '', 'icon' => '', 'link' => ''])
@php
$variants = [
'neutral' => 'bg-primary-600 text-white'
];
$variants = [
'neutral' => 'bg-primary-600 text-white',
'red' => 'bg-red-500 text-white'
];
$variantClass = $variants[$variant] ?? '';
$variantClass = $variants[$variant] ?? '';
@endphp
<button {{$attributes->merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass"])}}>
{{$slot}}
</button>
@if($link !== '')
<a {{$attributes->merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass", 'href' => $link])}}>
<div class="flex justify-center items-center space-x-2">
@if($icon !=='')
@svg("heroicon-o-$icon", 'w-5 h-5')
@endif
<p>{{$slot}}</p>
</div>
</a>
@else
<button {{$attributes->merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass"])}}>
<div class="flex justify-center items-center space-x-2">
@if($icon !=='')
@svg("heroicon-o-$icon", 'w-5 h-5')
@endif
<p>{{$slot}}</p>
</div>
</button>
@endif

View File

@ -1,9 +1,11 @@
@props(['image' => '', 'alt' => ''])
<div class="border border-gray-200 rounded-xl overflow-clip">
<div class="grid grid-rows-2">
<div class="flex flex-col">
<div class="rounded-t-xl h-40">
<img src="{{$image}}" alt="" class="object-cover">
</div>
<div>
{{$slot}}
</div>
</div>
</div>

View File

@ -1,32 +1,40 @@
@props(['name' => '', 'label' => '', 'allowed' => '', 'size' => '1', 'required' => false])
@props(['name' => '', 'label' => '', 'allowed' => '', 'size' => '1', 'required' => false, 'value' => ''])
<div class="flex flex-col space-y-2">
@if($label !== '')
<label class="text-sm font-bold" for="{{$name}}">
{{$label}}
@if($required)
{{$label}} @if($required && !$value)
*
@endif
</label>
@endif
<div class="relative">
<div class="relative group">
<div
class="p-8 border-2 border-dashed border-accent-600/70 rounded-lg flex flex-col space-y-2 justify-center items-center">
<x-heroicon-o-arrow-up-tray class="w-8 text-accent-600/70"/>
<p class="text-sm text-accent-600/90 font-bold">Click to upload or drag and drop</p>
<p class="text-xs text-accent-600/70">{{strtoupper($allowed)}} upto {{$size}}MB</p>
class="p-8 border-2 border-dashed border-accent-600/70 rounded-lg flex flex-col space-y-2 justify-center items-center overflow-hidden min-h-40 bg-gray-50 relative">
<img src="{{ $value }}" id="preview-image"
class="absolute inset-0 w-full h-full object-cover opacity-60 group-hover:opacity-10 transition-opacity">
<x-heroicon-o-arrow-up-tray class="w-8 text-accent-600/70 z-10"/>
<p class="text-sm text-accent-600/90 font-bold z-10">
{{ $value ? 'Click to replace current image' : 'Click to upload or drag and drop' }}
</p>
<p class="text-xs text-accent-600/70 z-10">{{strtoupper($allowed)}} up to {{$size}}MB</p>
</div>
<input
name="{{$name}}"
id="image-input"
class="opacity-0 absolute w-full h-full top-0 left-0"
class="opacity-0 absolute w-full h-full top-0 left-0 cursor-pointer"
type="file"
accept="{{ $allowed ? '.' . str_replace(',', ',.', $allowed) : 'image/*' }}"
onchange="upload(10)"
accept="image/{{$allowed}}"
{{ $required && !$value ? 'required' : '' }}
/>
<x-ui.inline-error :name="$name"/>
</div>
<x-ui.inline-error :name="$name"/>
</div>
<dialog id="image-modal"
@ -40,5 +48,4 @@ class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded
</div>
</dialog>
@vite('resources/js/image-input.js')

View File

@ -1,4 +1,4 @@
@props(['label' => '', 'name' => '', 'placeholder' => '', 'type' => 'text', 'description' => '', 'required' => false])
@props(['label' => '', 'name' => '', 'placeholder' => '', 'type' => 'text', 'description' => '', 'required' => false, 'value' => ''])
<div class="flex flex-col space-y-2">
@if($label !== '')
<label class="text-sm font-bold" for="{{$name}}">
@ -10,7 +10,7 @@
@endif
<input class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
type="{{$type}}" placeholder="{{$placeholder}}"
name="{{$name}}" value="{{old($name)}}"
name="{{$name}}" value="{{old($name, $value)}}"
{{$required?'required':''}}
{{$attributes}}
>

View File

@ -1,4 +1,14 @@
@props(['options' => [], 'name' => '', 'placeholder' => '', 'labelKey' => 'label', 'valueKey' => 'value', 'label' => '', 'required' => false])
@props([
'options' => [],
'name' => '',
'placeholder' => '',
'labelKey' => 'label',
'valueKey' => 'value',
'label' => '',
'required' => false,
'selected' => ''
]
)
<div class="flex flex-col space-y-2">
@if($label !== '')
@ -20,7 +30,8 @@ class="bg-[#F3F3F5] py-2 px-4 rounded-lg text-sm font-bold invalid:text-accent-6
@endif
@foreach($options as $option)
<option value="{{$option[$valueKey]}}" {{$option[$valueKey] == old($name) ? 'selected' : ''}}> {{$option[$labelKey]}} </option>
<option
value="{{$option[$valueKey]}}" {{$option[$valueKey] == old($name, $selected) ? 'selected' : ''}}> {{$option[$labelKey]}} </option>
@endforeach
</select>

View File

@ -1,4 +1,4 @@
@props(['label' => '', 'name' => '', 'placeholder' => '', 'required' => false])
@props(['label' => '', 'name' => '', 'placeholder' => '', 'required' => false, 'value' => ''])
@if($label !== '')
<div class="flex flex-col space-y-2">
@ -14,7 +14,7 @@
class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
name="{{$name}}" placeholder="{{$placeholder}}"
required="{{$required?'required':''}}"
>{{old($name)}}</textarea>
>{{old($name, $value)}}</textarea>
<x-ui.inline-error :name="$name"/>
</div>

View File

@ -0,0 +1,41 @@
<x-layout title="Edit deal">
<x-dashboard.page-heading
title="Edit Deal"
description="Modify your existing deal"
:back-link="route('broker.dashboard')"
/>
<div class="flex items-center justify-center mt-8">
<x-dashboard.card class="w-8/12">
<h3 class="text-md font-bold">Deal Information</h3>
<form method="post" enctype="multipart/form-data" action="{{route('broker.deals.update', $deal)}}" class="flex flex-col space-y-8 mt-4">
@csrf
@method('PATCH')
<x-ui.input name="title" label="Deal Title" :value="$deal->title" required placeholder="e.g., Luxury Apartment Downtown"/>
<x-ui.select :options="$categories" :selected="$deal->deal_category_id" name="deal_category_id" label-key="name" value-key="id" label="Category"
placeholder="Select a category" required/>
<x-ui.textarea :value="$deal->description" name="description" label="Description" required
placeholder="Describe your deal in detail..."/>
<x-ui.image-input name="image" label="Upload image" allowed="jpg,png" size="10" :value="asset('storage/'.$deal->image)"/>
<x-ui.input
:value="$deal->link"
name="link"
label="External Link (Optional)"
placeholder="https://example.com"
description="Add a link to your website, listing page or contact form"
/>
<div class="grid grid-cols-12 w-full space-x-4">
<x-ui.button variant="neutral" class="col-span-10">Update</x-ui.button>
<a href="{{route('broker.dashboard')}}" class="ui-btn border border-accent-600/20 col-span-2">Cancel</a>
</div>
</form>
</x-dashboard.card>
</div>
</x-layout>

View File

@ -1,6 +1,17 @@
<x-layout title="Broker Dashboard">
<section class="flex flex-col space-y-8 bg-[#F9FAFB]">
<x-dashboard.navbar/>
<div class="wrapper">
@session('success')
<x-ui.alert-success>{{$value}}</x-ui.alert-success>
@endsession
@session('error')
<x-ui.alert-error>{{$value}}</x-ui.alert-error>
@endsession
</div>
<x-dashboard.stats :list_count="$deals->count()"/>
<x-dashboard.listing :deals="$deals"/>
</section>