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:
parent
e2fabcd8b6
commit
a248d3fc79
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
Thumbs.db
|
||||
laradumps.yaml
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
];
|
||||
|
||||
21
app/Services/FileService.php
Normal file
21
app/Services/FileService.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
229
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": "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": [],
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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}}
|
||||
>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
41
resources/views/dashboards/broker/deals/edit.blade.php
Normal file
41
resources/views/dashboards/broker/deals/edit.blade.php
Normal 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>
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user