please add rental message list to user dashboard, and user can reply ren

This commit is contained in:
Indigo Tang 2025-06-10 06:02:29 +00:00
parent 888355d0cb
commit a2b1eae0fe
6 changed files with 197 additions and 3 deletions

View File

@ -0,0 +1,93 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { mockRentalRequests } from "@/lib/mockData";
import type { RentalRequest, MessageEntry } from "@/types";
import { getI18n } from "@/locales/server";
import type { Locale } from "@/locales/server";
import Link from "next/link";
import { MessageSquareQuote, ToyBrick } from "lucide-react";
import { format } from 'date-fns';
const currentUserId = 'user1'; // Assume this is the logged-in user's ID
// Helper function to get the other participant in a conversation
const getOtherParticipant = (request: RentalRequest, currentUserId: string) => {
if (request.toy.ownerId === currentUserId) {
return { id: request.requesterId, name: request.requesterName };
}
return { id: request.toy.ownerId, name: request.toy.ownerName };
};
export default async function MessagesPage({ params }: { params: { locale: Locale } }) {
const t = await getI18n();
const locale = params.locale;
const userConversations = mockRentalRequests.filter(req =>
(req.toy.ownerId === currentUserId || req.requesterId === currentUserId) &&
req.messages && req.messages.length > 0
);
return (
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold font-headline text-primary">{t('dashboard.messages.title')}</h1>
<p className="text-muted-foreground">{t('dashboard.messages.description')}</p>
</div>
{userConversations.length === 0 ? (
<Card className="text-center py-12 shadow-md">
<CardHeader>
<MessageSquareQuote className="h-16 w-16 mx-auto text-muted-foreground mb-4" />
<CardTitle>{t('dashboard.messages.no_messages_title')}</CardTitle>
<CardDescription>{t('dashboard.messages.no_messages_description')}</CardDescription>
</CardHeader>
<CardContent>
<Link href={`/${locale}/`} passHref>
<Button size="lg">
<ToyBrick className="mr-2 h-5 w-5" />
{t('dashboard.messages.go_find_toys')}
</Button>
</Link>
</CardContent>
</Card>
) : (
<div className="space-y-6">
{userConversations.map(request => {
const otherParticipant = getOtherParticipant(request, currentUserId);
const lastMessage = request.messages![request.messages!.length - 1];
const formattedTimestamp = lastMessage ? format(new Date(lastMessage.timestamp), 'PPp') : '';
return (
<Card key={request.id} className="shadow-md hover:shadow-lg transition-shadow">
<CardHeader>
<CardTitle className="text-xl font-headline">
{t('dashboard.messages.conversation_with', { name: otherParticipant.name })}
</CardTitle>
<CardDescription>
{t('dashboard.messages.about_toy', { toyName: request.toy.name })}
</CardDescription>
</CardHeader>
<CardContent>
{lastMessage && (
<div className="text-sm text-muted-foreground">
<p className="font-medium text-foreground line-clamp-2">
{t('dashboard.messages.last_message_from', { senderName: lastMessage.senderName, text: lastMessage.text })}
</p>
<p className="text-xs mt-1">{formattedTimestamp}</p>
</div>
)}
</CardContent>
<CardFooter>
<Link href={`/${locale}/dashboard/messages/${request.id}`} passHref>
<Button variant="outline">{t('dashboard.messages.view_conversation_button')}</Button>
</Link>
</CardFooter>
</Card>
);
})}
</div>
)}
</div>
);
}

View File

