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,25 +1,37 @@
import React from "react";
import { Metadata } from "next";
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import ChatList from "@/components/dashboard/ChatList";
import Dashboard from "@/components/dashboard/dashboard";
export const metadata: Metadata = {
title: "Customer Chats",
description: "Manage conversations with your customers",
};
import { MessageCircle } from "lucide-react";
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 (
<Dashboard>
<div className="container mx-auto py-6 space-y-6">
<div className="space-y-6">
<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 className="grid grid-cols-1 gap-6">
<ChatList />
</div>
</div>
</Dashboard>
);
}

View File

@@ -2,11 +2,33 @@
import React, { useState, useEffect, useRef } from "react";
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 { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
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 { toast } from "sonner";
import { getCookie } from "@/lib/client-utils";
@@ -260,91 +282,127 @@ export default function ChatList() {
}
return (
<Card className="w-full">
<CardHeader>
<CardTitle className="flex justify-between items-center">
<span>Customer Chats</span>
<Button onClick={handleCreateChat}>New Chat</Button>
</CardTitle>
{vendorStores.length === 0 ? (
<div className="text-sm text-muted-foreground">
No store available. Please create a store first.
</div>
) : vendorStores.length === 1 ? (
<div className="text-sm font-medium">
{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"
<div className="space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<Select
value={selectedStore || "all"}
onValueChange={(value) => setSelectedStore(value)}
>
<SelectTrigger className="w-48">
<SelectValue placeholder="All Stores" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Stores</SelectItem>
{vendorStores.map((store) => (
<option key={store._id} value={store._id}>
<SelectItem key={store._id} value={store._id}>
{store.name}
</option>
</SelectItem>
))}
</select>
</div>
)}
</CardHeader>
<CardContent>
{chats.length === 0 ? (
<div className="text-center py-12 px-4">
<div className="mb-4 mx-auto w-16 h-16 rounded-full bg-muted flex items-center justify-center">
<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>
</svg>
</div>
<h3 className="text-lg font-medium">No chats available</h3>
<p className="text-muted-foreground mt-2 mb-4">
There are no customer conversations for this store yet.
</p>
</SelectContent>
</Select>
<Button
variant="default"
onClick={handleCreateChat}
className="mt-2"
variant="outline"
size="sm"
onClick={() => fetchChats()}
disabled={loading}
>
Start a new conversation
{loading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<RefreshCw className="h-4 w-4" />
)}
<span className="ml-2">Refresh</span>
</Button>
</div>
<Button onClick={handleCreateChat} size="sm">
<Plus className="h-4 w-4 mr-2" />
New Chat
</Button>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[200px]">Customer</TableHead>
<TableHead>Last Activity</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{loading ? (
<TableRow>
<TableCell colSpan={4} className="h-24 text-center">
<Loader2 className="h-6 w-6 animate-spin mx-auto" />
</TableCell>
</TableRow>
) : chats.length === 0 ? (
<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>
</TableCell>
</TableRow>
) : (
<div className="space-y-4">
{chats.map((chat) => (
<div
chats.map((chat) => (
<TableRow
key={chat._id}
className="flex items-center justify-between p-4 rounded-lg border cursor-pointer hover:bg-muted/50 transition-colors"
className="cursor-pointer hover:bg-muted/50"
onClick={() => handleChatClick(chat._id)}
>
<div className="flex items-center space-x-4">
<TableCell>
<div className="flex items-center space-x-3">
<Avatar>
<AvatarFallback>
{chat.buyerId.slice(0, 2).toUpperCase()}
{chat.buyerId?.slice(0, 2).toUpperCase() || 'CU'}
</AvatarFallback>
</Avatar>
<div>
<h4 className="font-medium">Customer {chat.buyerId.slice(-4)}</h4>
<p className="text-sm text-muted-foreground">
<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 })}
</p>
</div>
</div>
{unreadCounts.chatCounts[chat._id] > 0 && (
<Badge variant="destructive">
{unreadCounts.chatCounts[chat._id]} unread
</TableCell>
<TableCell>
{unreadCounts.chatCounts[chat._id] > 0 ? (
<Badge variant="destructive" className="ml-1">
{unreadCounts.chatCounts[chat._id]} new
</Badge>
) : (
<Badge variant="outline">Read</Badge>
)}
</div>
))}
</div>
</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
handleChatClick(chat._id);
}}
>
View
</Button>
</TableCell>
</TableRow>
))
)}
</CardContent>
</Card>
</TableBody>
</Table>
</div>
</div>
);
}