477 lines
17 KiB
TypeScript
477 lines
17 KiB
TypeScript
import React from 'react';
|
|
import { AuthenticatedLayout } from '../layout/AuthenticatedLayout';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
|
|
import { Button } from '../ui/button';
|
|
import { Badge } from '../ui/badge';
|
|
import { toast } from "sonner";
|
|
import { ArrowLeft, Edit, Calendar, User, Tag, Globe, Copy, Share2 } from 'lucide-react';
|
|
import { Route } from '../../types/routes';
|
|
import { useGetFAQByIdQuery } from '../../store/services/contentManager.service';
|
|
|
|
interface ViewFAQProps {
|
|
onNavigate: (route: Route) => void;
|
|
onLogout: () => void;
|
|
user: any;
|
|
faqId?: string;
|
|
}
|
|
|
|
export function ViewFAQ({
|
|
onNavigate,
|
|
onLogout,
|
|
user,
|
|
faqId
|
|
}: ViewFAQProps) {
|
|
console.log('🔍 ViewFAQ component rendered with ID:', faqId);
|
|
|
|
// Use the FAQ by ID query
|
|
const { data: faq, isLoading, error } = useGetFAQByIdQuery(faqId!, {
|
|
skip: !faqId,
|
|
});
|
|
|
|
console.log('📊 FAQ Query Result:', { data: faq, isLoading, error });
|
|
|
|
const handleEdit = () => {
|
|
if (faq) {
|
|
onNavigate(`/content/faqs/edit/${faq.id}`);
|
|
}
|
|
};
|
|
|
|
const handleCopyToClipboard = async () => {
|
|
if (faq) {
|
|
const text = `Q: ${faq.question}\nA: ${faq.answer}`;
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
toast.success('FAQ copied to clipboard');
|
|
} catch (err) {
|
|
toast.error('Failed to copy to clipboard');
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleShare = async () => {
|
|
if (faq) {
|
|
const shareData = {
|
|
title: faq.question,
|
|
text: `Q: ${faq.question}\nA: ${faq.answer}`,
|
|
url: window.location.href,
|
|
};
|
|
|
|
if (navigator.share) {
|
|
try {
|
|
await navigator.share(shareData);
|
|
} catch (err) {
|
|
console.log('Error sharing:', err);
|
|
}
|
|
} else {
|
|
// Fallback to clipboard
|
|
handleCopyToClipboard();
|
|
}
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateString: string) => {
|
|
return new Date(dateString).toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
};
|
|
|
|
// Handle loading state
|
|
if (isLoading) {
|
|
return (
|
|
<AuthenticatedLayout
|
|
user={user}
|
|
onLogout={onLogout}
|
|
currentPath={`/content/faqs/view/${faqId}`} // ✅ Changed id to faqId
|
|
>
|
|
<div className="space-y-6 p-[0px] mt-[20px] mr-[20px] mb-[0px] ml-[20px]">
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => onNavigate('/content')}
|
|
className="min-h-[44px]"
|
|
disabled
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Back to Content
|
|
</Button>
|
|
<div className="flex-1">
|
|
<div className="h-8 bg-muted rounded w-1/3 animate-pulse"></div>
|
|
<div className="h-4 bg-muted rounded w-1/2 mt-2 animate-pulse"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div className="lg:col-span-2 space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="h-6 bg-muted rounded w-1/4 animate-pulse"></div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="h-20 bg-muted rounded animate-pulse"></div>
|
|
<div className="h-32 bg-muted rounded animate-pulse"></div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="h-6 bg-muted rounded w-1/3 animate-pulse"></div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{[...Array(4)].map((_, i) => (
|
|
<div key={i} className="flex justify-between">
|
|
<div className="h-4 bg-muted rounded w-1/3 animate-pulse"></div>
|
|
<div className="h-4 bg-muted rounded w-1/4 animate-pulse"></div>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
}
|
|
|
|
// Handle error state
|
|
if (error) {
|
|
return (
|
|
<AuthenticatedLayout
|
|
user={user}
|
|
onLogout={onLogout}
|
|
currentPath={`/content/faqs/view/${faqId}`} // ✅ Changed id to faqId
|
|
>
|
|
<div className="space-y-6 p-[0px] mt-[20px] mr-[20px] mb-[0px] ml-[20px]">
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => onNavigate('/content')}
|
|
className="min-h-[44px]"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Back to Content
|
|
</Button>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardContent className="py-12">
|
|
<div className="text-center">
|
|
<div className="text-destructive text-lg font-semibold mb-2">
|
|
Error Loading FAQ
|
|
</div>
|
|
<p className="text-muted-foreground mb-4">
|
|
{error && 'status' in error
|
|
? `Error ${error.status}: Failed to load FAQ`
|
|
: 'Failed to load FAQ. Please try again.'}
|
|
</p>
|
|
<Button
|
|
onClick={() => window.location.reload()}
|
|
variant="outline"
|
|
>
|
|
Retry
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
}
|
|
|
|
// Handle FAQ not found
|
|
if (!faq) {
|
|
return (
|
|
<AuthenticatedLayout
|
|
user={user}
|
|
onLogout={onLogout}
|
|
currentPath={`/content/faqs/view/${faqId}`} // ✅ Changed id to faqId
|
|
>
|
|
<div className="space-y-6 p-[0px] mt-[20px] mr-[20px] mb-[0px] ml-[20px]">
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => onNavigate('/content')}
|
|
className="min-h-[44px]"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Back to Content
|
|
</Button>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardContent className="py-12">
|
|
<div className="text-center">
|
|
<div className="text-lg font-semibold mb-2">
|
|
FAQ Not Found
|
|
</div>
|
|
<p className="text-muted-foreground mb-4">
|
|
The requested FAQ could not be found.
|
|
</p>
|
|
<Button
|
|
onClick={() => onNavigate('/content')}
|
|
variant="outline"
|
|
>
|
|
Back to Content
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AuthenticatedLayout
|
|
user={user}
|
|
onLogout={onLogout}
|
|
currentPath={`/content/faqs/view/${faqId}`} // ✅ Changed id to faqId
|
|
>
|
|
<div className="space-y-6 p-[0px] mt-[20px] mr-[20px] mb-[0px] ml-[20px]">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => onNavigate('/content')}
|
|
className="min-h-[44px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Back to Content
|
|
</Button>
|
|
<div>
|
|
<h1>View FAQ</h1>
|
|
<p className="text-muted-foreground mt-1">
|
|
Detailed view of the frequently asked question
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-2">
|
|
<Button
|
|
onClick={handleCopyToClipboard}
|
|
variant="outline"
|
|
size="sm"
|
|
className="min-h-[36px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
>
|
|
<Copy className="h-4 w-4 mr-2" />
|
|
Copy
|
|
</Button>
|
|
<Button
|
|
onClick={handleShare}
|
|
variant="outline"
|
|
size="sm"
|
|
className="min-h-[36px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
>
|
|
<Share2 className="h-4 w-4 mr-2" />
|
|
Share
|
|
</Button>
|
|
<Button
|
|
onClick={handleEdit}
|
|
className="min-h-[36px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
style={{ backgroundColor: "var(--color-brand-primary)" }}
|
|
>
|
|
<Edit className="h-4 w-4 mr-2" />
|
|
Edit FAQ
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Main Content */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Question Card */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<div className="w-2 h-6 bg-blue-500 rounded-full"></div>
|
|
Question
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="prose prose-sm max-w-none">
|
|
<p className="text-lg font-medium text-foreground leading-relaxed">
|
|
{faq.question}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Answer Card */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<div className="w-2 h-6 bg-green-500 rounded-full"></div>
|
|
Answer
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="prose prose-sm max-w-none">
|
|
<p className="text-foreground leading-relaxed whitespace-pre-wrap">
|
|
{faq.answer}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Tags Card */}
|
|
{(faq.tags.length > 0 || faq.globalTag.length > 0) && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Tags & Categories</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{faq.tags.length > 0 && (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2 text-sm font-medium">
|
|
<Tag className="h-4 w-4" />
|
|
Tags
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{faq.tags.map((tag, index) => (
|
|
<Badge key={index} variant="secondary" className="text-sm">
|
|
{tag}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{faq.globalTag.length > 0 && (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2 text-sm font-medium">
|
|
<Globe className="h-4 w-4" />
|
|
Global Tags
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{faq.globalTag.map((tag, index) => (
|
|
<Badge key={index} variant="default" className="text-sm">
|
|
{tag}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
|
|
{/* Sidebar */}
|
|
<div className="space-y-6">
|
|
{/* FAQ Details */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>FAQ Details</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between items-center">
|
|
<span className="text-sm font-medium text-muted-foreground">Category</span>
|
|
<Badge variant="outline">
|
|
{faq.category || 'Uncategorized'}
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<span className="text-sm font-medium text-muted-foreground">FAQ ID</span>
|
|
<code className="text-xs bg-muted px-2 py-1 rounded">
|
|
{faq.id}
|
|
</code>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-start">
|
|
<span className="text-sm font-medium text-muted-foreground flex items-center gap-1">
|
|
<Calendar className="h-3 w-3" />
|
|
Created
|
|
</span>
|
|
<span className="text-sm text-right">
|
|
{formatDate(faq.createdAt)}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-start">
|
|
<span className="text-sm font-medium text-muted-foreground flex items-center gap-1">
|
|
<Calendar className="h-3 w-3" />
|
|
Last Updated
|
|
</span>
|
|
<span className="text-sm text-right">
|
|
{formatDate(faq.updatedAt)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Quick Actions */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Quick Actions</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<Button
|
|
onClick={handleEdit}
|
|
className="w-full min-h-[44px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
style={{ backgroundColor: "var(--color-brand-primary)" }}
|
|
>
|
|
<Edit className="h-4 w-4 mr-2" />
|
|
Edit FAQ
|
|
</Button>
|
|
|
|
<Button
|
|
onClick={handleCopyToClipboard}
|
|
variant="outline"
|
|
className="w-full min-h-[44px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
>
|
|
<Copy className="h-4 w-4 mr-2" />
|
|
Copy Content
|
|
</Button>
|
|
|
|
<Button
|
|
onClick={handleShare}
|
|
variant="outline"
|
|
className="w-full min-h-[44px] focus-visible:ring-2 focus-visible:ring-[var(--color-brand-primary)] focus-visible:ring-opacity-50"
|
|
>
|
|
<Share2 className="h-4 w-4 mr-2" />
|
|
Share FAQ
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Statistics */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Content Info</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div className="flex justify-between">
|
|
<span className="text-sm text-muted-foreground">Question Length</span>
|
|
<span className="font-medium text-sm">{faq.question.length} chars</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-sm text-muted-foreground">Answer Length</span>
|
|
<span className="font-medium text-sm">{faq.answer.length} chars</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-sm text-muted-foreground">Total Tags</span>
|
|
<span className="font-medium text-sm">{faq.tags.length + faq.globalTag.length}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-sm text-muted-foreground">Status</span>
|
|
<Badge variant="default" className="text-sm">
|
|
Published
|
|
</Badge>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
} |