@ -1,8 +1,9 @@
'use client'; 'use client';
import Link from 'next/link'; import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation'; import { usePathname, useRouter } from 'next/navigation';
import { Home, ToyBrick, PlusCircle, ListOrdered, Settings, ShoppingBag, LogOutIcon, History } from 'lucide-react'; import { Home, ToyBrick, PlusCircle, ListOrdered, Settings, ShoppingBag, LogOutIcon, History, MessageSquareMore } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
@ -23,6 +24,7 @@ export default function DashboardSidebar() {
{ href: '/dashboard/rentals', label: t('dashboard.sidebar.my_rentals'), icon: ShoppingBag }, { href: '/dashboard/rentals', label: t('dashboard.sidebar.my_rentals'), icon: ShoppingBag },
{ href: '/dashboard/requests', label: t('dashboard.sidebar.rental_requests'), icon: ListOrdered }, { href: '/dashboard/requests', label: t('dashboard.sidebar.rental_requests'), icon: ListOrdered },
{ href: '/dashboard/rental-history', label: t('dashboard.sidebar.rental_history'), icon: History }, { href: '/dashboard/rental-history', label: t('dashboard.sidebar.rental_history'), icon: History },
{ href: '/dashboard/messages', label: t('dashboard.sidebar.messages'), icon: MessageSquareMore },
]; ];
const accountNavItems = [ const accountNavItems = [

View File

@ -1,5 +1,6 @@
import type { Toy, RentalHistoryEntry } from '@/types';
import { addDays, formatISO } from 'date-fns'; import type { Toy, RentalHistoryEntry, RentalRequest, MessageEntry } from '@/types';
import { addDays, formatISO, subDays } from 'date-fns';
const today = new Date(); const today = new Date();
@ -124,6 +125,62 @@ export const mockRentalHistory: RentalHistoryEntry[] = [
} }
]; ];
export const mockRentalRequests: RentalRequest[] = [
{
id: 'req1',
toy: mockToys[0], // Owned by user1 (Alice)
requesterName: 'Charlie Brown',
requesterId: 'user4',
requestedDates: 'August 10, 2024 - August 17, 2024',
status: 'pending',
message: 'My son would love to play with these for his birthday week! We are very careful with toys and will ensure it is returned in perfect condition. Could we possibly pick it up on the 9th evening?',
messages: [
{ id: 'm1-1', senderId: 'user4', senderName: 'Charlie Brown', text: 'Hi Alice, is this available for my son next week?', timestamp: subDays(new Date(), 2).toISOString() },
{ id: 'm1-2', senderId: 'user1', senderName: 'Alice Wonderland', text: 'Hi Charlie! Yes, it should be. Let me double check the dates.', timestamp: subDays(new Date(), 1).toISOString() },
{ id: 'm1-3', senderId: 'user4', senderName: 'Charlie Brown', text: 'Great, thanks!', timestamp: new Date().toISOString() },
],
dataAiHint: mockToys[0]?.category.toLowerCase(),
},
{
id: 'req2',
toy: mockToys[3], // Owned by user1 (Alice)
requesterName: 'Diana Prince',
requesterId: 'user5',
requestedDates: 'September 1, 2024 - September 5, 2024',
status: 'approved',
messages: [
{ id: 'm2-1', senderId: 'user5', senderName: 'Diana Prince', text: 'Hello, I would like to rent the Teddy Bear for the first week of September.', timestamp: subDays(new Date(), 3).toISOString() },
{ id: 'm2-2', senderId: 'user1', senderName: 'Alice Wonderland', text: 'Hi Diana, that works! I\'ve approved your request.', timestamp: subDays(new Date(), 2).toISOString() },
],
dataAiHint: mockToys[3]?.category.toLowerCase(),
},
{
id: 'req3',
toy: mockToys[0], // Owned by user1 (Alice)
requesterName: 'Edward Nigma',
requesterId: 'user6',
requestedDates: 'July 20, 2024 - July 22, 2024',
status: 'declined',
message: 'Looking for a weekend rental.',
// No follow-up messages for this one
dataAiHint: mockToys[0]?.category.toLowerCase(),
},
{
id: 'req4', // User1 is the requester here
toy: mockToys[1], // Owned by user2 (Bob)
requesterName: 'Alice Wonderland',
requesterId: 'user1',
requestedDates: 'October 5, 2024 - October 10, 2024',
status: 'pending',
message: 'Hi Bob, I\'d like to rent your RC Car for my nephew.',
messages: [
{ id: 'm4-1', senderId: 'user1', senderName: 'Alice Wonderland', text: 'Hi Bob, is the RC car available in early October?', timestamp: new Date().toISOString() },
],
dataAiHint: mockToys[1]?.category.toLowerCase(),
}
];
mockToys.forEach(toy => { mockToys.forEach(toy => {
if (!toy.dataAiHint) { if (!toy.dataAiHint) {
toy.dataAiHint = toy.category.toLowerCase().split(' ')[0]; toy.dataAiHint = toy.category.toLowerCase().split(' ')[0];

View File

@ -56,6 +56,7 @@ export default {
'dashboard.sidebar.my_rentals': 'My Rentals', 'dashboard.sidebar.my_rentals': 'My Rentals',
'dashboard.sidebar.rental_requests': 'Rental Requests', 'dashboard.sidebar.rental_requests': 'Rental Requests',
'dashboard.sidebar.rental_history': 'Rental History', 'dashboard.sidebar.rental_history': 'Rental History',
'dashboard.sidebar.messages': 'Messages',
'dashboard.sidebar.account': 'Account', 'dashboard.sidebar.account': 'Account',
'dashboard.sidebar.profile_settings': 'Profile Settings', 'dashboard.sidebar.profile_settings': 'Profile Settings',
'dashboard.sidebar.logout': 'Logout', 'dashboard.sidebar.logout': 'Logout',
@ -249,4 +250,14 @@ export default {
'rental_history_card.view_toy_button': 'View Toy', 'rental_history_card.view_toy_button': 'View Toy',
'rental_history_card.status_completed': 'Completed', 'rental_history_card.status_completed': 'Completed',
'rental_history_card.status_returned': 'Returned', 'rental_history_card.status_returned': 'Returned',
'dashboard.messages.title': 'My Messages',
'dashboard.messages.description': 'View and reply to your rental conversations.',
'dashboard.messages.no_messages_title': 'No Messages Yet',
'dashboard.messages.no_messages_description': 'Conversations related to your toy rentals or requests will appear here.',
'dashboard.messages.conversation_with': 'Conversation with {name}',
'dashboard.messages.about_toy': 'About: {toyName}',
'dashboard.messages.last_message_from': 'Last: {senderName}: {text}',
'dashboard.messages.view_conversation_button': 'View Conversation',
'dashboard.messages.message_date_time': '{date} at {time}',
'dashboard.messages.go_find_toys': 'Find Toys to Rent',
} as const; } as const;

View File

@ -56,6 +56,7 @@ export default {
'dashboard.sidebar.my_rentals': '我的租借', 'dashboard.sidebar.my_rentals': '我的租借',
'dashboard.sidebar.rental_requests': '租借請求', 'dashboard.sidebar.rental_requests': '租借請求',
'dashboard.sidebar.rental_history': '租借歷史', 'dashboard.sidebar.rental_history': '租借歷史',
'dashboard.sidebar.messages': '訊息',
'dashboard.sidebar.account': '帳戶', 'dashboard.sidebar.account': '帳戶',
'dashboard.sidebar.profile_settings': '個人資料設定', 'dashboard.sidebar.profile_settings': '個人資料設定',
'dashboard.sidebar.logout': '登出', 'dashboard.sidebar.logout': '登出',
@ -249,4 +250,14 @@ export default {
'rental_history_card.view_toy_button': '查看玩具', 'rental_history_card.view_toy_button': '查看玩具',
'rental_history_card.status_completed': '已完成', 'rental_history_card.status_completed': '已完成',
'rental_history_card.status_returned': '已歸還', 'rental_history_card.status_returned': '已歸還',
'dashboard.messages.title': '我的訊息',
'dashboard.messages.description': '查看並回覆您的租借對話。',
'dashboard.messages.no_messages_title': '尚無訊息',
'dashboard.messages.no_messages_description': '與您的玩具租借或請求相關的對話將會顯示在此。',
'dashboard.messages.conversation_with': '與 {name} 的對話',
'dashboard.messages.about_toy': '關於玩具:{toyName}',
'dashboard.messages.last_message_from': '最新:{senderName}{text}',
'dashboard.messages.view_conversation_button': '查看對話',
'dashboard.messages.message_date_time': '{date} {time}',
'dashboard.messages.go_find_toys': '尋找可租借的玩具',
} as const; } as const;

View File

@ -1,4 +1,12 @@
export interface MessageEntry {
id: string;
senderId: string;
senderName: string; // For easier display without needing to look up user by ID constantly in UI
text: string;
timestamp: string; // ISO date string
}
export interface Toy { export interface Toy {
id: string; id: string;
name: string; name: string;
@ -37,3 +45,15 @@ export interface RentalHistoryEntry {
status: 'Completed' | 'Returned'; // Example statuses status: 'Completed' | 'Returned'; // Example statuses
dataAiHint?: string; dataAiHint?: string;
} }
export interface RentalRequest {
id: string;
toy: Toy;
requesterName: string;
requesterId: string;
requestedDates: string;
status: 'pending' | 'approved' | 'declined';
message?: string; // Initial request message
messages?: MessageEntry[]; // Conversation thread
dataAiHint?: string;
}