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:
@@ -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>
|
||||||
|
|||||||
@@ -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 có 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>
|
||||||
|
|||||||
@@ -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
14
pnpm-lock.yaml
generated
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user