diff --git a/app/Enums/InteractionType.php b/app/Enums/InteractionType.php index e545e96..a25146f 100644 --- a/app/Enums/InteractionType.php +++ b/app/Enums/InteractionType.php @@ -11,4 +11,5 @@ enum InteractionType: string case Like = 'like'; case Favorite = 'favorite'; case Redirection = 'redirect'; + case View = 'view'; } diff --git a/app/Http/Controllers/Interaction/InteractionController.php b/app/Http/Controllers/Interaction/InteractionController.php index ec126fc..0d814a4 100644 --- a/app/Http/Controllers/Interaction/InteractionController.php +++ b/app/Http/Controllers/Interaction/InteractionController.php @@ -97,4 +97,36 @@ public function redirect(Deal $deal) return redirect()->away($deal->link); } + + public function view(Deal $deal) + { + ds('hi'); + try { + $interaction = $deal->interactions()->firstOrCreate([ + 'type' => InteractionType::View, + 'user_id' => Auth::id(), + ]); + + if (! $interaction->wasRecentlyCreated) { + $interaction->increment('count'); + } + ds($interaction); + + } catch (\Throwable $e) { + Log::error('Error when view a deal', + [ + 'deal_id' => $deal->id, + 'user_id' => Auth::id(), + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ] + ); + + return response()->json(['error' => 'Something went wrong.'], 500); + } + + return response()->json([ + 'message' => 'View counted', + ]); + } } diff --git a/app/Http/Resources/BrokerRoleResource.php b/app/Http/Resources/BrokerRoleResource.php new file mode 100644 index 0000000..f0390b7 --- /dev/null +++ b/app/Http/Resources/BrokerRoleResource.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'bio' => $this->bio, + 'location' => $this->location, + 'phone' => $this->phone, + ]; + } +} diff --git a/app/Http/Resources/DealResource.php b/app/Http/Resources/DealResource.php new file mode 100644 index 0000000..9fec5f6 --- /dev/null +++ b/app/Http/Resources/DealResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'title' => $this->title, + 'description' => $this->description, + 'image' => asset('storage/'.$this->image), + 'link' => $this->link !== null ? URL::signedRoute('redirect', $this->id) : null, + 'category' => $this->whenLoaded('category'), + 'broker' => new UserResource($this->whenLoaded('broker')), + 'totalLikes' => $this->total_likes, + 'totalRedirection' => $this->total_redirection, + 'isLiked' => $this->is_liked, + 'isFavorite' => $this->is_favorite, + ]; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php new file mode 100644 index 0000000..57438b4 --- /dev/null +++ b/app/Http/Resources/UserResource.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'role' => new BrokerRoleResource($this->whenLoaded('role')), + ]; + } +} diff --git a/app/Models/Deal.php b/app/Models/Deal.php index 207db51..ef3c218 100644 --- a/app/Models/Deal.php +++ b/app/Models/Deal.php @@ -92,7 +92,9 @@ public function WithRedirectionPerDeal(Builder $query): Builder #[Scope] public function search(Builder $query, string $search): Builder { - return $query->where('title', 'LIKE', "%$search%"); + return $query + ->where('title', 'LIKE', "%$search%") + ->orWhereRelation('broker', 'name', 'LIKE', "%$search%"); } /** diff --git a/database/migrations/2026_01_22_083544_add_view_interaction.php b/database/migrations/2026_01_22_083544_add_view_interaction.php new file mode 100644 index 0000000..e63e575 --- /dev/null +++ b/database/migrations/2026_01_22_083544_add_view_interaction.php @@ -0,0 +1,27 @@ +enum('type', InteractionType::values())->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('interactions', function (Blueprint $table) {}); + } +}; diff --git a/package-lock.json b/package-lock.json index 986036e..71f98a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "dealhub", + "name": "DealHub", "lockfileVersion": 3, "requires": true, "packages": { @@ -2031,7 +2031,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2268,7 +2267,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/resources/js/app.js b/resources/js/app.js index 9ab439d..a062cce 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,5 +1,19 @@ import './bootstrap'; -import { setSidebarState } from '@/sidebar.js'; +import {setSidebarState} from '@/sidebar.js'; +import "./alert.js" +import "./image-input.js" +import "./menu.js" +import "./modal.js" +import "./sidebar.js" +import "./toast.js" +import "./deal-view-modal.js" +import {favorite, like, redirect} from "./interaction.js"; +import {showReportModal} from "./report-deal.js"; + +document.like = like; +document.favorite = favorite; +document.redirect = redirect; +document.showReportModal = showReportModal; window.addEventListener('load', () => { const preloader = document.getElementById('preloader'); diff --git a/resources/js/deal-view-modal.js b/resources/js/deal-view-modal.js new file mode 100644 index 0000000..8c73c49 --- /dev/null +++ b/resources/js/deal-view-modal.js @@ -0,0 +1,106 @@ +import {showToast} from "@/toast.js"; +import {closeModal, showModal} from "@/modal.js"; +import {redirect} from "./interaction.js"; + +async function showDealModal(dealId) { + if (dealId === null || dealId === "") { + showToast('Something went wrong!'); + return; + } + + // Open the deal modal and show a loading state + showModal('deal-modal') + + try { + const response = await axios.get('/api/deals/' + dealId); + setDealDetails(response.data); + + const dealModal = document.getElementById('deal-modal'); + + // Add the id so that action buttons can identify the route + dealModal.dataset.dealId = dealId; + + // Increment the view count + await axios.post(`/api/view/${dealId}`); + } catch (e) { + console.error(e) + closeModal('deal-modal') + showToast('Something went wrong!'); + } +} + +function setDealDetails(dealDetails) { + if (dealDetails === null) { + throw new Error('DealDetails must be not null'); + } + + const deal = dealDetails.data + const { + id, title, description, link, + image, category, broker, + totalRedirection, totalLikes, + isLiked, isFavorite + } = deal; + const dealModal = document.getElementById('deal-modal'); + + dealModal.querySelector('.deal-image').src = image; + dealModal.querySelector('.deal-title').innerText = title; + dealModal.querySelector('.deal-description').innerText = description; + dealModal.querySelector('.deal-category').innerText = category.name; + dealModal.querySelector('.broker-name').innerText = broker.name; + dealModal.querySelector('.broker-email').innerText = broker.email; + dealModal.querySelector('.broker-phone').innerText = broker.role.phone; + + // Set the like and click counts + let likeCountElm = dealModal.querySelector('.likeCount'); + likeCountElm.querySelector('p').innerText = totalLikes ?? '0'; + likeCountElm.dataset.count = totalLikes ?? '0'; + dealModal.querySelector('.clickCount').querySelector('p').innerText = totalRedirection ?? '0'; + + // Set if current user has already liked the deal + let likeBtn = dealModal.querySelector('.likeBtn'); + let likeBtnSvg = likeBtn.querySelector('svg'); + if (isLiked) { + likeBtn.dataset.liked = 'true' + likeBtnSvg.classList.add('text-red-500', 'fill-current'); + } else { + likeBtn.dataset.liked = 'false' + likeBtnSvg.classList.remove('text-red-500', 'fill-current'); + } + + // Set if current user has already favorite the deal + let favoriteBtnSvg = dealModal.querySelector('.favoriteBtn').querySelector('svg'); + if (isFavorite) { + favoriteBtnSvg.classList.add('text-yellow-500', 'fill-current'); + } else { + favoriteBtnSvg.classList.remove('text-yellow-500', 'fill-current'); + } + const dealLink = dealModal.querySelector('.deal-link'); + if (link !== null && link !== "") { + dealLink.classList.remove('hidden'); + dealLink.classList.add('flex'); + dealLink.addEventListener('click', () => { + redirect(link); + }) + } else { + dealLink.classList.remove('flex'); + dealLink.classList.add('hidden'); + } +} + +window.addEventListener('DOMContentLoaded', () => { + const dealCards = document.querySelectorAll('.deal-card'); + if (dealCards) { + dealCards.forEach(dealCard => { + let dealId = dealCard.dataset.dealId; + let dealTitle = dealCard.querySelector('.action-toolbar').dataset.dealTitle; + + dealCard.addEventListener('click', async (e) => { + if (e.target.closest('button')) { + return; + } + await showDealModal(dealId, dealTitle); + }); + }) + } +}); diff --git a/resources/js/interaction.js b/resources/js/interaction.js index 6d5d632..f0d2e46 100644 --- a/resources/js/interaction.js +++ b/resources/js/interaction.js @@ -1,87 +1,90 @@ import {showToast} from "./toast.js"; -async function like(button, id) { - // Instant feedback for user - let likeBtns = button.querySelectorAll('.like'); - let likeBadge = document.getElementById("likeBadge".concat(id)); - toggleHidden(likeBtns); - - // Check if user liked the deal - let isLiked = button.classList.contains('liked'); - updateLikeCount(likeBadge, isLiked ? -1 : 1) ; - button.classList.toggle('liked') +export async function like(button) { + const activeClasses = ['fill-current', 'text-red-500'] + let isLiked = button.dataset.liked === 'true'; try { + // Update the like state and count + let id = button.closest('.deal-identifier').dataset.dealId; + const cardLikeBadge = document.getElementById(`likeBadge${id}`); + + setLikeState(button, !isLiked); + updateLikeCount(cardLikeBadge, isLiked ? -1 : 1); + + // Update the state of like button that is clicked + toggleState(button, !isLiked, activeClasses); + let response = await axios.post('/like/' + id); - - if (response.status !== 200) { - showToast(response.data.message) - // Revert the ui - toggleHidden(likeBtns); - } - else{ - showToast(response.data.message) - } + showToast(response.data.message) } catch (e) { - showToast(e.response.data.message) - toggleHidden(likeBtns); + showToast('Something went wrong!') + // Revert the states + toggleState(button, !isLiked, activeClasses); console.error(e); } } -async function favorite(e, id) { - // Instant feedback for user - let favoriteBtns = e.querySelectorAll('.favorite'); - toggleHidden(favoriteBtns); - +export async function favorite(button) { + const activeClasses = ['fill-current', 'text-yellow-500'] + let isFavorite = button.dataset.favorite === 'true'; try { - let response = await axios.post('/favorite/' + id); + let id = button.closest('.deal-identifier').dataset.dealId; + + setFavoriteState(button, !isFavorite); + toggleState(button, !isFavorite, activeClasses); + + let response = await axios.post('/favorite/' + id); + showToast(response.data.message) - if (response.status !== 200) { - showToast(response.data.message) - // Revert the ui - toggleHidden(favoriteBtns); - }else{ - showToast(response.data.message); - } } catch (e) { showToast(e.response.data.message) - toggleHidden(favoriteBtns); + toggleState(button, isFavorite, activeClasses); console.error(e); } } -function toggleHidden(nodelist) { - nodelist.forEach((node) => node.classList.toggle('hidden')) -} - -function updateLikeCount(badge, change){ - try{ - let likeCount = Math.max(parseInt( badge.dataset.count) + change, 0) - badge.querySelector('p').innerText = likeCount; - badge.dataset.count = likeCount.toString(); - } - catch(e) { - console.error(e); - } -} - -function redirect(url, id){ +export function redirect(url, id) { window.open(url, '_blank'); let redirectBadge = document.getElementById("redirectBadge".concat(id)); updateRedirectCount(redirectBadge, 1); // increment the count in ui } -function updateRedirectCount(badge, change){ - try{ - let likeCount = Math.max(parseInt( badge.dataset.count) + change, 0) + +export function setLikeState(button, isLiked) { + button.dataset.liked = isLiked ? 'true' : 'false'; +} + +export function setFavoriteState(button, isFavorite) { + button.dataset.favorite = isFavorite ? 'true' : 'false'; +} + +function toggleState(element, condition, activeClasses) { + const svg = element.querySelector('svg'); + if (condition) { + svg.classList.add(...activeClasses); + } else { + svg.classList.remove(...activeClasses); + } +} + +function updateLikeCount(badge, change) { + try { + let likeCount = Math.max(parseInt(badge.dataset.count) + change, 0) badge.querySelector('p').innerText = likeCount; badge.dataset.count = likeCount.toString(); - } - catch(e) { + } catch (e) { + console.error(e); + } +} + + +function updateRedirectCount(badge, change) { + try { + let likeCount = Math.max(parseInt(badge.dataset.count) + change, 0) + badge.querySelector('p').innerText = likeCount; + badge.dataset.count = likeCount.toString(); + } catch (e) { console.error(e); } } -document.like = like; -document.favorite = favorite; -document.redirect = redirect; diff --git a/resources/js/nav-menu.js b/resources/js/nav-menu.js index b135c36..c41bc3e 100644 --- a/resources/js/nav-menu.js +++ b/resources/js/nav-menu.js @@ -3,14 +3,18 @@ const closeBtn = document.getElementById('closeBtn'); const mobileMenu = document.getElementById('mobileMenu'); const body = document.body; -openBtn.addEventListener('click', () => { - mobileMenu.classList.remove('translate-x-full'); - mobileMenu.classList.add('translate-x-0'); - body.style.overflow = 'hidden'; -}) +if (openBtn) { + openBtn.addEventListener('click', () => { + mobileMenu.classList.remove('translate-x-full'); + mobileMenu.classList.add('translate-x-0'); + body.style.overflow = 'hidden'; + }) +} -closeBtn.addEventListener('click', () => { - mobileMenu.classList.add('translate-x-full'); - mobileMenu.classList.remove('translate-x-0'); - body.style.overflow = 'visible'; -}) +if (closeBtn) { + closeBtn.addEventListener('click', () => { + mobileMenu.classList.add('translate-x-full'); + mobileMenu.classList.remove('translate-x-0'); + body.style.overflow = 'visible'; + }) +} diff --git a/resources/js/report-deal.js b/resources/js/report-deal.js index afa7744..cad0547 100644 --- a/resources/js/report-deal.js +++ b/resources/js/report-deal.js @@ -1,10 +1,11 @@ import {closeModal, showModal} from './modal.js'; import {showToast} from './toast.js'; + const reportModal = document.getElementById('report-modal'); const reportForm = document.getElementById('report-form'); -function showReportModal(dealId, dealTitle) { +export function showReportModal(dealId, dealTitle) { // Clear the fields reportForm.reset(); const oldErrors = reportForm.querySelectorAll('.text-red-500'); @@ -18,43 +19,44 @@ function showReportModal(dealId, dealTitle) { showModal('report-modal'); } -reportForm.addEventListener('submit', async function (form) { - form.preventDefault(); - const formData = new FormData(this); - const dealId = reportModal.dataset.dealId - try { +if (reportForm) { - let response = await axios.post( - `report/${dealId}`, - formData - ); + reportForm.addEventListener('submit', async function (form) { + form.preventDefault(); + const formData = new FormData(this); + const dealId = reportModal.dataset.dealId + try { - showToast('Report submitted. Thank you for keeping DealHub safe!'); - closeModal('report-modal') + let response = await axios.post( + `report/${dealId}`, + formData + ); - } catch (error) { + showToast('Report submitted. Thank you for keeping DealHub safe!'); + closeModal('report-modal') - if (error.response.status === 405) { - closeModal('report-modal'); - showToast('You already have reported this deal !'); - return; + } catch (error) { + + if (error.response.status === 405) { + closeModal('report-modal'); + showToast('You already have reported this deal !'); + return; + } + + // Iterate over the all error messages spans and show validation errors + if (error.response.status === 422) { + + let errors = error.response.data.errors; + + Object.keys(errors).forEach(error => { + let errorField = reportForm.querySelector(`[name="${error}"]`) + const errorSpan = document.createElement('span'); + errorSpan.textContent = errors[error][0]; + errorSpan.classList.add('text-red-500'); + errorSpan.classList.add('text-sm'); + errorField.insertAdjacentElement('afterend', errorSpan); + }) + } } - - // Iterate over the all error messages spans and show validation errors - if (error.response.status === 422) { - - let errors = error.response.data.errors; - - Object.keys(errors).forEach(error => { - let errorField = reportForm.querySelector(`[name="${error}"]`) - const errorSpan = document.createElement('span'); - errorSpan.textContent = errors[error][0]; - errorSpan.classList.add('text-red-500'); - errorSpan.classList.add('text-sm'); - errorField.insertAdjacentElement('afterend', errorSpan); - }) - } - } -}); - -document.showReportModal = showReportModal; + }); +} diff --git a/resources/js/toast.js b/resources/js/toast.js index 0a4312d..affafaa 100644 --- a/resources/js/toast.js +++ b/resources/js/toast.js @@ -1,7 +1,8 @@ const toast = document.querySelector('.toast'); const toastBtn = document.querySelector('#toast-btn'); -let toastMessage = toast.querySelector('#toast-message'); + export function showToast(message) { + let toastMessage = toast.querySelector('#toast-message'); toast.classList.remove('translate-x-[100vw]'); toast.classList.add('translate-x-0'); toastMessage.textContent = message; @@ -9,9 +10,11 @@ export function showToast(message) { hideToast(); }, 5000) } + function hideToast() { toast.classList.remove('translate-x-0'); toast.classList.add('translate-x-[100vw]'); } + document.hideToast = hideToast; document.showToast = showToast; diff --git a/resources/views/components/dashboard/broker/layout.blade.php b/resources/views/components/dashboard/broker/layout.blade.php index ca7bf32..33875d9 100644 --- a/resources/views/components/dashboard/broker/layout.blade.php +++ b/resources/views/components/dashboard/broker/layout.blade.php @@ -23,5 +23,4 @@ class=" flex flex-col space-y-4 md:space-y-8 bg-[#F9FAFB] overflow-y-auto overfl {{$slot}} - @vite('resources/js/nav-menu.js') diff --git a/resources/views/components/dashboard/user/action-toolbar.blade.php b/resources/views/components/dashboard/user/action-toolbar.blade.php deleted file mode 100644 index 5324808..0000000 --- a/resources/views/components/dashboard/user/action-toolbar.blade.php +++ /dev/null @@ -1,38 +0,0 @@ -@props(['deal_id', 'deal_title', 'like' => false, 'favourite' => false]) -
- $like]) onclick="like(this, {{$deal_id}})"> - $like - ]) - /> - !$like - ]) - /> - - - - $favourite - ]) - /> - - !$favourite - ]) - /> - - - - - - -
diff --git a/resources/views/components/dashboard/user/action-toolbar/favorite-button.blade.php b/resources/views/components/dashboard/user/action-toolbar/favorite-button.blade.php new file mode 100644 index 0000000..43f19bc --- /dev/null +++ b/resources/views/components/dashboard/user/action-toolbar/favorite-button.blade.php @@ -0,0 +1,20 @@ +@props(['isFavourite' => false, 'isInteractive'=> true]) +@php + $actionAttributes = $isInteractive ? [ + 'data-favorite' => $isFavourite ? 'true' : 'false', + 'onclick' => "favorite(this)" + ] : [] ; + $buttonClasses = [ + 'text-accent-600 favoriteBtn', + ]; +@endphp +merge($actionAttributes)->class($buttonClasses)}} +> + $isFavourite + ]) + /> + diff --git a/resources/views/components/dashboard/user/action-toolbar/index.blade.php b/resources/views/components/dashboard/user/action-toolbar/index.blade.php new file mode 100644 index 0000000..ab1c75d --- /dev/null +++ b/resources/views/components/dashboard/user/action-toolbar/index.blade.php @@ -0,0 +1,6 @@ +@props(['deal_id' => '', 'deal_title' => '', 'isLiked' => false, 'isFavourite' => false, 'isInteractive' => true]) +
+ + + +
diff --git a/resources/views/components/dashboard/user/action-toolbar/like-button.blade.php b/resources/views/components/dashboard/user/action-toolbar/like-button.blade.php new file mode 100644 index 0000000..034ed7a --- /dev/null +++ b/resources/views/components/dashboard/user/action-toolbar/like-button.blade.php @@ -0,0 +1,21 @@ +@props(['isLiked' => false, 'isInteractive' => true]) +@php + $actionAttributes = $isInteractive ? [ + 'data-liked' => $isLiked ? 'true' : 'false', + 'onclick' => "like(this)" + ] : [] ; + $buttonClasses = [ + 'text-accent-600 likeBtn', + 'liked' => $isLiked + ]; +@endphp +merge($actionAttributes)->class($buttonClasses)}} +> + $isLiked + ]) + /> + diff --git a/resources/views/components/dashboard/user/action-toolbar/report-button.blade.php b/resources/views/components/dashboard/user/action-toolbar/report-button.blade.php new file mode 100644 index 0000000..2ad5cfb --- /dev/null +++ b/resources/views/components/dashboard/user/action-toolbar/report-button.blade.php @@ -0,0 +1,4 @@ +@props(['id', 'title']) + + + diff --git a/resources/views/components/dashboard/user/broker-contact.blade.php b/resources/views/components/dashboard/user/broker-contact.blade.php index f8a615d..85b661f 100644 --- a/resources/views/components/dashboard/user/broker-contact.blade.php +++ b/resources/views/components/dashboard/user/broker-contact.blade.php @@ -1,10 +1,10 @@ -@props(['broker']) -
+@props(['broker' => '']) +

