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,49 @@
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
export default function AboutPage() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>{t('common.about')} {t('common.appName')}</title>
<meta name="description" content="About our free online file conversion tools." />
</Helmet>
<div className="prose mx-auto max-w-2xl dark:prose-invert">
<h1>{t('common.about')}</h1>
<p>
We provide free, fast, and secure online tools for converting, compressing,
and processing files PDFs, images, videos, and text.
</p>
<h2>Why use our tools?</h2>
<ul>
<li><strong>100% Free</strong> No hidden charges, no sign-up required.</li>
<li><strong>Private & Secure</strong> Files are auto-deleted within 2 hours.</li>
<li><strong>Fast Processing</strong> Server-side processing for reliable results.</li>
<li><strong>Works Everywhere</strong> Desktop, tablet, or mobile.</li>
</ul>
<h2>Available Tools</h2>
<ul>
<li>PDF to Word Converter</li>
<li>Word to PDF Converter</li>
<li>PDF Compressor</li>
<li>Image Format Converter</li>
<li>Video to GIF Creator</li>
<li>Word Counter</li>
<li>Text Cleaner & Formatter</li>
</ul>
<h2>Contact</h2>
<p>
Have feedback or feature requests? Reach out at{' '}
<a href="mailto:support@example.com">support@example.com</a>.
</p>
</div>
</>
);
}

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" />
</>
);
}

View File

@@ -0,0 +1,34 @@
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
import { Home } from 'lucide-react';
export default function NotFoundPage() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>404 {t('common.appName')}</title>
<meta name="robots" content="noindex" />
</Helmet>
<div className="flex flex-col items-center justify-center py-24 text-center">
<p className="text-7xl font-bold text-primary-600">404</p>
<h1 className="mt-4 text-2xl font-semibold text-slate-900">
Page Not Found
</h1>
<p className="mt-2 text-slate-500">
The page you're looking for doesn't exist or has been moved.
</p>
<Link
to="/"
className="btn-primary mt-8 inline-flex items-center gap-2"
>
<Home className="h-4 w-4" />
{t('common.home')}
</Link>
</div>
</>
);
}

View File

@@ -0,0 +1,59 @@
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
export default function PrivacyPage() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>{t('common.privacy')} {t('common.appName')}</title>
<meta name="description" content="Privacy policy for our online tools." />
</Helmet>
<div className="prose mx-auto max-w-2xl dark:prose-invert">
<h1>{t('common.privacy')}</h1>
<p><em>Last updated: {new Date().toISOString().split('T')[0]}</em></p>
<h2>1. Data Collection</h2>
<p>
We only collect files you intentionally upload for processing. We do not
require registration, and we do not store personal information.
</p>
<h2>2. File Processing & Storage</h2>
<ul>
<li>Uploaded files are processed on our secure servers.</li>
<li>All uploaded and output files are <strong>automatically deleted within 2 hours</strong>.</li>
<li>Files are stored in encrypted cloud storage during processing.</li>
<li>We do not access, read, or share the content of your files.</li>
</ul>
<h2>3. Cookies & Analytics</h2>
<p>
We use essential cookies to remember your language preference. We may use
Google Analytics and Google AdSense, which may place their own cookies.
You can manage cookie preferences in your browser settings.
</p>
<h2>4. Third-Party Services</h2>
<ul>
<li><strong>Google AdSense</strong> for displaying advertisements.</li>
<li><strong>AWS S3</strong> for temporary file storage.</li>
</ul>
<h2>5. Security</h2>
<p>
We employ industry-standard security measures including HTTPS encryption,
file validation, rate limiting, and automatic file cleanup.
</p>
<h2>6. Contact</h2>
<p>
Questions about this policy? Contact us at{' '}
<a href="mailto:support@example.com">support@example.com</a>.
</p>
</div>
</>
);
}

View File

@@ -0,0 +1,66 @@
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet-async';
export default function TermsPage() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>{t('common.terms')} {t('common.appName')}</title>
<meta name="description" content="Terms of service for our online tools." />
</Helmet>
<div className="prose mx-auto max-w-2xl dark:prose-invert">
<h1>{t('common.terms')}</h1>
<p><em>Last updated: {new Date().toISOString().split('T')[0]}</em></p>
<h2>1. Acceptance of Terms</h2>
<p>
By accessing and using SaaS-PDF, you agree to be bound by these Terms of
Service. If you do not agree, please discontinue use immediately.
</p>
<h2>2. Service Description</h2>
<p>
SaaS-PDF provides free online tools for file conversion, compression,
and transformation. The service is provided &ldquo;as is&rdquo; without
warranties of any kind.
</p>
<h2>3. Acceptable Use</h2>
<ul>
<li>You may only upload files that you have the right to process.</li>
<li>You must not upload malicious, illegal, or copyrighted content without authorization.</li>
<li>Automated or excessive use of the service is prohibited.</li>
</ul>
<h2>4. File Handling</h2>
<ul>
<li>All uploaded and processed files are automatically deleted within 2 hours.</li>
<li>We are not responsible for any data loss during processing.</li>
<li>You are responsible for maintaining your own file backups.</li>
</ul>
<h2>5. Limitation of Liability</h2>
<p>
SaaS-PDF shall not be liable for any direct, indirect, incidental, or
consequential damages resulting from the use or inability to use the
service.
</p>
<h2>6. Changes to Terms</h2>
<p>
We reserve the right to modify these terms at any time. Continued use of
the service after changes constitutes acceptance of the updated terms.
</p>
<h2>7. Contact</h2>
<p>
Questions about these terms? Contact us at{' '}
<a href="mailto:support@example.com">support@example.com</a>.
</p>
</div>
</>
);
}