fix inconsistency

This commit is contained in:
NotII
2025-03-08 05:18:49 +00:00
parent d708098bd9
commit 5fb2575922
2 changed files with 165 additions and 95 deletions

View File

@@ -1,24 +1,36 @@
import React from "react"; "use client";
import { Metadata } from "next";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import ChatList from "@/components/dashboard/ChatList"; import ChatList from "@/components/dashboard/ChatList";
import Dashboard from "@/components/dashboard/dashboard"; import Dashboard from "@/components/dashboard/dashboard";
import { MessageCircle } from "lucide-react";
export const metadata: Metadata = {
title: "Customer Chats",
description: "Manage conversations with your customers",
};
export default function ChatsPage() { export default function ChatsPage() {
const router = useRouter();
useEffect(() => {
const authToken = document.cookie
.split("; ")
.find((row) => row.startsWith("Authorization="))
?.split("=")[1];
if (!authToken) {
router.push("/login");
}
}, [router]);
return ( return (
<Dashboard> <Dashboard>
<div className="container mx-auto py-6 space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h1 className="text-3xl font-bold tracking-tight">Customer Chats</h1> <h1 className="text-2xl font-semibold text-gray-900 dark:text-white flex items-center">
<MessageCircle className="mr-2 h-6 w-6" />
Customer Chats
</h1>
</div> </div>
<div className="grid grid-cols-1 gap-6"> <ChatList />
<ChatList />
</div>
</div> </div>
</Dashboard> </Dashboard>
); );

View File

@@ -2,11 +2,33 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { formatDistanceToNow } from "date-fns"; import { formatDistanceToNow } from "date-fns";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Plus,
MessageCircle,
Loader2,
RefreshCw,
ChevronLeft,
ChevronRight
} from "lucide-react";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { getCookie } from "@/lib/client-utils"; import { getCookie } from "@/lib/client-utils";
@@ -260,91 +282,127 @@ export default function ChatList() {
} }
return ( return (
<Card className="w-full"> <div className="space-y-4">
<CardHeader> <div className="flex justify-between items-center">
<CardTitle className="flex justify-between items-center"> <div className="flex items-center space-x-4">
<span>Customer Chats</span> <Select
<Button onClick={handleCreateChat}>New Chat</Button> value={selectedStore || "all"}
</CardTitle> onValueChange={(value) => setSelectedStore(value)}
{vendorStores.length === 0 ? ( >
<div className="text-sm text-muted-foreground"> <SelectTrigger className="w-48">
No store available. Please create a store first. <SelectValue placeholder="All Stores" />
</div> </SelectTrigger>
) : vendorStores.length === 1 ? ( <SelectContent>
<div className="text-sm font-medium"> <SelectItem value="all">All Stores</SelectItem>
{vendorStores[0].name}
</div>
) : (
<div className="flex items-center space-x-2">
<label htmlFor="store-select" className="text-sm font-medium">
Store:
</label>
<select
id="store-select"
value={selectedStore}
onChange={handleStoreChange}
className="rounded-md border border-input bg-background px-3 py-2 text-sm"
>
{vendorStores.map((store) => ( {vendorStores.map((store) => (
<option key={store._id} value={store._id}> <SelectItem key={store._id} value={store._id}>
{store.name} {store.name}
</option> </SelectItem>
))} ))}
</select> </SelectContent>
</div> </Select>
)}
</CardHeader> <Button
<CardContent> variant="outline"
{chats.length === 0 ? ( size="sm"
<div className="text-center py-12 px-4"> onClick={() => fetchChats()}
<div className="mb-4 mx-auto w-16 h-16 rounded-full bg-muted flex items-center justify-center"> disabled={loading}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-muted-foreground"> >
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path> {loading ? (
</svg> <Loader2 className="h-4 w-4 animate-spin" />
</div> ) : (
<h3 className="text-lg font-medium">No chats available</h3> <RefreshCw className="h-4 w-4" />
<p className="text-muted-foreground mt-2 mb-4"> )}
There are no customer conversations for this store yet. <span className="ml-2">Refresh</span>
</p> </Button>
<Button </div>
variant="default"
onClick={handleCreateChat} <Button onClick={handleCreateChat} size="sm">
className="mt-2" <Plus className="h-4 w-4 mr-2" />
> New Chat
Start a new conversation </Button>
</Button> </div>
</div>
) : ( <div className="rounded-md border">
<div className="space-y-4"> <Table>
{chats.map((chat) => ( <TableHeader>
<div <TableRow>
key={chat._id} <TableHead className="w-[200px]">Customer</TableHead>
className="flex items-center justify-between p-4 rounded-lg border cursor-pointer hover:bg-muted/50 transition-colors" <TableHead>Last Activity</TableHead>
onClick={() => handleChatClick(chat._id)} <TableHead>Status</TableHead>
> <TableHead className="text-right">Actions</TableHead>
<div className="flex items-center space-x-4"> </TableRow>
<Avatar> </TableHeader>
<AvatarFallback> <TableBody>
{chat.buyerId.slice(0, 2).toUpperCase()} {loading ? (
</AvatarFallback> <TableRow>
</Avatar> <TableCell colSpan={4} className="h-24 text-center">
<div> <Loader2 className="h-6 w-6 animate-spin mx-auto" />
<h4 className="font-medium">Customer {chat.buyerId.slice(-4)}</h4> </TableCell>
<p className="text-sm text-muted-foreground"> </TableRow>
{formatDistanceToNow(new Date(chat.lastUpdated), { addSuffix: true })} ) : chats.length === 0 ? (
</p> <TableRow>
<TableCell colSpan={4} className="h-24 text-center">
<div className="flex flex-col items-center justify-center">
<MessageCircle className="h-8 w-8 text-muted-foreground mb-2" />
<p className="text-muted-foreground">No chats found</p>
</div> </div>
</div> </TableCell>
{unreadCounts.chatCounts[chat._id] > 0 && ( </TableRow>
<Badge variant="destructive"> ) : (
{unreadCounts.chatCounts[chat._id]} unread chats.map((chat) => (
</Badge> <TableRow
)} key={chat._id}
</div> className="cursor-pointer hover:bg-muted/50"
))} onClick={() => handleChatClick(chat._id)}
</div> >
)} <TableCell>
</CardContent> <div className="flex items-center space-x-3">
</Card> <Avatar>
<AvatarFallback>
{chat.buyerId?.slice(0, 2).toUpperCase() || 'CU'}
</AvatarFallback>
</Avatar>
<div>
<div className="font-medium">Customer {chat.buyerId.slice(0, 4)}</div>
{chat.orderId && (
<div className="text-xs text-muted-foreground">
Order #{chat.orderId}
</div>
)}
</div>
</div>
</TableCell>
<TableCell>
{formatDistanceToNow(new Date(chat.lastUpdated), { addSuffix: true })}
</TableCell>
<TableCell>
{unreadCounts.chatCounts[chat._id] > 0 ? (
<Badge variant="destructive" className="ml-1">
{unreadCounts.chatCounts[chat._id]} new
</Badge>
) : (
<Badge variant="outline">Read</Badge>
)}
</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
handleChatClick(chat._id);
}}
>
View
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
</div>
); );
} }