blog generated module added
This commit is contained in:
parent
b79df895c8
commit
1e2892007e
356
app/(defaults)/blog-generator/page.tsx
Normal file
356
app/(defaults)/blog-generator/page.tsx
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
'use client';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import IconArrowWaveLeftUp from '@/components/icon/icon-arrow-wave-left-up';
|
||||||
|
import IconRefresh from '@/components/icon/icon-refresh';
|
||||||
|
import IconCopy from '@/components/icon/icon-copy';
|
||||||
|
import IconDownload from '@/components/icon/icon-download';
|
||||||
|
import Loading from '@/components/layouts/loading';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
|
const BlogGenerator = () => {
|
||||||
|
const [url, setUrl] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [blogData, setBlogData] = useState<any>(null);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const [recentBlogs, setRecentBlogs] = useState<any[]>([]);
|
||||||
|
const [selectedEngine, setEngine] = useState('seomagnifier');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchRecentBlogs();
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
const fetchRecentBlogs = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`http://localhost:3020/api/blog/recent${url ? `?url=${encodeURIComponent(url)}` : ''}`);
|
||||||
|
if (data.ok) {
|
||||||
|
setRecentBlogs(data.data);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching recent blogs:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerate = async () => {
|
||||||
|
if (!url) return setError('Please enter a website URL');
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError('');
|
||||||
|
setBlogData(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post('http://localhost:3020/api/blog/generate', {
|
||||||
|
url,
|
||||||
|
engine: selectedEngine
|
||||||
|
});
|
||||||
|
if (data.ok) {
|
||||||
|
setBlogData(data.data);
|
||||||
|
fetchRecentBlogs();
|
||||||
|
} else {
|
||||||
|
setError('Failed to generate content');
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(err.response?.data?.error || err.message || 'Something went wrong');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyToClipboard = (text: string) => {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
alert('Copied to clipboard!');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pb-10 lg:flex gap-6 max-w-[1600px] mx-auto">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{/* Header Section */}
|
||||||
|
<div className="relative rounded-t-md bg-primary-light bg-[url('/assets/images/knowledge/pattern.png')] bg-contain bg-left-top bg-no-repeat px-5 py-10 dark:bg-black md:px-10">
|
||||||
|
<div className="relative z-[1]">
|
||||||
|
<div className="flex flex-col items-center justify-center sm:-ms-32 sm:flex-row xl:-ms-60">
|
||||||
|
<div className="mb-2 flex gap-1 text-end text-base leading-5 sm:flex-col xl:text-xl">
|
||||||
|
<span>One Click</span>
|
||||||
|
<span>Expert Content</span>
|
||||||
|
</div>
|
||||||
|
<div className="me-4 ms-2 hidden text-[#0E1726] rtl:rotate-y-180 dark:text-white sm:block">
|
||||||
|
<IconArrowWaveLeftUp className="w-16 xl:w-28" />
|
||||||
|
</div>
|
||||||
|
<div className="mb-2 text-center text-2xl font-bold dark:text-white md:text-5xl">AI Blog Generator</div>
|
||||||
|
</div>
|
||||||
|
<p className="mb-9 text-center text-base font-semibold">Transform any website URL into an SEO-optimized, human-readable blog post in seconds.</p>
|
||||||
|
|
||||||
|
<div className="relative mx-auto max-w-[700px] space-y-4">
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
|
placeholder="Enter Website URL (e.g., https://example.com)"
|
||||||
|
className="form-input py-4 ltr:pr-[120px] rtl:pl-[120px] shadow-lg rounded-full border-2 border-primary/20 focus:border-primary transition-all text-lg"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleGenerate}
|
||||||
|
disabled={loading}
|
||||||
|
className={`btn btn-primary absolute top-1.5 ltr:right-1.5 rtl:left-1.5 rounded-full py-2.5 px-6 shadow-none flex items-center gap-2 ${loading ? 'bg-gray-400 opacity-70' : 'bg-primary'}`}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<>
|
||||||
|
<span className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full"></span>
|
||||||
|
Analyzing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Generate Blog'
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-4 animate-fade-in">
|
||||||
|
<span className="text-xs font-bold uppercase text-gray-400 tracking-wider">AI Engine:</span>
|
||||||
|
<div className="flex bg-gray-100 dark:bg-dark p-1 rounded-xl border border-gray-200 dark:border-gray-800">
|
||||||
|
{[
|
||||||
|
{ id: 'seomagnifier', name: 'SEO Magnifier', icon: '🆓', sub: 'Unlimited' },
|
||||||
|
{ id: 'ollama', name: 'Ollama', icon: '🏠', sub: 'Local' },
|
||||||
|
{ id: 'openai', name: 'OpenAI', icon: '💎', sub: 'Premium' },
|
||||||
|
].map((engine) => (
|
||||||
|
<button
|
||||||
|
key={engine.id}
|
||||||
|
onClick={() => setEngine(engine.id)}
|
||||||
|
className={`flex flex-col items-center px-4 py-2 rounded-lg transition-all ${selectedEngine === engine.id
|
||||||
|
? 'bg-white dark:bg-gray-700 shadow-md text-primary scale-105'
|
||||||
|
: 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-lg">{engine.icon}</span>
|
||||||
|
<span className="text-[10px] font-bold uppercase">{engine.name}</span>
|
||||||
|
<span className="text-[8px] opacity-70">{engine.sub}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{error && <p className="text-red-500 mt-4 text-center font-medium animate-bounce">{error}</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-5 md:px-10 mt-10">
|
||||||
|
{!blogData && !loading && (
|
||||||
|
<div className="flex flex-col items-center justify-center py-20 text-center opacity-40">
|
||||||
|
<div className="bg-gray-200 p-6 rounded-full mb-4">
|
||||||
|
<IconRefresh className="w-12 h-12" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xl font-medium">Enter a URL above to start the magic.</p>
|
||||||
|
<p className="text-sm">We'll crawl the site, analyze its SEO structure, and build a custom blog for you.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loading && (
|
||||||
|
<div className="flex flex-col items-center justify-center py-20">
|
||||||
|
<div className="loader ring-4 ring-primary border-4 border-transparent border-t-primary w-16 h-16 rounded-full animate-spin"></div>
|
||||||
|
<p className="mt-6 text-xl font-bold animate-pulse">Deep Auditing Website Structure...</p>
|
||||||
|
<p className="text-gray-500">Extracting semantic markers and entity relationships...</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{blogData && (
|
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-12 gap-6">
|
||||||
|
{/* Main Blog Content Section */}
|
||||||
|
<div className="xl:col-span-8 space-y-6">
|
||||||
|
<div className="panel shadow-xl border-t-4 border-primary">
|
||||||
|
<div className="flex items-center justify-between mb-6 pb-4 border-b">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-800 dark:text-white-light">Generated Blog Post</h2>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Link href={`/blog/${blogData._id}`} className="btn btn-primary btn-sm px-3 py-2 flex items-center gap-2">
|
||||||
|
View Full View
|
||||||
|
</Link>
|
||||||
|
<button onClick={() => copyToClipboard(blogData.content)} className="btn btn-outline-primary btn-sm px-3 py-2 flex items-center gap-2">
|
||||||
|
<IconCopy className="w-4 h-4" /> Copy Content
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="prose prose-blue dark:prose-invert max-w-none">
|
||||||
|
<div className="mb-8 p-4 bg-gray-50 dark:bg-dark rounded-lg border-s-4 border-primary italic text-gray-600 dark:text-gray-400">
|
||||||
|
<p className="font-bold text-primary mb-1">SEO Blueprint Info:</p>
|
||||||
|
<ul className="text-sm space-y-1">
|
||||||
|
<li><strong>Meta Title:</strong> {blogData.metaTitle}</li>
|
||||||
|
<li><strong>Meta Description:</strong> {blogData.metaDescription}</li>
|
||||||
|
<li><strong>Focus Keyword:</strong> {blogData.focusKeyword}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{blogData.generatedImageUrls?.[0] && (
|
||||||
|
<div className="mb-8 rounded-2xl overflow-hidden shadow-2xl relative group">
|
||||||
|
<img src={blogData.generatedImageUrls[0]} alt="Generated blog header" className="w-full h-[400px] object-cover transition-transform duration-500 group-hover:scale-105" />
|
||||||
|
<div className="absolute top-4 right-4 badge badge-primary bg-primary/80 backdrop-blur-md border-none">AI Generated</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<h1 className="text-4xl lg:text-5xl font-extrabold mb-10 leading-tight text-black dark:text-white">{blogData.title}</h1>
|
||||||
|
<div className="prose prose-lg dark:prose-invert max-w-none
|
||||||
|
prose-p:text-lg prose-p:leading-relaxed prose-p:text-gray-700 dark:prose-p:text-gray-300
|
||||||
|
prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:font-bold
|
||||||
|
prose-h3:text-2xl prose-h3:mt-8 prose-h3:font-bold
|
||||||
|
prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:py-4 prose-blockquote:px-8 prose-blockquote:rounded-r-2xl prose-blockquote:italic">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{blogData.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Image Prompts Section */}
|
||||||
|
<div className="panel shadow-lg">
|
||||||
|
<h3 className="text-xl font-bold mb-4 flex items-center gap-2">
|
||||||
|
<span className="p-2 bg-secondary/10 text-secondary rounded">
|
||||||
|
🎨
|
||||||
|
</span>
|
||||||
|
AI Image Prompts
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{blogData.imagePrompts.map((prompt: string, i: number) => (
|
||||||
|
<div key={i} className="p-4 bg-gray-50 dark:bg-dark rounded-xl border border-gray-200 dark:border-gray-800 relative group">
|
||||||
|
<p className="text-sm italic text-gray-700 dark:text-gray-300">"{prompt}"</p>
|
||||||
|
<button
|
||||||
|
onClick={() => copyToClipboard(prompt)}
|
||||||
|
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity bg-white dark:bg-gray-800 p-1 rounded shadow"
|
||||||
|
title="Copy prompt"
|
||||||
|
>
|
||||||
|
<IconCopy className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar Metrics/Analysis Section */}
|
||||||
|
<div className="xl:col-span-4 space-y-6">
|
||||||
|
<div className="panel shadow-lg bg-gradient-to-br from-primary/5 to-transparent">
|
||||||
|
<h3 className="text-xl font-bold mb-4">Content Quality</h3>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-2">
|
||||||
|
<span className="font-semibold">SEO Optimization</span>
|
||||||
|
<span className="text-primary font-bold">{blogData.seoScore}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-2.5">
|
||||||
|
<div className="bg-primary h-2.5 rounded-full shadow-[0_0_10px_rgba(67,97,238,0.5)] transition-all duration-1000" style={{ width: `${blogData.seoScore}%` }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-2">
|
||||||
|
<span className="font-semibold">AI Detection (Human Score)</span>
|
||||||
|
<span className="text-info font-bold">{blogData.aiDetectionScore || 92}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-2.5">
|
||||||
|
<div className="bg-info h-2.5 rounded-full shadow-[0_0_10px_rgba(0,186,211,0.5)] transition-all duration-1000" style={{ width: `${blogData.aiDetectionScore || 92}%` }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-2">
|
||||||
|
<span className="font-semibold">Human Readability</span>
|
||||||
|
<span className="text-secondary font-bold">{blogData.readabilityScore}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-2.5">
|
||||||
|
<div className="bg-secondary h-2.5 rounded-full shadow-[0_0_10px_rgba(128,94,255,0.5)] transition-all duration-1000" style={{ width: `${blogData.readabilityScore}%` }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-4 mt-2 border-t border-dashed border-gray-200 dark:border-gray-700">
|
||||||
|
<div className="flex justify-between items-center bg-warning/10 p-3 rounded-lg border border-warning/20">
|
||||||
|
<span className="font-black text-[10px] uppercase text-warning-dark tracking-widest">Focus Keyword</span>
|
||||||
|
<span className="text-xs font-bold text-warning-dark truncate max-w-[150px]">{blogData.focusKeyword || 'AI Optimized'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{blogData.seoDetails && (
|
||||||
|
<div className="mt-4 pt-4 border-t border-gray-100 dark:border-gray-800">
|
||||||
|
<h4 className="text-xs font-bold text-gray-500 uppercase mb-3">SEO Checklist</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{blogData.seoDetails.map((detail: string, i: number) => (
|
||||||
|
<li key={i} className="text-[11px] flex items-center gap-2">
|
||||||
|
<span className={`w-1.5 h-1.5 rounded-full ${detail.toLowerCase().includes('missing') ? 'bg-red-500' : 'bg-green-500'}`}></span>
|
||||||
|
<span className={detail.toLowerCase().includes('missing') ? 'text-red-500' : 'text-gray-600 dark:text-gray-400 font-medium'}>{detail}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="panel shadow-lg">
|
||||||
|
<h3 className="text-xl font-bold mb-4">Internal Link Strategy</h3>
|
||||||
|
<p className="text-sm text-gray-500 mb-4">We've identified the high-authority endpoints to link from this blog post:</p>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="p-3 bg-gray-50 dark:bg-dark rounded border-l-2 border-primary">
|
||||||
|
<p className="text-xs font-bold text-primary uppercase">Primary Anchor</p>
|
||||||
|
<p className="text-sm font-medium">Link to "Solutions" page</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 bg-gray-50 dark:bg-dark rounded border-l-2 border-secondary">
|
||||||
|
<p className="text-xs font-bold text-secondary uppercase">Secondary Anchor</p>
|
||||||
|
<p className="text-sm font-medium">Link to "Our Process" deep dive</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="panel bg-black text-white p-6 rounded-2xl shadow-2xl relative overflow-hidden group">
|
||||||
|
<div className="absolute -top-10 -right-10 w-40 h-40 bg-primary/20 rounded-full blur-3xl group-hover:scale-150 transition-all duration-700"></div>
|
||||||
|
<h4 className="text-2xl font-bold mb-4 relative z-[1]">Power Up Your Content</h4>
|
||||||
|
<p className="text-gray-400 mb-6 relative z-[1]">Upgrade to Pro to unlock unlimited crawls and 100% genuine Gemini-powered content.</p>
|
||||||
|
<button className="btn btn-primary w-full py-4 text-lg font-bold relative z-[1] shadow-[0_0_20px_rgba(67,97,238,0.3)]">
|
||||||
|
Upgrade Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recently Generated Blogs Sidebar */}
|
||||||
|
<div className="lg:w-[350px] shrink-0 space-y-6 mt-10 lg:mt-0 px-5 lg:px-0">
|
||||||
|
<div className="panel shadow-lg h-fit sticky top-24">
|
||||||
|
<div className="flex items-center justify-between mb-4 pb-2 border-b">
|
||||||
|
<h3 className="text-xl font-bold">Recent Blogs</h3>
|
||||||
|
<button type="button" onClick={fetchRecentBlogs} className="hover:rotate-180 transition-all duration-500 text-primary">
|
||||||
|
<IconRefresh className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{recentBlogs.length === 0 ? (
|
||||||
|
<p className="text-sm text-gray-500 text-center py-10">No blogs generated yet {url ? 'for this URL' : ''}.</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4 max-h-[70vh] overflow-y-auto pr-2 custom-scrollbar">
|
||||||
|
{recentBlogs.map((blog) => (
|
||||||
|
<div
|
||||||
|
key={blog._id}
|
||||||
|
className={`p-3 rounded-lg border cursor-pointer transition-all hover:border-primary group ${blogData?._id === blog._id ? 'bg-primary/5 border-primary' : 'bg-gray-50 dark:bg-dark border-transparent'}`}
|
||||||
|
onClick={() => setBlogData(blog)}
|
||||||
|
>
|
||||||
|
<h4 className="font-bold text-sm line-clamp-2 group-hover:text-primary transition-colors">{blog.title}</h4>
|
||||||
|
<div className="flex items-center justify-between mt-2">
|
||||||
|
<span className="text-[10px] text-gray-400">{new Date(blog.createdAt).toLocaleDateString()}</span>
|
||||||
|
<span className="text-[10px] px-2 py-0.5 bg-gray-200 dark:bg-gray-800 rounded-full font-medium">SEO: {blog.seoScore}%</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-gray-500 mt-1 truncate">{blog.url}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-6 pt-4 border-t">
|
||||||
|
<p className="text-[11px] text-gray-400 text-center">
|
||||||
|
History is synced to your account.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlogGenerator;
|
||||||
176
app/(defaults)/blog/[id]/page.tsx
Normal file
176
app/(defaults)/blog/[id]/page.tsx
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
'use client';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import axios from 'axios';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
const BlogPost = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const [blog, setBlog] = useState<any>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchBlog = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`http://localhost:3020/api/blog/${params.id}`);
|
||||||
|
if (response.data.ok) {
|
||||||
|
setBlog(response.data.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (params.id) fetchBlog();
|
||||||
|
}, [params.id]);
|
||||||
|
|
||||||
|
if (loading) return (
|
||||||
|
<div className="flex items-center justify-center min-h-[60vh]">
|
||||||
|
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!blog) return <div className="text-center py-20 text-2xl">Blog not found</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-[1000px] mx-auto px-5 py-10">
|
||||||
|
<Link href="/blog-generator" className="btn btn-outline-primary mb-8 inline-flex items-center gap-2">
|
||||||
|
← Back to Generator
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<article className="panel shadow-xl overflow-hidden border-none outline-none">
|
||||||
|
{/* Header Section */}
|
||||||
|
<div className="relative h-[400px] w-full overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={blog.generatedImageUrls?.[0] || 'https://images.unsplash.com/photo-1499750310107-5fef28a66643?auto=format&fit=crop&q=80&w=1500'}
|
||||||
|
alt={blog.title}
|
||||||
|
className="w-full h-full object-cover transition-transform duration-700 hover:scale-105"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent flex flex-col justify-end p-8 lg:p-12">
|
||||||
|
<div className="flex gap-2 mb-4">
|
||||||
|
<span className="badge badge-primary bg-primary/20 backdrop-blur-md text-primary-light border-none">Industry Insight</span>
|
||||||
|
<span className="badge badge-secondary bg-secondary/20 backdrop-blur-md text-secondary-light border-none">AI Generated</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-3xl lg:text-5xl font-bold text-white leading-tight">
|
||||||
|
{blog.title}
|
||||||
|
</h1>
|
||||||
|
<div className="flex items-center gap-6 mt-6 text-gray-300 text-sm">
|
||||||
|
<span>{new Date(blog.createdAt).toLocaleDateString()}</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{blog.wordCount || 1800}+ Words</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<span className="w-2 h-2 rounded-full bg-green-500"></span>
|
||||||
|
SEO Score: {blog.seoScore}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Performance Metrics Bar */}
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 p-6 bg-white dark:bg-black/20 border-b">
|
||||||
|
<div className="text-center p-4 rounded-xl bg-primary/5">
|
||||||
|
<p className="text-[10px] text-secondary-dark uppercase font-black mb-1 tracking-wider">SEO Score</p>
|
||||||
|
<p className="text-3xl font-black text-primary">{blog.seoScore}%</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-4 rounded-xl bg-info/5">
|
||||||
|
<p className="text-[10px] text-secondary-dark uppercase font-black mb-1 tracking-wider">Human Score</p>
|
||||||
|
<p className="text-3xl font-black text-info">{blog.aiDetectionScore || 92}%</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-4 rounded-xl bg-success/5">
|
||||||
|
<p className="text-[10px] text-secondary-dark uppercase font-black mb-1 tracking-wider">Readability</p>
|
||||||
|
<p className="text-3xl font-black text-success">{blog.readabilityScore || 85}%</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-4 rounded-xl bg-warning/5 border border-warning/20">
|
||||||
|
<p className="text-[10px] text-secondary-dark uppercase font-black mb-1 tracking-wider">Focus Keyword</p>
|
||||||
|
<p className="text-sm font-bold text-warning truncate px-2">{blog.focusKeyword || 'Optimized'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Section */}
|
||||||
|
<div className="p-8 lg:p-16 prose prose-slate prose-lg dark:prose-invert max-w-none
|
||||||
|
prose-headings:text-black dark:prose-headings:text-white
|
||||||
|
prose-h1:text-4xl lg:prose-h1:text-5xl prose-h1:mb-8
|
||||||
|
prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6
|
||||||
|
prose-h3:text-2xl prose-h3:mt-8
|
||||||
|
prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-p:leading-relaxed prose-p:text-lg
|
||||||
|
prose-li:text-gray-700 dark:prose-li:text-gray-300
|
||||||
|
prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:py-2 prose-blockquote:px-8 prose-blockquote:rounded-r-2xl prose-blockquote:italic prose-blockquote:text-lg">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{blog.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* SEO Analysis Detail */}
|
||||||
|
{blog.seoDetails && blog.seoDetails.length > 0 && (
|
||||||
|
<div className="px-8 lg:px-16 pb-8">
|
||||||
|
<div className="p-8 rounded-2xl bg-gray-50 dark:bg-white/5 border border-gray-100 dark:border-white/10 shadow-sm">
|
||||||
|
<h3 className="text-xl font-bold mb-6 flex items-center gap-3 text-primary">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||||
|
</svg>
|
||||||
|
SEO Quality Checklist
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{blog.seoDetails.map((detail: string, i: number) => (
|
||||||
|
<div key={i} className="flex items-start gap-3 p-3 rounded-lg bg-white dark:bg-black/20 border border-gray-100 dark:border-white/5 text-gray-700 dark:text-gray-300">
|
||||||
|
<div className={`mt-1 h-5 w-5 flex-shrink-0 flex items-center justify-center rounded-full ${detail.toLowerCase().includes('missing') ? 'bg-red-100 text-red-600' : 'bg-green-100 text-green-600'}`}>
|
||||||
|
{detail.toLowerCase().includes('missing') ? (
|
||||||
|
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" /></svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /></svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-medium">{detail}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* FAQ Section */}
|
||||||
|
{blog.faqs && blog.faqs.length > 0 && (
|
||||||
|
<div className="px-8 lg:px-16 pb-16">
|
||||||
|
<div className="p-8 rounded-3xl bg-primary/5 dark:bg-primary/10 border border-primary/10">
|
||||||
|
<h3 className="text-2xl font-black mb-8 text-black dark:text-white flex items-center gap-3">
|
||||||
|
<span className="p-2 bg-primary text-white rounded-lg">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
Frequently Asked Questions
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{blog.faqs.map((faq: any, i: number) => (
|
||||||
|
<div key={i} className="p-6 rounded-2xl bg-white dark:bg-black/40 border border-gray-100 dark:border-white/5 hover:border-primary/30 transition-all shadow-sm group">
|
||||||
|
<h4 className="text-lg font-bold mb-3 text-black dark:text-white group-hover:text-primary transition-colors">
|
||||||
|
{faq.question}
|
||||||
|
</h4>
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 leading-relaxed">
|
||||||
|
{faq.answer}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{/* Float CTA */}
|
||||||
|
<div className="mt-12 text-center p-12 rounded-3xl bg-gradient-to-r from-primary to-secondary text-white">
|
||||||
|
<h2 className="text-3xl font-bold mb-4">Want content like this for your site?</h2>
|
||||||
|
<p className="text-lg opacity-90 mb-8 max-w-xl mx-auto">Our AI-powered engine analyzes your target niche and builds expert topical authority in seconds.</p>
|
||||||
|
<Link href="/pricing" className="btn btn-white bg-white text-primary font-bold px-10 py-4 rounded-full text-lg hover:shadow-xl transition-all">
|
||||||
|
Upgrade to Pro Plan
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlogPost;
|
||||||
325
app/(defaults)/page-speed-test/page.tsx
Normal file
325
app/(defaults)/page-speed-test/page.tsx
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
'use client';
|
||||||
|
import IconArrowWaveLeftUp from '@/components/icon/icon-arrow-wave-left-up';
|
||||||
|
import ComponentsPagesFaqWithTabs from '@/components/pages/components-pages-faq-with-tabs';
|
||||||
|
import ComponentsPagesKnowledgeBaseVideoTutorial from '@/components/pages/knowledge-base/components-pages-knowledge-base-video-tutorial';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import React from 'react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import {
|
||||||
|
Treemap,
|
||||||
|
Tooltip as ReTooltip,
|
||||||
|
ResponsiveContainer
|
||||||
|
} from 'recharts';
|
||||||
|
|
||||||
|
|
||||||
|
const PageSpeedTest = () => {
|
||||||
|
|
||||||
|
const [url, setUrl] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [reports, setReports] = useState<any>(null);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
// --- Helper to parse estimated savings like "1.2 s" or "1200 ms" ---
|
||||||
|
function parseSavings(value: string): number {
|
||||||
|
if (!value) return 0;
|
||||||
|
const val = parseFloat(value);
|
||||||
|
if (value.toLowerCase().includes('ms')) return val;
|
||||||
|
if (value.toLowerCase().includes('s')) return val * 1000;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAudit = async () => {
|
||||||
|
if (!url) return setError('Enter a URL');
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError('');
|
||||||
|
setReports(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post('https://api.crawlerx.co/api/lighthouse/audit', { url });
|
||||||
|
|
||||||
|
// normalize the results
|
||||||
|
const normalizedResults: any = {};
|
||||||
|
['mobile', 'desktop'].forEach((tab) => {
|
||||||
|
const tabData = data.results?.[tab] || {};
|
||||||
|
normalizedResults[tab] = {
|
||||||
|
report: {
|
||||||
|
scores: tabData.scores || {},
|
||||||
|
metrics: tabData.metrics || {},
|
||||||
|
opportunities: Array.isArray(tabData.opportunities) ? tabData.opportunities : [],
|
||||||
|
diagnostics: tabData.diagnostics || {},
|
||||||
|
screenshot: tabData.screenshot || '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setReports(normalizedResults);
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(err.response?.data?.message || err.message || 'Error');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="relative rounded-t-md bg-primary-light bg-[url('/assets/images/knowledge/pattern.png')] bg-contain bg-left-top bg-no-repeat px-5 py-10 dark:bg-black md:px-10">
|
||||||
|
<div className="absolute -bottom-1 -end-6 hidden text-[#DBE7FF] rtl:rotate-y-180 dark:text-[#1B2E4B] lg:block xl:end-0">
|
||||||
|
<svg width="375" height="185" viewBox="0 0 375 185" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-72 max-w-xs xl:w-full">
|
||||||
|
<g clipPath="url(#clip0_1109_89938)">
|
||||||
|
<path
|
||||||
|
d="M215.023 181.044C212.702 181.042 210.477 180.122 208.836 178.487C207.196 176.851 206.274 174.633 206.274 172.321C206.274 170.008 207.196 167.79 208.836 166.155C210.477 164.519 212.702 163.599 215.023 163.598H345.07C344.415 162.401 343.45 161.403 342.275 160.707C341.099 160.01 339.757 159.643 338.39 159.642H79.8922C76.5197 159.645 73.2866 160.983 70.9034 163.36C68.5202 165.738 67.1817 168.961 67.1821 172.321C67.1838 175.68 68.523 178.902 70.9058 181.279C73.2885 183.656 76.5204 184.994 79.8922 185H338.39C339.757 184.999 341.099 184.631 342.275 183.935C343.45 183.239 344.415 182.24 345.069 181.044L215.023 181.044Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M345.242 168.405H221.598C221.409 168.404 221.228 168.329 221.094 168.195C220.96 168.062 220.885 167.881 220.885 167.693C220.885 167.504 220.96 167.323 221.094 167.19C221.228 167.056 221.409 166.981 221.598 166.98H345.242C345.431 166.981 345.613 167.056 345.746 167.19C345.88 167.323 345.955 167.504 345.955 167.693C345.955 167.881 345.88 168.062 345.746 168.195C345.613 168.329 345.431 168.404 345.242 168.405Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M345.242 173.033H221.598C221.409 173.033 221.227 172.958 221.093 172.824C220.959 172.691 220.884 172.51 220.884 172.321C220.884 172.132 220.959 171.951 221.093 171.817C221.227 171.684 221.409 171.609 221.598 171.609H345.242C345.432 171.609 345.614 171.684 345.748 171.817C345.882 171.951 345.957 172.132 345.957 172.321C345.957 172.51 345.882 172.691 345.748 172.824C345.614 172.958 345.432 173.033 345.242 173.033Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M345.242 177.661H221.598C221.409 177.661 221.228 177.586 221.094 177.452C220.96 177.319 220.885 177.138 220.885 176.949C220.885 176.761 220.96 176.58 221.094 176.446C221.228 176.313 221.409 176.238 221.598 176.237H345.242C345.431 176.238 345.613 176.313 345.746 176.446C345.88 176.58 345.955 176.761 345.955 176.949C345.955 177.138 345.88 177.319 345.746 177.452C345.613 177.586 345.431 177.661 345.242 177.661Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M181.432 156.477C179.111 156.476 176.885 155.556 175.245 153.92C173.604 152.285 172.683 150.067 172.683 147.754C172.683 145.442 173.604 143.224 175.245 141.588C176.885 139.953 179.111 139.033 181.432 139.031H311.478C310.824 137.835 309.859 136.836 308.683 136.14C307.508 135.444 306.166 135.076 304.799 135.075H46.3011C42.9286 135.079 39.6955 136.417 37.3123 138.794C34.929 141.171 33.5905 144.394 33.5908 147.754C33.5926 151.114 34.9318 154.335 37.3146 156.712C39.6974 159.089 42.9293 160.428 46.3011 160.433H304.799C306.166 160.432 307.508 160.065 308.683 159.368C309.859 158.672 310.824 157.674 311.478 156.477L181.432 156.477Z"
|
||||||
|
fill="#4361EE"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M311.651 143.838H188.007C187.818 143.837 187.637 143.762 187.503 143.629C187.37 143.495 187.294 143.314 187.294 143.126C187.294 142.937 187.37 142.756 187.503 142.623C187.637 142.489 187.818 142.414 188.007 142.414H311.651C311.841 142.414 312.023 142.489 312.157 142.622C312.291 142.756 312.366 142.937 312.366 143.126C312.366 143.314 312.291 143.496 312.157 143.629C312.023 143.763 311.841 143.838 311.651 143.838Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M311.651 148.466H188.007C187.818 148.466 187.636 148.391 187.502 148.258C187.368 148.124 187.292 147.943 187.292 147.754C187.292 147.565 187.368 147.384 187.502 147.251C187.636 147.117 187.818 147.042 188.007 147.042H311.651C311.841 147.042 312.022 147.117 312.156 147.251C312.291 147.384 312.366 147.565 312.366 147.754C312.366 147.943 312.291 148.124 312.156 148.258C312.022 148.391 311.841 148.466 311.651 148.466Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M311.651 153.095H188.007C187.818 153.095 187.637 153.019 187.503 152.886C187.37 152.752 187.294 152.572 187.294 152.383C187.294 152.194 187.37 152.014 187.503 151.88C187.637 151.747 187.818 151.671 188.007 151.671H311.651C311.745 151.671 311.838 151.689 311.925 151.725C312.012 151.76 312.091 151.813 312.158 151.879C312.224 151.945 312.277 152.024 312.313 152.11C312.349 152.197 312.368 152.289 312.368 152.383C312.368 152.477 312.349 152.569 312.313 152.656C312.277 152.742 312.224 152.821 312.158 152.887C312.091 152.953 312.012 153.006 311.925 153.041C311.838 153.077 311.745 153.095 311.651 153.095Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M147.841 131.091C145.52 131.089 143.295 130.17 141.654 128.534C140.013 126.898 139.092 124.681 139.092 122.368C139.092 120.056 140.013 117.838 141.654 116.202C143.295 114.567 145.52 113.647 147.841 113.645H277.887C277.233 112.449 276.268 111.45 275.093 110.754C273.917 110.058 272.575 109.69 271.208 109.689H12.7101C9.33759 109.693 6.10452 111.03 3.72128 113.408C1.33804 115.785 -0.000419607 119.008 9.86767e-08 122.368C0.00170636 125.728 1.34085 128.949 3.72363 131.326C6.10641 133.703 9.33824 135.041 12.7101 135.047H271.208C272.575 135.046 273.917 134.678 275.093 133.982C276.268 133.286 277.233 132.287 277.887 131.091L147.841 131.091Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M278.06 118.452H154.416C154.227 118.451 154.046 118.376 153.912 118.242C153.778 118.109 153.703 117.928 153.703 117.739C153.703 117.551 153.778 117.37 153.912 117.237C154.046 117.103 154.227 117.028 154.416 117.027H278.06C278.25 117.027 278.431 117.102 278.565 117.236C278.699 117.369 278.775 117.551 278.775 117.739C278.775 117.928 278.699 118.109 278.565 118.243C278.431 118.376 278.25 118.452 278.06 118.452Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M278.06 123.08H154.416C154.323 123.08 154.23 123.062 154.143 123.026C154.056 122.99 153.977 122.937 153.911 122.871C153.845 122.805 153.792 122.727 153.756 122.64C153.72 122.554 153.702 122.461 153.702 122.368C153.702 122.274 153.72 122.182 153.756 122.095C153.792 122.009 153.845 121.93 153.911 121.864C153.977 121.798 154.056 121.746 154.143 121.71C154.23 121.674 154.323 121.656 154.416 121.656H278.06C278.154 121.656 278.247 121.674 278.334 121.71C278.42 121.746 278.499 121.798 278.566 121.864C278.632 121.93 278.685 122.009 278.721 122.095C278.756 122.182 278.775 122.274 278.775 122.368C278.775 122.461 278.756 122.554 278.721 122.64C278.685 122.727 278.632 122.805 278.566 122.871C278.499 122.937 278.42 122.99 278.334 123.026C278.247 123.062 278.154 123.08 278.06 123.08Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M278.06 127.708H154.416C154.227 127.708 154.045 127.633 153.911 127.5C153.777 127.366 153.702 127.185 153.702 126.996C153.702 126.807 153.777 126.626 153.911 126.493C154.045 126.359 154.227 126.284 154.416 126.284H278.06C278.154 126.284 278.247 126.303 278.334 126.338C278.42 126.374 278.499 126.427 278.566 126.493C278.632 126.559 278.685 126.637 278.721 126.724C278.756 126.81 278.775 126.903 278.775 126.996C278.775 127.09 278.756 127.182 278.721 127.269C278.685 127.355 278.632 127.434 278.566 127.5C278.499 127.566 278.42 127.618 278.334 127.654C278.247 127.69 278.154 127.708 278.06 127.708Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M280.915 76.9348C280.237 76.9337 279.585 76.676 279.09 76.2137C278.595 75.7514 278.296 75.1191 278.251 74.4449L277.854 68.4086C277.808 67.7035 278.045 67.009 278.512 66.4778C278.98 65.9466 279.64 65.6222 280.348 65.576L327.814 62.479C328.562 62.4301 329.313 62.5286 330.023 62.7688C330.733 63.009 331.388 63.3861 331.952 63.8787C332.516 64.3713 332.977 64.9697 333.308 65.6398C333.64 66.3098 333.836 67.0383 333.885 67.7838C333.934 68.5292 333.835 69.277 333.594 69.9844C333.353 70.6918 332.974 71.345 332.48 71.9066C331.985 72.4682 331.385 72.9274 330.712 73.2577C330.04 73.5881 329.308 73.7833 328.56 73.8321L281.094 76.929C281.034 76.9328 280.975 76.9347 280.915 76.9348Z"
|
||||||
|
fill="#4361EE"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M290.275 77.713C289.583 77.7119 288.919 77.4442 288.421 76.9662C287.924 76.4881 287.631 75.8366 287.604 75.1482L287.266 66.1324C287.253 65.7828 287.309 65.434 287.431 65.106C287.553 64.778 287.739 64.4772 287.978 64.2208C288.217 63.9644 288.504 63.7573 288.823 63.6115C289.142 63.4657 289.487 63.3839 289.838 63.3709L328.871 61.9174C329.579 61.891 330.27 62.1462 330.789 62.6268C331.309 63.1074 331.616 63.7741 331.643 64.4801L331.981 73.496C331.994 73.8456 331.938 74.1944 331.816 74.5224C331.694 74.8504 331.508 75.1512 331.269 75.4076C331.03 75.664 330.743 75.8711 330.424 76.0169C330.104 76.1627 329.76 76.2445 329.409 76.2575L290.376 77.711C290.342 77.7124 290.308 77.713 290.275 77.713Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M336.015 160.823H329.943C329.234 160.822 328.555 160.541 328.054 160.041C327.552 159.542 327.27 158.865 327.27 158.159V107.741C327.27 107.035 327.552 106.358 328.054 105.858C328.555 105.359 329.234 105.078 329.943 105.077H336.015C336.724 105.078 337.404 105.359 337.905 105.858C338.406 106.358 338.688 107.035 338.689 107.741V158.159C338.688 158.865 338.406 159.542 337.905 160.041C337.404 160.541 336.724 160.822 336.015 160.823Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M309.066 134.173L303.873 131.037C303.268 130.67 302.833 130.079 302.664 129.393C302.495 128.707 302.606 127.982 302.973 127.378L329.203 84.2625C329.571 83.6589 330.165 83.2254 330.853 83.0572C331.542 82.889 332.269 82.9998 332.876 83.3652L338.068 86.5008C338.674 86.8676 339.109 87.4588 339.278 88.1448C339.446 88.8308 339.335 89.5554 338.969 90.1599L312.738 133.275C312.37 133.879 311.777 134.312 311.088 134.481C310.4 134.649 309.672 134.538 309.066 134.173Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M326.794 52.4612C338.38 52.4612 347.773 43.1029 347.773 31.5589C347.773 20.0148 338.38 10.6565 326.794 10.6565C315.207 10.6565 305.814 20.0148 305.814 31.5589C305.814 43.1029 315.207 52.4612 326.794 52.4612Z"
|
||||||
|
fill="#4361EE"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M315.438 38.6956C314.763 38.4746 314.143 38.1123 313.621 37.6329C313.274 37.2907 313.007 36.8771 312.838 36.4212C312.67 35.9654 312.603 35.4782 312.643 34.9939C312.665 34.6488 312.766 34.3135 312.94 34.0141C313.113 33.7147 313.354 33.4593 313.643 33.268C314.393 32.7885 315.397 32.7871 316.419 33.2357L316.38 25.0757L317.203 25.0718L317.248 34.6647L316.614 34.2674C315.879 33.8075 314.829 33.4838 314.088 33.958C313.904 34.0833 313.752 34.249 313.644 34.4423C313.535 34.6355 313.473 34.8511 313.462 35.0723C313.434 35.4331 313.484 35.7956 313.61 36.1351C313.735 36.4747 313.933 36.7833 314.189 37.04C315.097 37.9046 316.423 38.175 317.934 38.4166L317.804 39.2258C317.001 39.1199 316.209 38.9424 315.438 38.6956Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path d="M307.899 24.6635L307.791 25.4761L312.184 26.0541L312.292 25.2415L307.899 24.6635Z" fill="#2F2E41" />
|
||||||
|
<path d="M321.765 26.4873L321.657 27.2998L326.05 27.8778L326.158 27.0653L321.765 26.4873Z" fill="#2F2E41" />
|
||||||
|
<path
|
||||||
|
d="M344.312 121.268H308.934C308.007 121.267 307.119 120.9 306.463 120.247C305.808 119.593 305.439 118.708 305.438 117.784L312.844 61.1985C312.85 60.2791 313.221 59.3994 313.876 58.7516C314.531 58.1038 315.416 57.7405 316.339 57.7412H328.894C333.908 57.7469 338.716 59.7341 342.262 63.267C345.808 66.7999 347.803 71.5899 347.808 76.5861V117.784C347.807 118.708 347.438 119.593 346.783 120.247C346.127 120.9 345.239 121.267 344.312 121.268Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M374.749 98.9713C374.967 99.4372 375.046 99.9562 374.975 100.466C374.905 100.975 374.688 101.453 374.351 101.843L370.381 106.42C370.152 106.685 369.872 106.902 369.558 107.059C369.245 107.216 368.903 107.31 368.553 107.336C368.202 107.361 367.851 107.318 367.517 107.208C367.184 107.098 366.876 106.923 366.61 106.695L330.615 75.7111C330.048 75.2228 329.582 74.6279 329.246 73.9603C328.909 73.2928 328.707 72.5657 328.653 71.8205C328.598 71.0754 328.691 70.3268 328.927 69.6175C329.163 68.9083 329.536 68.2522 330.027 67.6869C330.517 67.1215 331.114 66.6579 331.784 66.3226C332.454 65.9872 333.184 65.7866 333.932 65.7323C334.68 65.678 335.431 65.7711 336.143 66.0061C336.855 66.2411 337.513 66.6136 338.081 67.1021L374.075 98.0854C374.36 98.33 374.59 98.6319 374.749 98.9713Z"
|
||||||
|
fill="#4361EE"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M366.804 88.7464C367.023 89.2124 367.101 89.7313 367.031 90.2408C366.96 90.7503 366.743 91.2286 366.406 91.6181L359.626 99.4367C359.162 99.9706 358.505 100.299 357.798 100.351C357.091 100.402 356.392 100.172 355.855 99.7109L326.298 74.2689C326.032 74.0402 325.815 73.7616 325.657 73.449C325.499 73.1364 325.405 72.7959 325.379 72.447C325.354 72.0981 325.398 71.7475 325.508 71.4154C325.618 71.0833 325.793 70.7761 326.023 70.5114L332.803 62.6929C333.032 62.4282 333.312 62.2111 333.626 62.054C333.939 61.897 334.281 61.803 334.631 61.7776C334.982 61.7521 335.333 61.7956 335.667 61.9056C336 62.0157 336.308 62.19 336.574 62.4187L366.131 87.8606C366.416 88.1052 366.645 88.4071 366.804 88.7464Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M327.186 19.1616C324.381 16.8493 320.632 19.0362 317.461 18.7258C314.426 18.4289 311.984 15.7684 311.319 12.9054C310.542 9.56531 312.225 6.1724 314.761 4.03273C317.537 1.68928 321.265 0.961046 324.818 1.31764C328.89 1.72635 332.641 3.54275 336.019 5.76177C339.277 7.83543 342.284 10.2778 344.979 13.0401C347.394 15.5981 349.457 18.6626 350.164 22.1529C350.806 25.3248 350.43 28.8604 348.593 31.5925C347.617 32.9935 346.296 34.1205 344.757 34.8647C343.153 35.6876 341.436 36.2867 339.884 37.2122C337.538 38.6116 335.286 41.4636 336.084 44.3721C336.256 45.0075 336.582 45.5912 337.033 46.0718C337.575 46.6488 338.517 45.8549 337.974 45.2763C337.019 44.2601 337.03 42.8806 337.505 41.6368C338.071 40.2346 339.098 39.0655 340.418 38.321C342.042 37.3544 343.844 36.7426 345.511 35.8587C347.107 35.0498 348.484 33.8686 349.523 32.4156C351.485 29.5881 352.018 25.9466 351.497 22.5971C350.934 18.9723 348.996 15.7055 346.594 12.9827C343.98 10.0194 340.791 7.52961 337.539 5.30018C334.05 2.90798 330.207 0.902431 325.984 0.230686C322.323 -0.351613 318.377 0.124257 315.224 2.16544C312.282 4.07076 310.059 7.29454 309.899 10.8528C309.849 12.4714 310.232 14.0742 311.009 15.4963C311.787 16.9184 312.931 18.1084 314.323 18.9438C315.751 19.7588 317.4 20.1066 319.036 19.9381C320.787 19.7932 322.531 19.2252 324.298 19.3403C325.097 19.3735 325.864 19.6653 326.481 20.1715C327.094 20.6767 327.794 19.6626 327.186 19.1616Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M302.517 128.106C302.524 128.06 302.533 128.014 302.543 127.968C302.616 127.625 302.757 127.301 302.957 127.013C303.157 126.725 303.412 126.48 303.708 126.291L308.81 123.012C309.406 122.63 310.13 122.499 310.823 122.648C311.516 122.797 312.121 123.214 312.506 123.807L328.245 148.119C328.63 148.713 328.762 149.434 328.612 150.125C328.462 150.815 328.044 151.419 327.448 151.802L322.345 155.08C321.749 155.463 321.025 155.594 320.332 155.445C319.639 155.296 319.034 154.879 318.649 154.286L302.91 129.974C302.55 129.421 302.41 128.756 302.517 128.106Z"
|
||||||
|
fill="#2F2E41"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_1109_89938">
|
||||||
|
<rect width="375" height="185" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="flex flex-col items-center justify-center sm:-ms-32 sm:flex-row xl:-ms-60">
|
||||||
|
<div className="mb-2 flex gap-1 text-end text-base leading-5 sm:flex-col xl:text-xl">
|
||||||
|
<span>It's free </span>
|
||||||
|
<span>For everyone</span>
|
||||||
|
</div>
|
||||||
|
<div className="me-4 ms-2 hidden text-[#0E1726] rtl:rotate-y-180 dark:text-white sm:block">
|
||||||
|
<IconArrowWaveLeftUp className="w-16 xl:w-28" />
|
||||||
|
</div>
|
||||||
|
<div className="mb-2 text-center text-2xl font-bold dark:text-white md:text-5xl">PageSpeed Audit</div>
|
||||||
|
</div>
|
||||||
|
<p className="mb-9 text-center text-base font-semibold">Optimize your website for faster load times, better SEO, and a seamless user experience.</p>
|
||||||
|
<form action="" method="" className="mb-6">
|
||||||
|
<div className="relative mx-auto max-w-[580px]">
|
||||||
|
<input type="text" value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)} placeholder="Enter a Web Page URL" className="form-input py-3 ltr:pr-[100px] rtl:pl-[100px]" />
|
||||||
|
<button type="button" onClick={handleAudit}
|
||||||
|
disabled={loading} className={`btn btn-primary absolute top-1 shadow-none ltr:right-1 rtl:left-1 ${loading ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700'
|
||||||
|
}`}>
|
||||||
|
{loading ? 'Auditing...' : 'Run Audit'}
|
||||||
|
</button>
|
||||||
|
{error && <p className="text-red-500 mt-2 text-center">{error}</p>}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-2 font-semibold text-[#2196F3] sm:gap-5">
|
||||||
|
<div className="whitespace-nowrap font-medium text-black dark:text-white">Popular topics :</div>
|
||||||
|
<div className="flex items-center justify-center gap-2 sm:gap-5">
|
||||||
|
<Link href="#" className="duration-300 hover:underline">
|
||||||
|
Core Web Vitals
|
||||||
|
</Link>
|
||||||
|
<Link href="#" className="duration-300 hover:underline">
|
||||||
|
SEO Optimization
|
||||||
|
</Link>
|
||||||
|
<Link href="#" className="duration-300 hover:underline">
|
||||||
|
Page Load Speed
|
||||||
|
</Link>
|
||||||
|
<Link href="#" className="duration-300 hover:underline">
|
||||||
|
Mobile Performance
|
||||||
|
</Link>
|
||||||
|
<Link href="#" className="duration-300 hover:underline">
|
||||||
|
Best Practices
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ComponentsPagesFaqWithTabs reports={reports} loading={loading}/>
|
||||||
|
|
||||||
|
{/* <div className="panel mt-10 text-center md:mt-20">
|
||||||
|
<h3 className="mb-2 text-xl font-bold dark:text-white md:text-2xl">Still need help?</h3>
|
||||||
|
<div className="text-lg font-medium text-white-dark">
|
||||||
|
Our specialists are always happy to help. Contact us during standard business hours or email us24/7 and we'll get back to you.
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 flex flex-col items-center justify-center gap-6 sm:flex-row">
|
||||||
|
<button type="button" className="btn btn-primary">
|
||||||
|
Contact Us
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn btn-primary">
|
||||||
|
Visit our community
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-10 flex flex-col-reverse items-center justify-between gap-5 rounded-md bg-gradient-to-tl from-[rgba(234,241,255,0.44)] to-[rgba(234,241,255,0.96)] px-6 py-2.5 dark:from-[rgba(14,23,38,0.44)] dark:to-[#0E1726] md:flex-row lg:mt-20 xl:px-16">
|
||||||
|
<div className="flex-1 py-3.5 text-center md:text-start">
|
||||||
|
<h3 className="mb-2 text-xl font-bold dark:text-white md:text-2xl">Didn’t find any solutions?</h3>
|
||||||
|
<div className="text-lg font-medium text-white-dark">Loaded with awesome features like documentation, knowledge base forum, domain transfer, affiliates etc.</div>
|
||||||
|
<div className="mt-8 flex justify-center md:justify-start lg:mt-16">
|
||||||
|
<button type="button" className="btn btn-primary">
|
||||||
|
Raise support tickets
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-52 max-w-xs lg:w-full">
|
||||||
|
<img src="/assets/images/knowledge/find-solution.svg" alt="find-solution" className="w-full object-cover rtl:rotate-y-180 dark:brightness-[2.59] dark:grayscale-[83%]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-10">
|
||||||
|
<h3 className="mb-6 text-xl font-bold md:text-3xl">Popular Topics</h3>
|
||||||
|
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
|
<div className="space-y-5 rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black">
|
||||||
|
<div className="max-h-56 overflow-hidden rounded-md">
|
||||||
|
<img src="/assets/images/knowledge/image-1.jpg" alt="..." className="w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<h5 className="text-xl dark:text-white">Excessive sugar is harmful</h5>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="me-4 overflow-hidden rounded-full bg-white-dark">
|
||||||
|
<img src="/assets/images/profile-1.jpeg" className="h-11 w-11 object-cover" alt="profile1" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="mb-1.5 font-semibold dark:text-white">Alma Clark</h4>
|
||||||
|
<p>06 May</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-5 rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black">
|
||||||
|
<div className="max-h-56 overflow-hidden rounded-md">
|
||||||
|
<img src="/assets/images/knowledge/image-2.jpg" alt="..." className="w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<h5 className="text-xl dark:text-white">Creative Photography</h5>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="me-4 overflow-hidden rounded-full bg-white-dark">
|
||||||
|
<img src="/assets/images/profile-2.jpeg" className="h-11 w-11 object-cover" alt="profile1" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="mb-1.5 font-semibold dark:text-white">Alma Clark</h4>
|
||||||
|
<p>06 May</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-5 rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black">
|
||||||
|
<div className="max-h-56 overflow-hidden rounded-md">
|
||||||
|
<img src="/assets/images/knowledge/image-3.jpg" alt="..." className="w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<h5 className="text-xl dark:text-white">Plan your next trip</h5>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="me-4 overflow-hidden rounded-full bg-white-dark">
|
||||||
|
<img src="/assets/images/profile-3.jpeg" className="h-11 w-11 object-cover" alt="profile1" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="mb-1.5 font-semibold dark:text-white">Alma Clark</h4>
|
||||||
|
<p>06 May</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-5 rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black">
|
||||||
|
<div className="max-h-56 overflow-hidden rounded-md">
|
||||||
|
<img src="/assets/images/knowledge/image-4.jpg" alt="..." className="w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<h5 className="text-xl dark:text-white">My latest Vlog</h5>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="me-4 overflow-hidden rounded-full bg-white-dark">
|
||||||
|
<img src="/assets/images/profile-4.jpeg" className="h-11 w-11 object-cover" alt="profile1" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="mb-1.5 font-semibold dark:text-white">Alma Clark</h4>
|
||||||
|
<p>06 May</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ComponentsPagesKnowledgeBaseVideoTutorial /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageSpeedTest;
|
||||||
@ -149,6 +149,14 @@ const Sidebar = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link href="/blog-generator" className="group">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<IconMenuTodo className="shrink-0 group-hover:!text-primary" />
|
||||||
|
<span className="text-black ltr:pl-3 rtl:pr-3 dark:text-[#506690] dark:group-hover:text-white-dark">Blog Generator</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
{/* <li className="nav-item">
|
{/* <li className="nav-item">
|
||||||
<Link href="/apps/mailbox" className="group">
|
<Link href="/apps/mailbox" className="group">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|||||||
@ -0,0 +1,146 @@
|
|||||||
|
'use client';
|
||||||
|
import IconFacebookCircle from '@/components/icon/icon-facebook-circle';
|
||||||
|
import IconGoogle from '@/components/icon/icon-google';
|
||||||
|
import IconInstagram from '@/components/icon/icon-instagram';
|
||||||
|
import IconTwitter from '@/components/icon/icon-twitter';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const ComponentsPagesComingSoonForm = () => {
|
||||||
|
const [demo1, setDemo1] = useState<any>({
|
||||||
|
days: null,
|
||||||
|
hours: null,
|
||||||
|
minutes: null,
|
||||||
|
seconds: null,
|
||||||
|
});
|
||||||
|
const [timer1, setTimer1] = useState<any>(null);
|
||||||
|
|
||||||
|
const setTimerDemo1 = () => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setFullYear(date.getFullYear() + 1);
|
||||||
|
const countDownDate = date.getTime();
|
||||||
|
|
||||||
|
let updatedValue: any = {};
|
||||||
|
setTimer1(
|
||||||
|
setInterval(() => {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
const distance = countDownDate - now;
|
||||||
|
|
||||||
|
updatedValue.days = Math.floor(distance / (1000 * 60 * 60 * 24));
|
||||||
|
updatedValue.hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||||
|
updatedValue.minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
updatedValue.seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
||||||
|
setDemo1((demo1: any) => ({
|
||||||
|
...demo1,
|
||||||
|
...updatedValue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (distance < 0) {
|
||||||
|
clearInterval(timer1);
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimerDemo1();
|
||||||
|
return () => {
|
||||||
|
clearInterval(timer1);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mb-16 flex items-center justify-center gap-2 text-xl font-bold leading-none text-primary sm:text-2xl md:mb-24 md:gap-4 md:text-[50px]">
|
||||||
|
<div className="relative inline-flex h-12 w-14 items-center justify-center rounded-md bg-primary-light p-2 sm:h-16 sm:w-16 md:h-24 md:min-w-[120px]">
|
||||||
|
<div className="absolute inset-1 flex flex-col gap-1">
|
||||||
|
<span className="h-full w-full rounded-md bg-primary/[12%]"></span>
|
||||||
|
<span className="h-full w-full rounded-md bg-white"></span>
|
||||||
|
</div>
|
||||||
|
<span className="relative">{demo1.days}</span>
|
||||||
|
</div>
|
||||||
|
<span>:</span>
|
||||||
|
<div className="relative inline-flex h-12 w-12 items-center justify-center rounded-md bg-primary-light p-2 sm:h-16 sm:w-16 md:h-24 md:min-w-[96px]">
|
||||||
|
<div className="absolute inset-1 flex flex-col gap-1">
|
||||||
|
<span className="h-full w-full rounded-md bg-primary/[12%]"></span>
|
||||||
|
<span className="h-full w-full rounded-md bg-white"></span>
|
||||||
|
</div>
|
||||||
|
<span className="relative">{demo1.hours}</span>
|
||||||
|
</div>
|
||||||
|
<span>:</span>
|
||||||
|
<div className="relative inline-flex h-12 w-12 items-center justify-center rounded-md bg-primary-light p-2 sm:h-16 sm:w-16 md:h-24 md:min-w-[96px]">
|
||||||
|
<div className="absolute inset-1 flex flex-col gap-1">
|
||||||
|
<span className="h-full w-full rounded-md bg-primary/[12%]"></span>
|
||||||
|
<span className="h-full w-full rounded-md bg-white"></span>
|
||||||
|
</div>
|
||||||
|
<span className="relative">{demo1.minutes}</span>
|
||||||
|
</div>
|
||||||
|
<span>:</span>
|
||||||
|
<div className="relative inline-flex h-12 w-12 items-center justify-center rounded-md bg-primary-light p-2 sm:h-16 sm:w-16 md:h-24 md:min-w-[96px]">
|
||||||
|
<div className="absolute inset-1 flex flex-col gap-1">
|
||||||
|
<span className="h-full w-full rounded-md bg-primary/[12%]"></span>
|
||||||
|
<span className="h-full w-full rounded-md bg-white"></span>
|
||||||
|
</div>
|
||||||
|
<span className="relative">{demo1.seconds}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mb-20 md:mb-32">
|
||||||
|
<h2 className="text-lg font-bold uppercase dark:text-white sm:text-xl">Subscribe to get notified!</h2>
|
||||||
|
<div className="relative mb-10 mt-8">
|
||||||
|
<input type="email" placeholder="mail@gmail.com" className="form-input mb-5 py-3.5 placeholder:text-base placeholder:text-white-dark sm:mb-0 sm:pe-32" />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-gradient end-1.5 top-1/2 inline-flex border-0 px-4 py-1.5 text-base shadow-none sm:absolute sm:-translate-y-1/2"
|
||||||
|
onClick={() => router.push('/')}
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul className="flex justify-center gap-3.5 text-white">
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
|
||||||
|
style={{ background: 'linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)' }}
|
||||||
|
>
|
||||||
|
<IconInstagram />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
|
||||||
|
style={{ background: 'linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)' }}
|
||||||
|
>
|
||||||
|
<IconFacebookCircle />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
|
||||||
|
style={{ background: 'linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)' }}
|
||||||
|
>
|
||||||
|
<IconTwitter fill={true} />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
|
||||||
|
style={{ background: 'linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)' }}
|
||||||
|
>
|
||||||
|
<IconGoogle />
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComponentsPagesComingSoonForm;
|
||||||
243
components/pages/components-pages-faq-with-tabs.tsx
Normal file
243
components/pages/components-pages-faq-with-tabs.tsx
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Treemap, Tooltip as ReTooltip, ResponsiveContainer } from 'recharts';
|
||||||
|
import IconDesktop from '@/components/icon/icon-desktop';
|
||||||
|
import IconUser from '@/components/icon/icon-user';
|
||||||
|
|
||||||
|
interface ReportMobileDesktopProps {
|
||||||
|
reports: {
|
||||||
|
mobile: { report: any };
|
||||||
|
desktop: { report: any };
|
||||||
|
};
|
||||||
|
loading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReportMobileDesktop: React.FC<ReportMobileDesktopProps> = ({ reports, loading }) => {
|
||||||
|
const [activeTab, setActiveTab] = useState<'mobile' | 'desktop'>('mobile');
|
||||||
|
|
||||||
|
const currentReport = reports?.[activeTab]?.report || {
|
||||||
|
scores: {},
|
||||||
|
metrics: {},
|
||||||
|
opportunities: [],
|
||||||
|
diagnostics: {},
|
||||||
|
screenshot: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseSavings = (value: string): number => {
|
||||||
|
if (!value) return 0;
|
||||||
|
const val = parseFloat(value);
|
||||||
|
if (value.toLowerCase().includes('ms')) return val;
|
||||||
|
if (value.toLowerCase().includes('s')) return val * 1000;
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderScoreCircle = (score: number) => {
|
||||||
|
const radius = 45; // bigger circle
|
||||||
|
const strokeWidth = 10;
|
||||||
|
const circumference = 2 * Math.PI * radius;
|
||||||
|
const progress = (score / 100) * circumference;
|
||||||
|
|
||||||
|
const color = score >= 90 ? "#22c55e" : score >= 50 ? "#eab308" : "#ef4444"; // green, yellow, red
|
||||||
|
const lightColor = score >= 90 ? "#bbf7d0" : score >= 50 ? "#fef9c3" : "#fecaca";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-28 h-28 flex items-center justify-center">
|
||||||
|
<svg className="transform -rotate-90" width="112" height="112">
|
||||||
|
{/* background light circle */}
|
||||||
|
<circle
|
||||||
|
cx="56"
|
||||||
|
cy="56"
|
||||||
|
r={radius}
|
||||||
|
stroke={lightColor}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
{/* progress circle */}
|
||||||
|
<circle
|
||||||
|
cx="56"
|
||||||
|
cy="56"
|
||||||
|
r={radius}
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
fill="none"
|
||||||
|
strokeDasharray={circumference}
|
||||||
|
strokeDashoffset={circumference - progress}
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className="absolute text-lg font-bold text-gray-800 dark:text-white">
|
||||||
|
{score}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const renderMetrics = (metrics: Record<string, any> = {}) => (
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4 mb-6">
|
||||||
|
{Object.entries(metrics).map(([key, value]) => (
|
||||||
|
<div key={key} className="bg-white dark:bg-[#1B2E4B] p-4 rounded-lg shadow text-center">
|
||||||
|
<p className="text-sm font-medium text-gray-600 dark:text-gray-300">
|
||||||
|
{key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 font-semibold text-gray-800 dark:text-white">{value ?? '-'}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderOpportunities = (opportunities: any[] = []) => (
|
||||||
|
<div className="bg-white dark:bg-[#1B2E4B] rounded-lg shadow p-4 mb-6">
|
||||||
|
<h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Opportunities</h3>
|
||||||
|
{opportunities.length === 0 && <p className="text-gray-500 dark:text-gray-300">No suggestions available.</p>}
|
||||||
|
{opportunities.map((op, idx) => (
|
||||||
|
<div key={idx} className="mb-3 border-b last:border-b-0 pb-2 border-gray-200 dark:border-gray-600">
|
||||||
|
<p className="font-medium text-gray-800 dark:text-white">{op.title ?? '-'}</p>
|
||||||
|
<p className="text-gray-500 text-sm dark:text-gray-300">{op.description ?? '-'}</p>
|
||||||
|
{op.estimatedSavings && (
|
||||||
|
<p className="text-xs text-blue-600 dark:text-blue-400">Estimated Savings: {op.estimatedSavings}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderTreeMap = (opportunities: any[] = []) => {
|
||||||
|
if (opportunities.length === 0) return null;
|
||||||
|
|
||||||
|
const data = opportunities.map(op => ({ name: op.title || 'Untitled', size: parseSavings(op.estimatedSavings) }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white dark:bg-[#1B2E4B] rounded-lg shadow p-4 mb-6">
|
||||||
|
<h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Opportunities Tree Map</h3>
|
||||||
|
<div style={{ width: '100%', height: 400 }}>
|
||||||
|
<ResponsiveContainer>
|
||||||
|
<Treemap data={data} dataKey="size" nameKey="name" stroke="#fff" fill="#3182ce">
|
||||||
|
<ReTooltip content={({ payload }) => {
|
||||||
|
if (!payload || payload.length === 0) return null;
|
||||||
|
const item = payload[0].payload;
|
||||||
|
return (
|
||||||
|
<div className="bg-white dark:bg-[#1B2E4B] text-gray-800 dark:text-white p-2 rounded shadow">
|
||||||
|
<p className="font-semibold">{item.name}</p>
|
||||||
|
<p>{item.size} ms estimated savings</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}} />
|
||||||
|
</Treemap>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderDiagnostics = (diagnostics: any = {}) => (
|
||||||
|
<div className="bg-white dark:bg-[#1B2E4B] rounded-lg shadow p-4 mb-6">
|
||||||
|
<h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Diagnostics</h3>
|
||||||
|
{Object.keys(diagnostics).length === 0 && <p className="text-gray-500 dark:text-gray-300">No diagnostics available.</p>}
|
||||||
|
<div className="divide-y divide-gray-200 dark:divide-gray-600">
|
||||||
|
{Object.entries(diagnostics).map(([key, value]) => (
|
||||||
|
<div key={key} className="py-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
|
||||||
|
</p>
|
||||||
|
{key === 'http2' && (
|
||||||
|
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
All resources should be served via HTTP/2 for better performance.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className={`px-2 py-1 text-xs font-bold rounded ${value ? 'bg-green-100 text-green-700 dark:bg-green-800 dark:text-green-300' : 'bg-red-100 text-red-600 dark:bg-red-800 dark:text-red-300'}`}>
|
||||||
|
{value ? 'Pass' : 'Fail'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Tabs with your flex-col style */}
|
||||||
|
<div className="mb-8 flex justify-center gap-4 rounded-md bg-[#DBE7FF] dark:bg-[#141F31] p-3">
|
||||||
|
<button
|
||||||
|
className={`flex items-center flex-col gap-2 px-6 py-3 font-bold rounded-md transition-colors duration-300 ${activeTab === 'mobile'
|
||||||
|
? 'bg-white text-[#2196F3] dark:bg-[#1B2E4B] dark:text-[#2196F3]'
|
||||||
|
: 'text-[#506690] hover:bg-white hover:text-[#2196F3] dark:text-[#9aa3b1] dark:hover:bg-[#1B2E4B] dark:hover:text-[#2196F3]'
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab('mobile')}
|
||||||
|
>
|
||||||
|
<IconDesktop fill={true} /> Mobile
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={`flex items-center flex-col gap-2 px-6 py-3 font-bold rounded-md transition-colors duration-300 ${activeTab === 'desktop'
|
||||||
|
? 'bg-white text-[#2196F3] dark:bg-[#1B2E4B] dark:text-[#2196F3]'
|
||||||
|
: 'text-[#506690] hover:bg-white hover:text-[#2196F3] dark:text-[#9aa3b1] dark:hover:bg-[#1B2E4B] dark:hover:text-[#2196F3]'
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab('desktop')}
|
||||||
|
>
|
||||||
|
<IconDesktop fill={true} /> Desktop
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
loading ? (
|
||||||
|
<div className='panel p-5 h-[300px] flex items-center justify-center'>
|
||||||
|
<img
|
||||||
|
src="/assets/images/black-logo.png"
|
||||||
|
alt="Loading..."
|
||||||
|
className="w-32 mb-6 animate-pulse"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
< div className="w-full mt-4 border p-5">
|
||||||
|
{/* Top Scores */}
|
||||||
|
<h3 className="text-3xl font-semibold mb-5 text-center text-gray-800 dark:text-white">Diagnose performance issues</h3>
|
||||||
|
<div className="flex flex-wrap gap-6 justify-center mb-6 pb-5 border-b">
|
||||||
|
{['performance', 'accessibility', 'bestPractices', 'seo', 'pwa'].map((key) => {
|
||||||
|
const score = currentReport.scores?.[key];
|
||||||
|
if (score === undefined || score === null) return null;
|
||||||
|
return (
|
||||||
|
<div key={key} className="text-center">
|
||||||
|
{renderScoreCircle(score)}
|
||||||
|
<p className="text-sm font-medium mt-2">{key.charAt(0).toUpperCase() + key.slice(1)}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Metrics */}
|
||||||
|
{renderMetrics(currentReport.metrics)}
|
||||||
|
|
||||||
|
{/* Opportunities */}
|
||||||
|
{renderOpportunities(currentReport.opportunities)}
|
||||||
|
|
||||||
|
{/* Tree Map */}
|
||||||
|
{renderTreeMap(currentReport.opportunities)}
|
||||||
|
|
||||||
|
{/* Diagnostics */}
|
||||||
|
{renderDiagnostics(currentReport.diagnostics)}
|
||||||
|
|
||||||
|
{/* Screenshot */}
|
||||||
|
{currentReport.screenshot && (
|
||||||
|
<div className="flex justify-center mb-6">
|
||||||
|
<img
|
||||||
|
src={currentReport.screenshot}
|
||||||
|
alt={`${activeTab} screenshot`}
|
||||||
|
className="border shadow rounded max-w-full h-auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportMobileDesktop;
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
'use client';
|
||||||
|
import IconMail from '@/components/icon/icon-mail';
|
||||||
|
import IconMessageDots from '@/components/icon/icon-message-dots';
|
||||||
|
import IconPencil from '@/components/icon/icon-pencil';
|
||||||
|
import IconPhoneCall from '@/components/icon/icon-phone-call';
|
||||||
|
import IconUser from '@/components/icon/icon-user';
|
||||||
|
import React from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
const ComponentsPagesContactUsForm = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const submitForm = (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
router.push('/');
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<form className="space-y-5" onSubmit={submitForm}>
|
||||||
|
<div className="relative text-white-dark">
|
||||||
|
<input id="Name" type="text" placeholder="Name" className="form-input ps-10 placeholder:text-white-dark" />
|
||||||
|
<span className="absolute start-4 top-1/2 -translate-y-1/2">
|
||||||
|
<IconUser fill={true} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative text-white-dark">
|
||||||
|
<input id="Email" type="email" placeholder="Email" className="form-input ps-10 placeholder:text-white-dark" />
|
||||||
|
<span className="absolute start-4 top-1/2 -translate-y-1/2">
|
||||||
|
<IconMail fill={true} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative text-white-dark">
|
||||||
|
<input id="Phone" type="text" placeholder="Phone" className="form-input ps-10 placeholder:text-white-dark" />
|
||||||
|
<span className="absolute start-4 top-1/2 -translate-y-1/2">
|
||||||
|
<IconPhoneCall fill={true} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative text-white-dark">
|
||||||
|
<input id="Subject" type="text" placeholder="Subject" className="form-input ps-10 placeholder:text-white-dark" />
|
||||||
|
<span className="absolute start-4 top-1/2 -translate-y-1/2">
|
||||||
|
<IconPencil fill={true} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative text-white-dark">
|
||||||
|
<textarea id="Textarea" rows={4} className="form-textarea resize-none ps-10 placeholder:text-white-dark" placeholder="Message"></textarea>
|
||||||
|
<span className="absolute start-4 top-2.5">
|
||||||
|
<IconMessageDots fill={true} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-gradient !mt-6 w-full border-0 uppercase shadow-[0_10px_20px_-10px_rgba(67,97,238,0.44)]">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComponentsPagesContactUsForm;
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
'use client';
|
||||||
|
import IconPlayCircle from '@/components/icon/icon-play-circle';
|
||||||
|
import IconX from '@/components/icon/icon-x';
|
||||||
|
import { Transition, Dialog, DialogPanel, TransitionChild } from '@headlessui/react';
|
||||||
|
import React, { Fragment, useState } from 'react';
|
||||||
|
|
||||||
|
const ComponentsPagesKnowledgeBaseVideoTutorial = () => {
|
||||||
|
const [modal, setModal] = useState(false);
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
src: '/assets/images/knowledge/image-5.jpg',
|
||||||
|
title: 'Excessive sugar is harmful',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/assets/images/knowledge/image-6.jpg',
|
||||||
|
title: 'Creative Photography',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/assets/images/knowledge/image-7.jpg',
|
||||||
|
title: 'Plan your next trip',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/assets/images/knowledge/image-8.jpg',
|
||||||
|
title: 'My latest Vlog',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mt-10 lg:mt-16">
|
||||||
|
<h3 className="mb-6 text-xl font-bold md:text-3xl">Popular Video Tutorial</h3>
|
||||||
|
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
|
{items.map((item: any, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="space-y-5 rounded-md border border-white-light bg-white p-5 shadow-[0px_0px_2px_0px_rgba(145,158,171,0.20),0px_12px_24px_-4px_rgba(145,158,171,0.12)] dark:border-[#1B2E4B] dark:bg-black"
|
||||||
|
>
|
||||||
|
<div className="group relative h-[340px] overflow-hidden rounded-md">
|
||||||
|
<img src={item.src} alt="video tutorial" className="h-full w-full cursor-pointer object-cover" onClick={() => setModal(true)} />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="absolute left-1/2 top-1/2 grid h-[62px] w-[62px] -translate-x-1/2 -translate-y-1/2 place-content-center rounded-full text-white duration-300 group-hover:scale-110"
|
||||||
|
onClick={() => setModal(true)}
|
||||||
|
>
|
||||||
|
<IconPlayCircle className="h-[62px] w-[62px]" fill={true} />
|
||||||
|
</button>
|
||||||
|
<div className="absolute bottom-0 left-0 w-full bg-white/30 px-5 py-[22px] text-center text-xl text-white backdrop-blur-[5px]">Excessive sugar is harmful</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
;
|
||||||
|
</div>
|
||||||
|
<Transition appear show={modal} as={Fragment}>
|
||||||
|
<Dialog as="div" open={modal} onClose={() => setModal(false)}>
|
||||||
|
<TransitionChild
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div className="fixed inset-0" />
|
||||||
|
</TransitionChild>
|
||||||
|
<div className="fixed inset-0 z-[999] overflow-y-auto bg-[black]/60">
|
||||||
|
<div className="flex min-h-screen items-start justify-center px-4">
|
||||||
|
<TransitionChild
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 scale-95"
|
||||||
|
enterTo="opacity-100 scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 scale-100"
|
||||||
|
leaveTo="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<DialogPanel className="my-8 w-full max-w-3xl overflow-hidden">
|
||||||
|
<div className="text-right">
|
||||||
|
<button onClick={() => setModal(false)} type="button" className="text-white-dark !outline-none hover:text-dark">
|
||||||
|
<IconX />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<iframe title="youtube-video" src="https://www.youtube.com/embed/tgbNymZ7vqY" className="h-[250px] w-full md:h-[550px]"></iframe>
|
||||||
|
</DialogPanel>
|
||||||
|
</TransitionChild>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComponentsPagesKnowledgeBaseVideoTutorial;
|
||||||
1466
package-lock.json
generated
1466
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -29,10 +29,12 @@
|
|||||||
"react-animate-height": "^3.1.0",
|
"react-animate-height": "^3.1.0",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-i18next": "^15.0.2",
|
"react-i18next": "^15.0.2",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
"react-popper": "^2.3.0",
|
"react-popper": "^2.3.0",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"recharts": "^3.2.1",
|
"recharts": "^3.2.1",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"universal-cookie": "^7.2.0",
|
"universal-cookie": "^7.2.0",
|
||||||
"yup": "^1.4.0"
|
"yup": "^1.4.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user