diff --git a/.gitignore b/.gitignore index b71b1ea..6220fcf 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ Homestead.json Homestead.yaml Thumbs.db +laradumps.yaml diff --git a/app/Http/Controllers/Broker/BrokerDashboardController.php b/app/Http/Controllers/Broker/BrokerDashboardController.php index 89ec053..8a2be20 100644 --- a/app/Http/Controllers/Broker/BrokerDashboardController.php +++ b/app/Http/Controllers/Broker/BrokerDashboardController.php @@ -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') diff --git a/app/Http/Controllers/BrokerDealController.php b/app/Http/Controllers/BrokerDealController.php index 2790587..ed6e8cd 100644 --- a/app/Http/Controllers/BrokerDealController.php +++ b/app/Http/Controllers/BrokerDealController.php @@ -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.'); } } diff --git a/app/Http/Requests/StoreBrokerDeal.php b/app/Http/Requests/StoreBrokerDeal.php index 29674b8..3b3b9e0 100644 --- a/app/Http/Requests/StoreBrokerDeal.php +++ b/app/Http/Requests/StoreBrokerDeal.php @@ -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', ]; diff --git a/app/Services/FileService.php b/app/Services/FileService.php new file mode 100644 index 0000000..9b7f134 --- /dev/null +++ b/app/Services/FileService.php @@ -0,0 +1,21 @@ +storeAs($folder, $filename.'.'.$file->extension(), 'public'); + } + + public function delete(?string $path): void + { + if ($path && Storage::disk('public')->exists($path)) { + Storage::disk('public')->delete($path); + } + } +} diff --git a/composer.json b/composer.json index 746e4aa..266a839 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/composer.lock b/composer.lock index 478352f..53dbe20 100644 --- a/composer.lock +++ b/composer.lock @@ -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": [], diff --git a/resources/js/image-input.js b/resources/js/image-input.js index 39f6153..5f31410 100644 --- a/resources/js/image-input.js +++ b/resources/js/image-input.js @@ -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; diff --git a/resources/views/components/dashboard/listing-card.blade.php b/resources/views/components/dashboard/listing-card.blade.php index ed282b7..f0ea3a0 100644 --- a/resources/views/components/dashboard/listing-card.blade.php +++ b/resources/views/components/dashboard/listing-card.blade.php @@ -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' => '']) + -
-
-

{{$title}}

+
+
+ +
+

{{$title}}

+ + @if($external_link !== '') + + + + @endif + +
+ @if($status == 1) @else @endif +
+

{{$category}}

+ + +
+ Edit + +
+ @csrf + @method("DELETE") + Delete +
+
+
diff --git a/resources/views/components/dashboard/listing.blade.php b/resources/views/components/dashboard/listing.blade.php index fe63db6..915dd90 100644 --- a/resources/views/components/dashboard/listing.blade.php +++ b/resources/views/components/dashboard/listing.blade.php @@ -3,9 +3,9 @@

My Listings

- @foreach($deals as $deal) - + @forelse($deals as $deal) - @endforeach + @empty +

No Deals created

+ @endforelse
diff --git a/resources/views/components/ui/button.blade.php b/resources/views/components/ui/button.blade.php index 5e84e25..6251e1a 100644 --- a/resources/views/components/ui/button.blade.php +++ b/resources/views/components/ui/button.blade.php @@ -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 - +@if($link !== '') + merge(['class' => "px-4 py-2 rounded-lg font-medium hover:opacity-80 $variantClass", 'href' => $link])}}> +
+ @if($icon !=='') + @svg("heroicon-o-$icon", 'w-5 h-5') + @endif +

{{$slot}}

+
+
+@else + +@endif diff --git a/resources/views/components/ui/image-card.blade.php b/resources/views/components/ui/image-card.blade.php index 8ff1143..633ee26 100644 --- a/resources/views/components/ui/image-card.blade.php +++ b/resources/views/components/ui/image-card.blade.php @@ -1,9 +1,11 @@ @props(['image' => '', 'alt' => ''])
-
+
- {{$slot}} +
+ {{$slot}} +
diff --git a/resources/views/components/ui/image-input.blade.php b/resources/views/components/ui/image-input.blade.php index efe2c58..1f752d2 100644 --- a/resources/views/components/ui/image-input.blade.php +++ b/resources/views/components/ui/image-input.blade.php @@ -1,32 +1,40 @@ -@props(['name' => '', 'label' => '', 'allowed' => '', 'size' => '1', 'required' => false]) +@props(['name' => '', 'label' => '', 'allowed' => '', 'size' => '1', 'required' => false, 'value' => ''])
@if($label !== '') @endif -
+ +
- -

Click to upload or drag and drop

-

{{strtoupper($allowed)}} upto {{$size}}MB

+ 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"> + + + + +

+ {{ $value ? 'Click to replace current image' : 'Click to upload or drag and drop' }} +

+

{{strtoupper($allowed)}} up to {{$size}}MB

+ -
+
@if($label !== '')