Broker Contact

-

{{$broker->name}}

-

{{$broker->email}}

-

{{$broker->role->phone}}

+

{{$broker->name ?? ''}}

+

{{$broker->email ?? ''}}

+

{{$broker->role->phone ?? ''}}

diff --git a/resources/views/components/dashboard/user/deal-modal.blade.php b/resources/views/components/dashboard/user/deal-modal.blade.php new file mode 100644 index 0000000..9834fdd --- /dev/null +++ b/resources/views/components/dashboard/user/deal-modal.blade.php @@ -0,0 +1,37 @@ + +
+

Deal Details

+ +
+
+ +
+ +

+

+
+
+ + +
+ +
+ + +
+
+ + +
diff --git a/resources/views/components/dashboard/user/deal-stats.blade.php b/resources/views/components/dashboard/user/deal-stats.blade.php new file mode 100644 index 0000000..2ce4198 --- /dev/null +++ b/resources/views/components/dashboard/user/deal-stats.blade.php @@ -0,0 +1,10 @@ +@props(['likeCount' => 0, 'clickCount' => 0]) +
+ + + + + + + +
diff --git a/resources/views/components/dashboard/user/listing-card.blade.php b/resources/views/components/dashboard/user/listing-card.blade.php index 2cd9bb3..7da308e 100644 --- a/resources/views/components/dashboard/user/listing-card.blade.php +++ b/resources/views/components/dashboard/user/listing-card.blade.php @@ -1,11 +1,11 @@ @props(['deal' => '', 'broker' => '']) - +
{{$deal->category->name}} - +

