fix(web): wire up inquiry modal toast notification on listing detail page

The "Nhắn tin" button's inquiry modal now shows a success toast via
sonner after submission instead of an in-dialog success state, and
closes the modal automatically. Added sonner as a dependency and
mounted <Toaster> in the root locale layout.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-16 10:56:56 +07:00
parent 25f415f3bc
commit 44533a88f4
4 changed files with 22 additions and 36 deletions

View File

@@ -3,6 +3,7 @@ import { Inter } from 'next/font/google';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl'; import { NextIntlClientProvider } from 'next-intl';
import { getMessages, getTranslations } from 'next-intl/server'; import { getMessages, getTranslations } from 'next-intl/server';
import { Toaster } from 'sonner';
import { AuthProvider } from '@/components/providers/auth-provider'; import { AuthProvider } from '@/components/providers/auth-provider';
import { NotificationsProvider } from '@/components/providers/notifications-provider'; import { NotificationsProvider } from '@/components/providers/notifications-provider';
import { QueryProvider } from '@/components/providers/query-provider'; import { QueryProvider } from '@/components/providers/query-provider';
@@ -124,6 +125,7 @@ export default async function LocaleLayout({
<QueryProvider> <QueryProvider>
<AuthProvider> <AuthProvider>
<NotificationsProvider> <NotificationsProvider>
<Toaster position="top-right" richColors closeButton />
<WebVitals /> <WebVitals />
{children} {children}
</NotificationsProvider> </NotificationsProvider>

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
@@ -38,7 +39,6 @@ export function InquiryModal({
const [message, setMessage] = React.useState(''); const [message, setMessage] = React.useState('');
const [phone, setPhone] = React.useState(''); const [phone, setPhone] = React.useState('');
const [error, setError] = React.useState<string | null>(null); const [error, setError] = React.useState<string | null>(null);
const [success, setSuccess] = React.useState(false);
// Pre-fill phone from auth store when modal opens // Pre-fill phone from auth store when modal opens
React.useEffect(() => { React.useEffect(() => {
@@ -47,7 +47,6 @@ export function InquiryModal({
} }
if (open) { if (open) {
setError(null); setError(null);
setSuccess(false);
setMessage(''); setMessage('');
} }
}, [open, user?.phone]); }, [open, user?.phone]);
@@ -81,7 +80,10 @@ export function InquiryModal({
message: trimmedMessage, message: trimmedMessage,
phone: trimmedPhone, phone: trimmedPhone,
}); });
setSuccess(true); onOpenChange(false);
toast.success('Đã gửi thành công!', {
description: `Tin nhắn của bạn đã được gửi đến ${sellerName}. Họ sẽ liên hệ với bạn sớm nhất có thể.`,
});
} catch (err) { } catch (err) {
if (err instanceof ApiError && err.status === 401) { if (err instanceof ApiError && err.status === 401) {
window.location.href = '/login'; window.location.href = '/login';
@@ -96,39 +98,6 @@ export function InquiryModal({
} }
}; };
if (success) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Đã gửi thành công!</DialogTitle>
<DialogDescription>
Tin nhắn của bạn đã đưc gửi đến {sellerName}. Họ sẽ liên hệ với bạn sớm nhất thể.
</DialogDescription>
</DialogHeader>
<div className="flex justify-center py-4">
<svg
className="h-16 w-16 text-green-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
<DialogFooter>
<Button onClick={() => onOpenChange(false)}>Đóng</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>

View File

@@ -28,6 +28,7 @@
"react-hook-form": "^7.72.1", "react-hook-form": "^7.72.1",
"recharts": "^3.8.1", "recharts": "^3.8.1",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
"web-vitals": "^5.2.0", "web-vitals": "^5.2.0",
"zod": "^4.3.6", "zod": "^4.3.6",

14
pnpm-lock.yaml generated
View File

@@ -343,6 +343,9 @@ importers:
socket.io-client: socket.io-client:
specifier: ^4.8.3 specifier: ^4.8.3
version: 4.8.3 version: 4.8.3
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwind-merge: tailwind-merge:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
@@ -6493,6 +6496,12 @@ packages:
sonic-boom@4.2.1: sonic-boom@4.2.1:
resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
sonner@2.0.7:
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -14145,6 +14154,11 @@ snapshots:
dependencies: dependencies:
atomic-sleep: 1.0.0 atomic-sleep: 1.0.0
sonner@2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
source-map-support@0.5.21: source-map-support@0.5.21: