content manager api integrated
This commit is contained in:
477
src/components/pages/ViewFAQ.tsx
Normal file
477
src/components/pages/ViewFAQ.tsx
Normal file
@@ -0,0 +1,477 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user