feat: Initialize frontend with React, Vite, and Tailwind CSS

- Set up main entry point for React application.
- Create About, Home, NotFound, Privacy, and Terms pages with SEO support.
- Implement API service for file uploads and task management.
- Add global styles using Tailwind CSS.
- Create utility functions for SEO and text processing.
- Configure Vite for development and production builds.
- Set up Nginx configuration for serving frontend and backend.
- Add scripts for cleanup of expired files and sitemap generation.
- Implement deployment script for production environment.
This commit is contained in:
Your Name
2026-02-28 23:31:19 +02:00
parent 3b84ebb916
commit 85d98381df
93 changed files with 5940 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
import {
FileText,
FileOutput,
Minimize2,
ImageIcon,
Film,
Hash,
Eraser,
} from 'lucide-react';
import ToolCard from '@/components/shared/ToolCard';
import AdSlot from '@/components/layout/AdSlot';
interface ToolInfo {
key: string;
path: string;
icon: React.ReactNode;
bgColor: string;
}
const tools: ToolInfo[] = [
{ key: 'pdfToWord', path: '/tools/pdf-to-word', icon: <FileText className="h-6 w-6 text-red-600" />, bgColor: 'bg-red-50' },
{ key: 'wordToPdf', path: '/tools/word-to-pdf', icon: <FileOutput className="h-6 w-6 text-blue-600" />, bgColor: 'bg-blue-50' },
{ key: 'compressPdf', path: '/tools/compress-pdf', icon: <Minimize2 className="h-6 w-6 text-orange-600" />, bgColor: 'bg-orange-50' },
{ key: 'imageConvert', path: '/tools/image-converter', icon: <ImageIcon className="h-6 w-6 text-purple-600" />, bgColor: 'bg-purple-50' },
{ key: 'videoToGif', path: '/tools/video-to-gif', icon: <Film className="h-6 w-6 text-emerald-600" />, bgColor: 'bg-emerald-50' },
{ key: 'wordCounter', path: '/tools/word-counter', icon: <Hash className="h-6 w-6 text-blue-600" />, bgColor: 'bg-blue-50' },
{ key: 'textCleaner', path: '/tools/text-cleaner', icon: <Eraser className="h-6 w-6 text-indigo-600" />, bgColor: 'bg-indigo-50' },
];
export default function HomePage() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>{t('common.appName')} {t('home.heroSub')}</title>
<meta name="description" content={t('home.heroSub')} />
<link rel="canonical" href={window.location.origin} />
<script type="application/ld+json">
{JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebSite',
name: t('common.appName'),
url: window.location.origin,
description: t('home.heroSub'),
potentialAction: {
'@type': 'SearchAction',
target: `${window.location.origin}/tools/{search_term_string}`,
'query-input': 'required name=search_term_string',
},
})}
</script>
</Helmet>
{/* Hero Section */}
<section className="py-12 text-center sm:py-16">
<h1 className="text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl">
{t('home.hero')}
</h1>
<p className="mx-auto mt-4 max-w-xl text-lg text-slate-500">
{t('home.heroSub')}
</p>
</section>
{/* Ad Slot */}
<AdSlot slot="home-top" format="horizontal" className="mb-8" />
{/* Tools Grid */}
<section>
<h2 className="mb-6 text-center text-xl font-semibold text-slate-800">
{t('home.popularTools')}
</h2>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{tools.map((tool) => (
<ToolCard
key={tool.key}
to={tool.path}
icon={tool.icon}
title={t(`tools.${tool.key}.title`)}
description={t(`tools.${tool.key}.shortDesc`)}
bgColor={tool.bgColor}
/>
))}
</div>
</section>
{/* Ad Slot - Bottom */}
<AdSlot slot="home-bottom" className="mt-12" />
</>
);
}