diff --git a/apps/web/app/[locale]/layout.tsx b/apps/web/app/[locale]/layout.tsx index e9e6636..26bcc4b 100644 --- a/apps/web/app/[locale]/layout.tsx +++ b/apps/web/app/[locale]/layout.tsx @@ -3,6 +3,7 @@ import { Inter } from 'next/font/google'; import { notFound } from 'next/navigation'; import { NextIntlClientProvider } from 'next-intl'; import { getMessages, getTranslations } from 'next-intl/server'; +import { Toaster } from 'sonner'; import { AuthProvider } from '@/components/providers/auth-provider'; import { NotificationsProvider } from '@/components/providers/notifications-provider'; import { QueryProvider } from '@/components/providers/query-provider'; @@ -124,6 +125,7 @@ export default async function LocaleLayout({ + {children} diff --git a/apps/web/components/listings/inquiry-modal.tsx b/apps/web/components/listings/inquiry-modal.tsx index 5b69327..5de8a1c 100644 --- a/apps/web/components/listings/inquiry-modal.tsx +++ b/apps/web/components/listings/inquiry-modal.tsx @@ -1,6 +1,7 @@ 'use client'; import * as React from 'react'; +import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Dialog, @@ -38,7 +39,6 @@ export function InquiryModal({ const [message, setMessage] = React.useState(''); const [phone, setPhone] = React.useState(''); const [error, setError] = React.useState(null); - const [success, setSuccess] = React.useState(false); // Pre-fill phone from auth store when modal opens React.useEffect(() => { @@ -47,7 +47,6 @@ export function InquiryModal({ } if (open) { setError(null); - setSuccess(false); setMessage(''); } }, [open, user?.phone]); @@ -81,7 +80,10 @@ export function InquiryModal({ message: trimmedMessage, 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) { if (err instanceof ApiError && err.status === 401) { window.location.href = '/login'; @@ -96,39 +98,6 @@ export function InquiryModal({ } }; - if (success) { - return ( - - - - Đã gửi thành công! - - 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ể. - - -
- - - -
- - - -
-
- ); - } - return ( diff --git a/apps/web/package.json b/apps/web/package.json index 8c88e6e..19fbe01 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -28,6 +28,7 @@ "react-hook-form": "^7.72.1", "recharts": "^3.8.1", "socket.io-client": "^4.8.3", + "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "web-vitals": "^5.2.0", "zod": "^4.3.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d345e3..9a61eda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -343,6 +343,9 @@ importers: socket.io-client: specifier: ^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: specifier: ^3.5.0 version: 3.5.0 @@ -6493,6 +6496,12 @@ packages: sonic-boom@4.2.1: 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: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -14145,6 +14154,11 @@ snapshots: dependencies: 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-support@0.5.21: