first commit

This commit is contained in:
Samandar Turgunboyev
2025-10-18 17:14:59 +05:00
parent edf364b389
commit 036a36ce90
92 changed files with 14614 additions and 135 deletions

269
src/pages/seo/ui/Seo.tsx Normal file
View File

@@ -0,0 +1,269 @@
"use client";
import {
AlertCircle,
CheckCircle,
FileText,
Image as ImageIcon,
TrendingUp,
} from "lucide-react";
import { useState, type ChangeEvent } from "react";
type SeoData = {
title: string;
description: string;
keywords: string;
ogTitle: string;
ogDescription: string;
ogImage: string;
};
export default function Seo() {
const [formData, setFormData] = useState<SeoData>({
title: "",
description: "",
keywords: "",
ogTitle: "",
ogDescription: "",
ogImage: "",
});
const [savedSeo, setSavedSeo] = useState<SeoData | null>(null);
const [imagePreview, setImagePreview] = useState<string | null>(null);
const handleChange = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleImageUpload = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result as string;
setImagePreview(result);
setFormData((prev) => ({
...prev,
ogImage: result,
}));
};
reader.readAsDataURL(file);
}
};
const handleSave = () => {
setSavedSeo(formData);
setFormData({
description: "",
keywords: "",
ogDescription: "",
ogImage: "",
ogTitle: "",
title: "",
});
};
const getTitleLength = () => formData.title.length;
const getDescriptionLength = () => formData.description.length;
const isValidTitle = getTitleLength() > 30 && getTitleLength() <= 60;
const isValidDescription =
getDescriptionLength() > 120 && getDescriptionLength() <= 160;
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 to-slate-800 p-8 w-full">
<div className="max-w-[90%] mx-auto">
{/* Header */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-2">
<TrendingUp className="w-8 h-8 text-blue-400" />
<h1 className="text-4xl font-bold text-white">SEO Manager</h1>
</div>
<p className="text-slate-400">
Saytingizni qidiruv tizimida yaxshi pozitsiyaga keltiring
</p>
</div>
{/* Main Content */}
<div className="grid grid-cols-1 gap-8">
<div className="bg-slate-800 rounded-lg p-6 space-y-6">
{/* Title */}
<div>
<label className="block text-sm font-semibold text-white mb-2">
<FileText className="inline w-4 h-4 mr-1" /> Page Title
</label>
<input
type="text"
name="title"
value={formData.title}
onChange={handleChange}
placeholder="Sahifa sarlavhasi (3060 belgi)"
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<div className="flex items-center justify-between mt-2">
<span className="text-sm text-slate-400">
{getTitleLength()} / 60
</span>
{isValidTitle && (
<CheckCircle className="w-5 h-5 text-green-400" />
)}
{getTitleLength() > 0 && !isValidTitle && (
<AlertCircle className="w-5 h-5 text-yellow-400" />
)}
</div>
</div>
{/* Description */}
<div>
<label className="block text-sm font-semibold text-white mb-2">
Meta Description
</label>
<textarea
name="description"
value={formData.description}
onChange={handleChange}
placeholder="Sahifa tavsifi (120160 belgi)"
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
/>
<div className="flex items-center justify-between mt-2">
<span className="text-sm text-slate-400">
{getDescriptionLength()} / 160
</span>
{isValidDescription && (
<CheckCircle className="w-5 h-5 text-green-400" />
)}
{getDescriptionLength() > 0 && !isValidDescription && (
<AlertCircle className="w-5 h-5 text-yellow-400" />
)}
</div>
</div>
{/* Keywords */}
<div>
<label className="block text-sm font-semibold text-white mb-2">
Keywords
</label>
<input
type="text"
name="keywords"
value={formData.keywords}
onChange={handleChange}
placeholder="Kalit so'zlar (vergul bilan ajratilgan)"
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<p className="text-xs text-slate-400 mt-1">
Masalan: Python, Web Development, Coding
</p>
</div>
{/* OG Tags */}
<div className="border-t border-slate-700 pt-6">
<h3 className="text-sm font-semibold text-white mb-4">
Open Graph (Ijtimoiy Tarmoqlar)
</h3>
<div className="space-y-4">
<div>
<label className="block text-sm text-slate-300 mb-2">
OG Title
</label>
<input
type="text"
name="ogTitle"
value={formData.ogTitle}
onChange={handleChange}
placeholder="Ijtimoiy tarmoqdagi sarlavha"
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm text-slate-300 mb-2">
OG Description
</label>
<textarea
name="ogDescription"
value={formData.ogDescription}
onChange={handleChange}
placeholder="Ijtimoiy tarmoqdagi tavsif"
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
/>
</div>
<div>
<label className="block text-sm text-slate-300 mb-2">
<ImageIcon className="inline w-4 h-4 mr-1" /> OG Image
</label>
<div className="space-y-3">
<input
type="file"
accept="image/*"
onChange={handleImageUpload}
className="w-full bg-slate-700 text-white px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 file:bg-blue-600 file:text-white file:px-3 file:py-1 file:rounded file:border-0 file:cursor-pointer"
/>
{imagePreview && (
<div className="relative">
<img
src={imagePreview}
alt="Preview"
className="w-full h-40 object-cover rounded-lg"
/>
<button
type="button"
onClick={() => {
setImagePreview(null);
setFormData((prev) => ({
...prev,
ogImage: "",
}));
}}
className="mt-2 text-xs bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded"
>
Ochirish
</button>
</div>
)}
</div>
</div>
</div>
</div>
<button
onClick={handleSave}
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg transition-colors"
>
Saqlash
</button>
</div>
</div>
{/* Saved SEO Data (Preview) */}
{savedSeo && (
<div className="mt-8 bg-slate-700 rounded-lg p-6 text-slate-200">
<h3 className="text-lg font-semibold mb-2">
Saqlangan SEO Malumotlari
</h3>
<pre className="bg-slate-800 p-4 rounded text-xs overflow-auto">
{JSON.stringify(
{
...savedSeo,
ogImage: savedSeo.ogImage
? savedSeo.ogImage.substring(0, 100) + "..."
: "",
},
null,
2,
)}
</pre>
</div>
)}
</div>
</div>
);
}