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.json
|
||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
laradumps.yaml
|
||||||
|
|||||||
@ -19,11 +19,13 @@ protected function deals()
|
|||||||
return Auth::user()
|
return Auth::user()
|
||||||
->deals()
|
->deals()
|
||||||
->select([
|
->select([
|
||||||
|
'id',
|
||||||
'title',
|
'title',
|
||||||
'description',
|
'description',
|
||||||
'image',
|
'image',
|
||||||
'active',
|
'active',
|
||||||
'slug',
|
'slug',
|
||||||
|
'link',
|
||||||
'deal_category_id',
|
'deal_category_id',
|
||||||
])
|
])
|
||||||
->with('category:id,name')
|
->with('category:id,name')
|
||||||
|
|||||||
@ -5,9 +5,12 @@
|
|||||||
use App\Http\Requests\StoreBrokerDeal;
|
use App\Http\Requests\StoreBrokerDeal;
|
||||||
use App\Models\Deal;
|
use App\Models\Deal;
|
||||||
use App\Models\DealCategory;
|
use App\Models\DealCategory;
|
||||||
|
use App\Services\FileService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use const http\Client\Curl\AUTH_ANY;
|
|
||||||
|
|
||||||
class BrokerDealController extends Controller
|
class BrokerDealController extends Controller
|
||||||
{
|
{
|
||||||
@ -38,9 +41,9 @@ public function store(StoreBrokerDeal $request)
|
|||||||
$data['user_id'] = $request->user()->id;
|
$data['user_id'] = $request->user()->id;
|
||||||
|
|
||||||
$path = '';
|
$path = '';
|
||||||
if($request->hasFile('image')){
|
if ($request->hasFile('image')) {
|
||||||
$image = $request->file('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;
|
$data['image'] = $path;
|
||||||
|
|
||||||
@ -62,24 +65,61 @@ public function show(string $id)
|
|||||||
/**
|
/**
|
||||||
* Show the form for editing the specified resource.
|
* 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.
|
* 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.
|
* 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 [
|
return [
|
||||||
'title' => 'required|min:10|max:255',
|
'title' => 'required|min:10|max:255',
|
||||||
'description' => 'required|min:10|max:300',
|
'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',
|
'link' => 'nullable|url',
|
||||||
'deal_category_id' => 'required|exists:deal_categories,id',
|
'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": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
"laradumps/laradumps": "^5.0",
|
||||||
"laravel/pail": "^1.2.2",
|
"laravel/pail": "^1.2.2",
|
||||||
"laravel/pint": "^1.24",
|
"laravel/pint": "^1.24",
|
||||||
"laravel/sail": "^1.41",
|
"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",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2f3167c1eddf52a1400c85a1ac601d4d",
|
"content-hash": "d2215c7e3da7601aa624d057baef6449",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "blade-ui-kit/blade-heroicons",
|
"name": "blade-ui-kit/blade-heroicons",
|
||||||
@ -1204,16 +1204,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/framework",
|
"name": "laravel/framework",
|
||||||
"version": "v12.45.1",
|
"version": "v12.46.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/framework.git",
|
"url": "https://github.com/laravel/framework.git",
|
||||||
"reference": "1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa"
|
"reference": "9dcff48d25a632c1fadb713024c952fec489c4ae"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/framework/zipball/1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa",
|
"url": "https://api.github.com/repos/laravel/framework/zipball/9dcff48d25a632c1fadb713024c952fec489c4ae",
|
||||||
"reference": "1ca7e2a2ee17ae5bc435af7cb52d2f130148e2fa",
|
"reference": "9dcff48d25a632c1fadb713024c952fec489c4ae",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1422,7 +1422,7 @@
|
|||||||
"issues": "https://github.com/laravel/framework/issues",
|
"issues": "https://github.com/laravel/framework/issues",
|
||||||
"source": "https://github.com/laravel/framework"
|
"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",
|
"name": "laravel/prompts",
|
||||||
@ -6594,6 +6594,147 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-03-19T14:43:43+00:00"
|
"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",
|
"name": "laravel/pail",
|
||||||
"version": "v1.2.4",
|
"version": "v1.2.4",
|
||||||
@ -9183,6 +9324,70 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-02-07T05:00:38+00:00"
|
"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",
|
"name": "staabm/side-effects-detector",
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
@ -9422,16 +9627,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/assert",
|
"name": "webmozart/assert",
|
||||||
"version": "2.0.0",
|
"version": "2.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/webmozarts/assert.git",
|
"url": "https://github.com/webmozarts/assert.git",
|
||||||
"reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54"
|
"reference": "bdbabc199a7ba9965484e4725d66170e5711323b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
|
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bdbabc199a7ba9965484e4725d66170e5711323b",
|
||||||
"reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
|
"reference": "bdbabc199a7ba9965484e4725d66170e5711323b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -9478,9 +9683,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/webmozarts/assert/issues",
|
"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": [],
|
"aliases": [],
|
||||||
|
|||||||
@ -1,19 +1,11 @@
|
|||||||
|
const previewImage = document.getElementById('preview-image');
|
||||||
|
|
||||||
function upload(size) {
|
function upload(size) {
|
||||||
const imageInput = document.getElementById("image-input");
|
const imageInput = document.getElementById("image-input");
|
||||||
const closeModalBtn = document.getElementById("close-modal");
|
const closeModalBtn = document.getElementById("close-modal");
|
||||||
const cancelBtn = document.getElementById("cancel-modal");
|
const cancelBtn = document.getElementById("cancel-modal");
|
||||||
const modal = document.getElementById("image-modal");
|
const modal = document.getElementById("image-modal");
|
||||||
|
let imageUrl = '';
|
||||||
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();
|
|
||||||
})
|
|
||||||
|
|
||||||
const image = imageInput.files[0];
|
const image = imageInput.files[0];
|
||||||
|
|
||||||
@ -32,9 +24,21 @@ function upload(size) {
|
|||||||
|
|
||||||
fileReader.onload = (e) => {
|
fileReader.onload = (e) => {
|
||||||
const imagePlaceholder = document.getElementById("image-placeholder");
|
const imagePlaceholder = document.getElementById("image-placeholder");
|
||||||
imagePlaceholder.src = e.target.result;
|
imagePlaceholder.src = imageUrl = e.target.result;
|
||||||
modal.showModal();
|
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;
|
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">
|
<x-ui.image-card :image="$image">
|
||||||
<div class="bg-white pt-8 px-4 space-y-4">
|
<div class="bg-white pt-8 p-4 space-y-4">
|
||||||
<div class="flex justify-between items-baseline">
|
<div class="flex justify-between items-center">
|
||||||
<p class="font-bold text-lg">{{$title}}</p>
|
|
||||||
|
<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)
|
@if($status == 1)
|
||||||
<x-ui.badge title="Active"/>
|
<x-ui.badge title="Active"/>
|
||||||
@else
|
@else
|
||||||
<x-ui.badge title="Pending" variant="ghost"/>
|
<x-ui.badge title="Pending" variant="ghost"/>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-accent-600">{{$category}}</p>
|
<p class="text-accent-600">{{$category}}</p>
|
||||||
|
|
||||||
<x-dashboard.listing-stats :impression="$impressions" :likes="$likes" :clicks="$clicks"/>
|
<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>
|
</div>
|
||||||
</x-ui.image-card>
|
</x-ui.image-card>
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
<x-dashboard.card class="bg-white">
|
<x-dashboard.card class="bg-white">
|
||||||
<p class="font-bold mb-6">My Listings</p>
|
<p class="font-bold mb-6">My Listings</p>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
@foreach($deals as $deal)
|
@forelse($deals as $deal)
|
||||||
|
|
||||||
<x-dashboard.listing-card
|
<x-dashboard.listing-card
|
||||||
|
:id="$deal->id"
|
||||||
:image="asset('storage/'.$deal->image)"
|
:image="asset('storage/'.$deal->image)"
|
||||||
:title="$deal->title"
|
:title="$deal->title"
|
||||||
:category="$deal->category->name"
|
:category="$deal->category->name"
|
||||||
@ -13,8 +13,11 @@
|
|||||||
impressions="1245"
|
impressions="1245"
|
||||||
likes="89"
|
likes="89"
|
||||||
class="156"
|
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>
|
</div>
|
||||||
</x-dashboard.card>
|
</x-dashboard.card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,11 +1,29 @@
|
|||||||
@props(['variant' => ''])
|
@props(['variant' => '', 'icon' => '', 'link' => ''])
|
||||||
@php
|
@php
|
||||||
$variants = [
|
$variants = [
|
||||||
'neutral' => 'bg-primary-600 text-white'
|
'neutral' => 'bg-primary-600 text-white',
|
||||||
];
|
'red' => 'bg-red-500 text-white'
|
||||||
|
];
|
||||||
|
|
||||||
$variantClass = $variants[$variant] ?? '';
|
$variantClass = $variants[$variant] ?? '';
|
||||||
@endphp
|
@endphp
|
||||||
<button {{$attributes->merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass"])}}>
|
@if($link !== '')
|
||||||
{{$slot}}
|
<a {{$attributes->merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass", 'href' => $link])}}>
|
||||||
</button>
|
<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' => ''])
|
@props(['image' => '', 'alt' => ''])
|
||||||
<div class="border border-gray-200 rounded-xl overflow-clip">
|
<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">
|
<div class="rounded-t-xl h-40">
|
||||||
<img src="{{$image}}" alt="" class="object-cover">
|
<img src="{{$image}}" alt="" class="object-cover">
|
||||||
</div>
|
</div>
|
||||||
{{$slot}}
|
<div>
|
||||||
|
{{$slot}}
|
||||||
|
</div>
|
||||||
</div>
|
</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">
|
<div class="flex flex-col space-y-2">
|
||||||
@if($label !== '')
|
@if($label !== '')
|
||||||
<label class="text-sm font-bold" for="{{$name}}">
|
<label class="text-sm font-bold" for="{{$name}}">
|
||||||
{{$label}}
|
{{$label}} @if($required && !$value)
|
||||||
@if($required)
|
|
||||||
*
|
*
|
||||||
@endif
|
@endif
|
||||||
</label>
|
</label>
|
||||||
@endif
|
@endif
|
||||||
<div class="relative">
|
|
||||||
|
<div class="relative group">
|
||||||
<div
|
<div
|
||||||
class="p-8 border-2 border-dashed border-accent-600/70 rounded-lg flex flex-col space-y-2 justify-center items-center">
|
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">
|
||||||
<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>
|
<img src="{{ $value }}" id="preview-image"
|
||||||
<p class="text-xs text-accent-600/70">{{strtoupper($allowed)}} upto {{$size}}MB</p>
|
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>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
name="{{$name}}"
|
name="{{$name}}"
|
||||||
id="image-input"
|
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"
|
type="file"
|
||||||
|
accept="{{ $allowed ? '.' . str_replace(',', ',.', $allowed) : 'image/*' }}"
|
||||||
onchange="upload(10)"
|
onchange="upload(10)"
|
||||||
accept="image/{{$allowed}}"
|
{{ $required && !$value ? 'required' : '' }}
|
||||||
/>
|
/>
|
||||||
<x-ui.inline-error :name="$name"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<x-ui.inline-error :name="$name"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dialog id="image-modal"
|
<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>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
@vite('resources/js/image-input.js')
|
@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">
|
<div class="flex flex-col space-y-2">
|
||||||
@if($label !== '')
|
@if($label !== '')
|
||||||
<label class="text-sm font-bold" for="{{$name}}">
|
<label class="text-sm font-bold" for="{{$name}}">
|
||||||
@ -10,7 +10,7 @@
|
|||||||
@endif
|
@endif
|
||||||
<input class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
|
<input class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
|
||||||
type="{{$type}}" placeholder="{{$placeholder}}"
|
type="{{$type}}" placeholder="{{$placeholder}}"
|
||||||
name="{{$name}}" value="{{old($name)}}"
|
name="{{$name}}" value="{{old($name, $value)}}"
|
||||||
{{$required?'required':''}}
|
{{$required?'required':''}}
|
||||||
{{$attributes}}
|
{{$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">
|
<div class="flex flex-col space-y-2">
|
||||||
|
|
||||||
@if($label !== '')
|
@if($label !== '')
|
||||||
@ -20,7 +30,8 @@ class="bg-[#F3F3F5] py-2 px-4 rounded-lg text-sm font-bold invalid:text-accent-6
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
@foreach($options as $option)
|
@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
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@props(['label' => '', 'name' => '', 'placeholder' => '', 'required' => false])
|
@props(['label' => '', 'name' => '', 'placeholder' => '', 'required' => false, 'value' => ''])
|
||||||
@if($label !== '')
|
@if($label !== '')
|
||||||
<div class="flex flex-col space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
|
class="bg-[#F3F3F5] py-2 px-4 rounded-lg"
|
||||||
name="{{$name}}" placeholder="{{$placeholder}}"
|
name="{{$name}}" placeholder="{{$placeholder}}"
|
||||||
required="{{$required?'required':''}}"
|
required="{{$required?'required':''}}"
|
||||||
>{{old($name)}}</textarea>
|
>{{old($name, $value)}}</textarea>
|
||||||
|
|
||||||
<x-ui.inline-error :name="$name"/>
|
<x-ui.inline-error :name="$name"/>
|
||||||
</div>
|
</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">
|
<x-layout title="Broker Dashboard">
|
||||||
<section class="flex flex-col space-y-8 bg-[#F9FAFB]">
|
<section class="flex flex-col space-y-8 bg-[#F9FAFB]">
|
||||||
<x-dashboard.navbar/>
|
<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.stats :list_count="$deals->count()"/>
|
||||||
<x-dashboard.listing :deals="$deals"/>
|
<x-dashboard.listing :deals="$deals"/>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user