feat: Enhance Pricing Page with Enterprise Plan and Billing Toggle
- Added Enterprise plan with features and pricing. - Introduced billing toggle for monthly and yearly subscriptions. - Updated feature list to include enterprise-specific features. - Improved UI for plan cards and added new styles for better visual appeal. - Adjusted SEO metadata to reflect new pricing structure. - Enhanced global styles for marketing elements.
This commit is contained in:
@@ -1,7 +1,21 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { Mail, Send, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
|
||||
import {
|
||||
Mail,
|
||||
Send,
|
||||
CheckCircle,
|
||||
AlertCircle,
|
||||
Loader2,
|
||||
Phone,
|
||||
MapPin,
|
||||
ChevronDown,
|
||||
Github,
|
||||
Twitter,
|
||||
Linkedin,
|
||||
Facebook,
|
||||
Instagram,
|
||||
} from 'lucide-react';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { toast } from 'sonner';
|
||||
import SEOHead from '@/components/seo/SEOHead';
|
||||
@@ -9,11 +23,27 @@ import { generateWebPage, getSiteOrigin } from '@/utils/seo';
|
||||
import { getApiClient } from '@/services/api';
|
||||
|
||||
const CONTACT_EMAIL = 'support@dociva.io';
|
||||
const CONTACT_PHONE = '+1 (555) 123-4567';
|
||||
const OFFICE_ADDRESS = '123 Tech Avenue, Innovation City, CA 90001';
|
||||
const API_BASE = import.meta.env.VITE_API_URL || '';
|
||||
const api = getApiClient();
|
||||
|
||||
type Category = 'general' | 'bug' | 'feature';
|
||||
|
||||
const FAQ_ITEMS = [
|
||||
{ questionKey: 'pages.contact.faq1q', answerKey: 'pages.contact.faq1a', questionDefault: 'What is your pricing?', answerDefault: 'We offer a generous free tier with all tools. Pro plans start at $9/month for more credits and features.' },
|
||||
{ questionKey: 'pages.contact.faq2q', answerKey: 'pages.contact.faq2a', questionDefault: 'How does the platform work?', answerDefault: 'Upload your file, choose a tool, and download the result — no sign-up required for basic usage.' },
|
||||
{ questionKey: 'pages.contact.faq3q', answerKey: 'pages.contact.faq3a', questionDefault: 'Is my data secure?', answerDefault: 'Yes. All transfers are encrypted, and files are automatically deleted within minutes of processing.' },
|
||||
];
|
||||
|
||||
const SOCIAL_LINKS = [
|
||||
{ icon: Facebook, href: '#', label: 'Facebook' },
|
||||
{ icon: Twitter, href: '#', label: 'Twitter' },
|
||||
{ icon: Linkedin, href: '#', label: 'LinkedIn' },
|
||||
{ icon: Instagram, href: '#', label: 'Instagram' },
|
||||
{ icon: Github, href: '#', label: 'GitHub' },
|
||||
];
|
||||
|
||||
export default function ContactPage() {
|
||||
const { t } = useTranslation();
|
||||
const siteOrigin = getSiteOrigin(typeof window !== 'undefined' ? window.location.origin : '');
|
||||
@@ -21,6 +51,7 @@ export default function ContactPage() {
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||
|
||||
const placeholderKey = `pages.contact.${category}Placeholder` as const;
|
||||
|
||||
@@ -93,130 +124,187 @@ export default function ContactPage() {
|
||||
})}
|
||||
/>
|
||||
|
||||
<div className="mx-auto max-w-2xl">
|
||||
<div className="mb-8 text-center">
|
||||
<h1 className="text-3xl font-bold text-slate-800 dark:text-slate-100">
|
||||
{t('pages.contact.title')}
|
||||
<div className="mx-auto max-w-6xl">
|
||||
{/* Page header */}
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-extrabold tracking-tight text-slate-900 dark:text-white sm:text-5xl">
|
||||
{t('pages.contact.title', 'Get in Touch')}
|
||||
</h1>
|
||||
<p className="mt-2 text-slate-600 dark:text-slate-400">
|
||||
<p className="mt-3 text-lg text-primary-600 dark:text-primary-400">
|
||||
{t('pages.contact.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm dark:border-slate-700 dark:bg-slate-800">
|
||||
<h2 className="text-lg font-semibold text-slate-700 dark:text-slate-200">
|
||||
{t('pages.contact.formTitle')}
|
||||
</h2>
|
||||
|
||||
{/* Category */}
|
||||
{/* Two-column layout */}
|
||||
<div className="grid gap-10 lg:grid-cols-2">
|
||||
{/* Left column — Contact form */}
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('pages.contact.categoryLabel')}
|
||||
</label>
|
||||
<select
|
||||
value={category}
|
||||
onChange={(e) => setCategory(e.target.value as Category)}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white px-4 py-2 text-slate-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200"
|
||||
>
|
||||
<option value="general">{t('pages.contact.categories.general')}</option>
|
||||
<option value="bug">{t('pages.contact.categories.bug')}</option>
|
||||
<option value="feature">{t('pages.contact.categories.feature')}</option>
|
||||
</select>
|
||||
<form onSubmit={handleSubmit} className="space-y-5">
|
||||
{/* Category */}
|
||||
<select
|
||||
value={category}
|
||||
onChange={(e) => setCategory(e.target.value as Category)}
|
||||
className="w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-slate-700 shadow-sm transition-colors focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||
>
|
||||
<option value="general">{t('pages.contact.categories.general')}</option>
|
||||
<option value="bug">{t('pages.contact.categories.bug')}</option>
|
||||
<option value="feature">{t('pages.contact.categories.feature')}</option>
|
||||
</select>
|
||||
|
||||
{/* Name */}
|
||||
<input
|
||||
name="name"
|
||||
type="text"
|
||||
required
|
||||
placeholder={t('pages.contact.namePlaceholder', 'Name')}
|
||||
className="w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-slate-700 shadow-sm transition-colors focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||
/>
|
||||
|
||||
{/* Email */}
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
placeholder={t('pages.contact.emailPlaceholder', 'Email')}
|
||||
className="w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-slate-700 shadow-sm transition-colors focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||
/>
|
||||
|
||||
{/* Subject */}
|
||||
<input
|
||||
name="subject"
|
||||
type="text"
|
||||
required
|
||||
placeholder={t('pages.contact.subjectPlaceholder', 'Subject')}
|
||||
className="w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-slate-700 shadow-sm transition-colors focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||
/>
|
||||
|
||||
{/* Message */}
|
||||
<textarea
|
||||
name="message"
|
||||
rows={5}
|
||||
required
|
||||
placeholder={t(placeholderKey, 'Message')}
|
||||
className="w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-slate-700 shadow-sm transition-colors focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||
/>
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Submit */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="inline-flex items-center gap-2 rounded-xl bg-primary-600 px-8 py-3 font-semibold text-white shadow-md transition-all hover:-translate-y-0.5 hover:bg-primary-700 hover:shadow-lg disabled:opacity-50"
|
||||
>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||
{loading ? t('common.sending', 'Sending...') : t('common.send', 'Submit')}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<div>
|
||||
<label htmlFor="name" className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('common.name')}
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
required
|
||||
placeholder={t('pages.contact.namePlaceholder')}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white px-4 py-2 text-slate-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label htmlFor="email" className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('common.email')}
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
placeholder={t('pages.contact.emailPlaceholder')}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white px-4 py-2 text-slate-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Subject */}
|
||||
<div>
|
||||
<label htmlFor="subject" className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('common.subject')}
|
||||
</label>
|
||||
<input
|
||||
id="subject"
|
||||
name="subject"
|
||||
type="text"
|
||||
required
|
||||
placeholder={t('pages.contact.subjectPlaceholder')}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white px-4 py-2 text-slate-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div>
|
||||
<label htmlFor="message" className="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">
|
||||
{t('common.message')}
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows={6}
|
||||
required
|
||||
placeholder={t(placeholderKey)}
|
||||
className="w-full rounded-lg border border-slate-300 bg-white px-4 py-2 text-slate-700 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-200 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Error */}
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-900/30 dark:text-red-300">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
{error}
|
||||
{/* Right column — Contact info cards */}
|
||||
<div className="space-y-5">
|
||||
{/* Email card */}
|
||||
<div className="flex items-start gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-shadow hover:shadow-md dark:border-slate-700 dark:bg-slate-800">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-primary-100 dark:bg-primary-900/30">
|
||||
<Mail className="h-6 w-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900 dark:text-white">{t('pages.contact.emailLabel', 'Email:')}</p>
|
||||
<a
|
||||
href={`mailto:${CONTACT_EMAIL}`}
|
||||
className="text-sm text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400"
|
||||
>
|
||||
{CONTACT_EMAIL}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Submit */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex w-full items-center justify-center gap-2 rounded-lg bg-primary-600 px-6 py-3 font-medium text-white transition-colors hover:bg-primary-700 disabled:opacity-50"
|
||||
>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||
{loading ? t('common.sending', 'Sending...') : t('common.send')}
|
||||
</button>
|
||||
</form>
|
||||
{/* Phone card */}
|
||||
<div className="flex items-start gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-shadow hover:shadow-md dark:border-slate-700 dark:bg-slate-800">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-primary-100 dark:bg-primary-900/30">
|
||||
<Phone className="h-6 w-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900 dark:text-white">{t('pages.contact.phoneLabel', 'Phone:')}</p>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">{CONTACT_PHONE}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Direct email fallback */}
|
||||
<div className="mt-6 text-center text-sm text-slate-500 dark:text-slate-400">
|
||||
<p>
|
||||
{t('pages.contact.directEmail')}{' '}
|
||||
<a
|
||||
href={`mailto:${CONTACT_EMAIL}`}
|
||||
className="inline-flex items-center gap-1 font-medium text-primary-600 hover:underline dark:text-primary-400"
|
||||
>
|
||||
<Mail className="h-4 w-4" />
|
||||
{CONTACT_EMAIL}
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-1">{t('pages.contact.responseTime')}</p>
|
||||
{/* Office card */}
|
||||
<div className="flex items-start gap-4 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-shadow hover:shadow-md dark:border-slate-700 dark:bg-slate-800">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-primary-100 dark:bg-primary-900/30">
|
||||
<MapPin className="h-6 w-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900 dark:text-white">{t('pages.contact.officeLabel', 'Office:')}</p>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">{OFFICE_ADDRESS}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social links */}
|
||||
<div>
|
||||
<h3 className="mb-4 text-lg font-bold text-slate-900 dark:text-white">
|
||||
{t('pages.contact.connectTitle', 'Connect With Us')}
|
||||
</h3>
|
||||
<div className="flex gap-3">
|
||||
{SOCIAL_LINKS.map(({ icon: Icon, href, label }) => (
|
||||
<a
|
||||
key={label}
|
||||
href={href}
|
||||
aria-label={label}
|
||||
className="flex h-11 w-11 items-center justify-center rounded-full bg-primary-600 text-white shadow-md transition-all hover:-translate-y-0.5 hover:bg-primary-700 hover:shadow-lg"
|
||||
>
|
||||
<Icon className="h-5 w-5" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Response time */}
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">
|
||||
{t('pages.contact.responseTime')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<section className="mt-16">
|
||||
<h2 className="mb-8 text-2xl font-bold text-slate-900 dark:text-white">
|
||||
{t('pages.contact.faqTitle', 'FAQ')}
|
||||
</h2>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{FAQ_ITEMS.map((faq, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition-shadow hover:shadow-md dark:border-slate-700 dark:bg-slate-800"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpenFaq(openFaq === idx ? null : idx)}
|
||||
className="flex w-full items-center justify-between text-left"
|
||||
>
|
||||
<span className="pr-2 text-sm font-semibold text-slate-900 dark:text-white">
|
||||
{t(faq.questionKey, faq.questionDefault)}
|
||||
</span>
|
||||
<ChevronDown
|
||||
className={`h-5 w-5 shrink-0 text-primary-500 transition-transform ${openFaq === idx ? 'rotate-180' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
{openFaq === idx && (
|
||||
<p className="mt-3 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
|
||||
{t(faq.answerKey, faq.answerDefault)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user