{{$deal->title}}

@@ -25,7 +25,7 @@
@if(filled($deal->link)) - +

View Deal

diff --git a/resources/views/components/ui/modal.blade.php b/resources/views/components/ui/modal.blade.php index 342bafb..2549cee 100644 --- a/resources/views/components/ui/modal.blade.php +++ b/resources/views/components/ui/modal.blade.php @@ -1,5 +1,5 @@ merge(["class"=>"fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg p-4 shadow-lg"])}} > + {{$attributes->merge(["class"=>"fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg p-2 md:p-4 shadow-lg"])}} >
{{$slot}}
diff --git a/resources/views/explore.blade.php b/resources/views/explore.blade.php index b99eb7d..55fc2ab 100644 --- a/resources/views/explore.blade.php +++ b/resources/views/explore.blade.php @@ -27,7 +27,7 @@ + - @vite(['resources/js/menu.js', 'resources/js/interaction.js', 'resources/js/report-deal.js', 'resources/js/toast.js']) diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 69e909f..7c974dc 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -5,5 +5,4 @@ - @vite('resources/js/nav-menu.js') diff --git a/routes/api/api.php b/routes/api/api.php new file mode 100644 index 0000000..3a7e4cd --- /dev/null +++ b/routes/api/api.php @@ -0,0 +1,10 @@ +middleware('auth') + ->group(function () { + include __DIR__.'/interactions.php'; + include __DIR__.'/deals.php'; + }); diff --git a/routes/api/deals.php b/routes/api/deals.php new file mode 100644 index 0000000..ea3872a --- /dev/null +++ b/routes/api/deals.php @@ -0,0 +1,14 @@ +builder() + ->where('id', $deal->id) + ->first() + ); +}); diff --git a/routes/api/interactions.php b/routes/api/interactions.php new file mode 100644 index 0000000..3f3eac8 --- /dev/null +++ b/routes/api/interactions.php @@ -0,0 +1,7 @@ +middleware('throttle:30,1') + ->name('view-deal'); diff --git a/routes/web.php b/routes/web.php index 448fdf4..521431c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,3 +19,10 @@ ->middleware(HasRole::class.':'.UserTypes::Admin->value) ->name('admin.dashboard'); }); + +/** + * This routes are accessed by JS XHR requests, and is loaded here cause + * we do not want to use sanctum for web requests + */ +// ------------- API Routes ------------ +require __DIR__.'/api/api.php';