From 9e61dd9f519c068f162c2ca92e9a04647a5c30c5 Mon Sep 17 00:00:00 2001 From: kusowl Date: Thu, 22 Jan 2026 13:34:02 +0530 Subject: [PATCH] feature(deal-modal): add interaction only in card - show current user liked and favorite status in the modal --- app/Http/Resources/DealResource.php | 7 +- package-lock.json | 4 +- resources/js/app.js | 9 +- resources/js/deal-view-modal.js | 56 ++++--- resources/js/interaction.js | 156 ++++++------------ .../dashboard/user/action-toolbar.blade.php | 38 ----- .../action-toolbar/favorite-button.blade.php | 20 +++ .../user/action-toolbar/index.blade.php | 6 + .../user/action-toolbar/like-button.blade.php | 21 +++ .../action-toolbar/report-button.blade.php | 4 + .../dashboard/user/deal-modal.blade.php | 9 +- .../dashboard/user/listing-card.blade.php | 4 +- 12 files changed, 156 insertions(+), 178 deletions(-) delete mode 100644 resources/views/components/dashboard/user/action-toolbar.blade.php create mode 100644 resources/views/components/dashboard/user/action-toolbar/favorite-button.blade.php create mode 100644 resources/views/components/dashboard/user/action-toolbar/index.blade.php create mode 100644 resources/views/components/dashboard/user/action-toolbar/like-button.blade.php create mode 100644 resources/views/components/dashboard/user/action-toolbar/report-button.blade.php diff --git a/app/Http/Resources/DealResource.php b/app/Http/Resources/DealResource.php index b8ecd0a..9fec5f6 100644 --- a/app/Http/Resources/DealResource.php +++ b/app/Http/Resources/DealResource.php @@ -23,9 +23,10 @@ public function toArray(Request $request): array 'link' => $this->link !== null ? URL::signedRoute('redirect', $this->id) : null, 'category' => $this->whenLoaded('category'), 'broker' => new UserResource($this->whenLoaded('broker')), - 'total_likes' => $this->total_likes, - 'total_redirection' => $this->total_redirection, - + 'totalLikes' => $this->total_likes, + 'totalRedirection' => $this->total_redirection, + 'isLiked' => $this->is_liked, + 'isFavorite' => $this->is_favorite, ]; } } 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 0496a19..a062cce 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,7 +1,5 @@ import './bootstrap'; import {setSidebarState} from '@/sidebar.js'; -import "./interaction.js" -import "./report-deal.js" import "./alert.js" import "./image-input.js" import "./menu.js" @@ -9,6 +7,13 @@ 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 index 271d8b4..6ba492f 100644 --- a/resources/js/deal-view-modal.js +++ b/resources/js/deal-view-modal.js @@ -1,7 +1,6 @@ import {showToast} from "@/toast.js"; import {closeModal, showModal} from "@/modal.js"; -import {favorite, like, redirect} from "@/interaction.js"; -import {showReportModal} from "@/report-deal.js"; +import {redirect} from "./interaction.js"; async function showDealModal(dealId, dealTitle) { if (dealId === null || dealId === "") { @@ -18,21 +17,8 @@ async function showDealModal(dealId, dealTitle) { const dealModal = document.getElementById('deal-modal'); - const likeBtn = dealModal.querySelector('.likeBtn'); - likeBtn.addEventListener('click', () => { - like(likeBtn, dealId); - }) - - const favoriteBtn = dealModal.querySelector('.favoriteBtn'); - favoriteBtn.addEventListener('click', () => { - favorite(favoriteBtn, dealId); - }) - - - const reportBtn = dealModal.querySelector('.reportBtn'); - reportBtn.addEventListener('click', async () => { - showReportModal(dealId, dealTitle) - }) + // Add the id so that action buttons can identify the route + dealModal.dataset.dealId = dealId; } catch (e) { console.error(e) @@ -46,11 +32,13 @@ function setDealDetails(dealDetails) { throw new Error('DealDetails must be not null'); } - // // Can throw error, handle it else - // dealDetails = JSON.parse(dealDetails); - console.log(dealDetails); const deal = dealDetails.data - const {id, title, description, link, image, category, broker, total_redirection, total_likes} = deal; + 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; @@ -60,9 +48,31 @@ function setDealDetails(dealDetails) { dealModal.querySelector('.broker-name').innerText = broker.name; dealModal.querySelector('.broker-email').innerText = broker.email; dealModal.querySelector('.broker-phone').innerText = broker.role.phone; - dealModal.querySelector('.likeCount').querySelector('p').innerText = total_likes ?? '0'; - dealModal.querySelector('.clickCount').querySelector('p').innerText = total_redirection ?? '0'; + // 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'); diff --git a/resources/js/interaction.js b/resources/js/interaction.js index f7388e1..f0d2e46 100644 --- a/resources/js/interaction.js +++ b/resources/js/interaction.js @@ -1,142 +1,90 @@ -import { showToast } from "./toast.js"; -import { showReportModal } from "@/report-deal.js"; - -/* ========================= - ACTION HANDLERS -========================= */ - -export async function like(button, id) { - if (button.dataset.loading) return; - button.dataset.loading = 'true'; - - const likeBtns = button.querySelectorAll('.like'); - const likeBadge = document.getElementById(`likeBadge${id}`); - - toggleHidden(likeBtns); - - const isLiked = button.classList.contains('liked'); - updateLikeCount(likeBadge, isLiked ? -1 : 1); - button.classList.toggle('liked'); +import {showToast} from "./toast.js"; +export async function like(button) { + const activeClasses = ['fill-current', 'text-red-500'] + let isLiked = button.dataset.liked === 'true'; try { - const response = await axios.post(`/like/${id}`); + // 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); + showToast(response.data.message) - if (response.status !== 200) { - showToast(response.data.message); - toggleHidden(likeBtns); - button.classList.toggle('liked'); - updateLikeCount(likeBadge, isLiked ? 1 : -1); - } else { - showToast(response.data.message); - } } catch (e) { - showToast(e?.response?.data?.message || 'Something went wrong'); - toggleHidden(likeBtns); - button.classList.toggle('liked'); - updateLikeCount(likeBadge, isLiked ? 1 : -1); + showToast('Something went wrong!') + // Revert the states + toggleState(button, !isLiked, activeClasses); console.error(e); - } finally { - delete button.dataset.loading; } } -export async function favorite(button, id) { - if (button.dataset.loading) return; - button.dataset.loading = 'true'; - - const favoriteBtns = button.querySelectorAll('.favorite'); - toggleHidden(favoriteBtns); - +export async function favorite(button) { + const activeClasses = ['fill-current', 'text-yellow-500'] + let isFavorite = button.dataset.favorite === 'true'; try { - const 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); - toggleHidden(favoriteBtns); - } else { - showToast(response.data.message); - } } catch (e) { - showToast(e?.response?.data?.message || 'Something went wrong'); - toggleHidden(favoriteBtns); + showToast(e.response.data.message) + toggleState(button, isFavorite, activeClasses); console.error(e); - } finally { - delete button.dataset.loading; } } export function redirect(url, id) { window.open(url, '_blank'); - const redirectBadge = document.getElementById(`redirectBadge${id}`); + let redirectBadge = document.getElementById("redirectBadge".concat(id)); updateRedirectCount(redirectBadge, 1); + // increment the count in ui } -/* ========================= - EVENT DELEGATION (🔥 FIX) -========================= */ +export function setLikeState(button, isLiked) { + button.dataset.liked = isLiked ? 'true' : 'false'; +} -document.addEventListener('click', async (e) => { - const button = e.target.closest('button'); - if (!button) return; +export function setFavoriteState(button, isFavorite) { + button.dataset.favorite = isFavorite ? 'true' : 'false'; +} - const dealCard = button.closest('.deal-card'); - if (!dealCard) return; - - const dealId = dealCard.dataset.dealId; - const dealTitle = - dealCard.querySelector('.action-toolbar')?.dataset.dealTitle; - - if (button.classList.contains('likeBtn')) { - await like(button, dealId); +function toggleState(element, condition, activeClasses) { + const svg = element.querySelector('svg'); + if (condition) { + svg.classList.add(...activeClasses); + } else { + svg.classList.remove(...activeClasses); } - - if (button.classList.contains('favoriteBtn')) { - await favorite(button, dealId); - } - - if (button.classList.contains('reportBtn')) { - showReportModal(dealId, dealTitle); - } -}); - -/* ========================= - HELPERS -========================= */ - -function toggleHidden(nodes) { - nodes.forEach(node => node.classList.toggle('hidden')); } function updateLikeCount(badge, change) { - if (!badge) return; try { - const count = Math.max( - parseInt(badge.dataset.count || '0') + change, - 0 - ); - badge.querySelector('p').innerText = count; - badge.dataset.count = count.toString(); + 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 updateRedirectCount(badge, change) { - if (!badge) return; try { - const count = Math.max( - parseInt(badge.dataset.count || '0') + change, - 0 - ); - badge.querySelector('p').innerText = count; - badge.dataset.count = count.toString(); + 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); } } - -/* ========================= - GLOBAL (if needed) -========================= */ - -window.redirect = redirect; 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 67b02a8..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])> - $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/deal-modal.blade.php b/resources/views/components/dashboard/user/deal-modal.blade.php index bbee0be..9834fdd 100644 --- a/resources/views/components/dashboard/user/deal-modal.blade.php +++ b/resources/views/components/dashboard/user/deal-modal.blade.php @@ -1,4 +1,4 @@ - +

Deal Details