280 lines
11 KiB
TypeScript
280 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import React, { FormEvent, useEffect, useMemo, useState } from 'react';
|
|
import Link from 'next/link';
|
|
import { CheckCircle2, Lightbulb, MessageSquare, Star, TriangleAlert } from 'lucide-react';
|
|
import { AnnouncementBar } from '@/layout/AnnouncementBar';
|
|
import { Header } from '@/layout/Header';
|
|
import { Navbar } from '@/layout/Navbar';
|
|
import { Footer } from '@/layout/Footer';
|
|
import { StickyHeaderSpacer } from '@/components/StickyHeaderSpacer';
|
|
import { FloatingButtons } from '@/components/FloatingButtons';
|
|
import {
|
|
storefrontService,
|
|
type FooterInfo,
|
|
type StorefrontSettings,
|
|
MOCK_FOOTER_INFO,
|
|
} from '@/services/api/storefrontService';
|
|
import { buildFeedbackPage } from '@/lib/feedbackContent';
|
|
|
|
const HIGHLIGHT_ICONS = {
|
|
idea: Lightbulb,
|
|
issue: TriangleAlert,
|
|
praise: MessageSquare,
|
|
} as const;
|
|
|
|
const fieldClass =
|
|
'w-full px-3 py-2.5 bg-white border border-[#e5e5e5] rounded-lg text-[13px] text-[#222] outline-none focus:border-primary transition-colors';
|
|
|
|
const labelClass = 'block text-[12px] font-bold text-[#1E293B] mb-1.5';
|
|
|
|
export default function FeedbackPage() {
|
|
const [footer, setFooter] = useState<FooterInfo>(MOCK_FOOTER_INFO);
|
|
const [settings, setSettings] = useState<StorefrontSettings>({ store_name: 'iFixKart' });
|
|
const [name, setName] = useState('');
|
|
const [email, setEmail] = useState('');
|
|
const [topic, setTopic] = useState('suggestion');
|
|
const [orderNo, setOrderNo] = useState('');
|
|
const [rating, setRating] = useState(0);
|
|
const [message, setMessage] = useState('');
|
|
const [submitted, setSubmitted] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
Promise.all([
|
|
storefrontService.getFooterInfo(),
|
|
storefrontService.getSettings(),
|
|
]).then(([info, store]) => {
|
|
if (cancelled) return;
|
|
if (info) setFooter(info);
|
|
if (store) setSettings(store);
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
const storeName = settings.store_name || 'iFixKart';
|
|
const supportEmail = footer.email || 'support@ifixkart.com';
|
|
const doc = useMemo(() => buildFeedbackPage(storeName), [storeName]);
|
|
|
|
const handleSubmit = (e: FormEvent) => {
|
|
e.preventDefault();
|
|
const topicLabel = doc.topics.find((t) => t.id === topic)?.label || topic;
|
|
const body = [
|
|
`Name: ${name}`,
|
|
`Email: ${email}`,
|
|
`Topic: ${topicLabel}`,
|
|
orderNo ? `Order / job: ${orderNo}` : '',
|
|
rating ? `Rating: ${rating} / 5` : '',
|
|
'',
|
|
message,
|
|
]
|
|
.filter((line) => line !== '')
|
|
.join('\n');
|
|
|
|
const href = `mailto:${supportEmail}?subject=${encodeURIComponent(
|
|
`${storeName} feedback — ${topicLabel}`
|
|
)}&body=${encodeURIComponent(body)}`;
|
|
window.location.href = href;
|
|
setSubmitted(true);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#F8F9FB] text-[#1E293B] flex flex-col font-sans">
|
|
<AnnouncementBar />
|
|
<Header />
|
|
<StickyHeaderSpacer />
|
|
<Navbar />
|
|
|
|
<main className="flex-grow py-10 pb-20">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-[900px]">
|
|
<h1 className="text-[28px] md:text-[32px] font-bold text-[#1E293B]">
|
|
{doc.heading}
|
|
</h1>
|
|
<p className="text-[14px] text-gray-500 mt-2 max-w-2xl">{doc.intro}</p>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-8">
|
|
{doc.highlights.map((item) => {
|
|
const Icon =
|
|
HIGHLIGHT_ICONS[item.id as keyof typeof HIGHLIGHT_ICONS] || MessageSquare;
|
|
return (
|
|
<div
|
|
key={item.id}
|
|
className="bg-white border border-gray-200 rounded-xl p-5"
|
|
>
|
|
<div className="w-10 h-10 rounded-lg bg-primary/10 text-primary flex items-center justify-center mb-3">
|
|
<Icon className="w-5 h-5" />
|
|
</div>
|
|
<h2 className="text-[16px] font-bold text-[#1E293B]">{item.title}</h2>
|
|
<p className="text-[13px] text-gray-500 mt-1 leading-relaxed">
|
|
{item.detail}
|
|
</p>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<article className="mt-8 bg-white border border-gray-200 rounded-xl overflow-hidden">
|
|
<div className="p-6 md:p-8">
|
|
{submitted ? (
|
|
<div className="py-6 text-center">
|
|
<CheckCircle2 className="w-10 h-10 text-primary mx-auto mb-3" />
|
|
<h2 className="text-[20px] font-bold text-[#1E293B]">Thanks for the feedback</h2>
|
|
<p className="text-[14px] text-gray-600 mt-2 max-w-md mx-auto leading-relaxed">
|
|
Your mail app should open with the message ready to send to {supportEmail}.
|
|
If it did not, copy your note and email us directly.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
setSubmitted(false);
|
|
setMessage('');
|
|
}}
|
|
className="mt-6 text-[13px] font-semibold text-primary hover:underline cursor-pointer"
|
|
>
|
|
Send another message
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<h2 className="text-[16px] font-bold text-[#1E293B] mb-1">Send a message</h2>
|
|
<p className="text-[13px] text-gray-500 mb-4">
|
|
Fields marked with * are required.
|
|
</p>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label htmlFor="fb-name" className={labelClass}>
|
|
Name *
|
|
</label>
|
|
<input
|
|
id="fb-name"
|
|
required
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
className={fieldClass}
|
|
autoComplete="name"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="fb-email" className={labelClass}>
|
|
Email *
|
|
</label>
|
|
<input
|
|
id="fb-email"
|
|
type="email"
|
|
required
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
className={fieldClass}
|
|
autoComplete="email"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label htmlFor="fb-topic" className={labelClass}>
|
|
Topic *
|
|
</label>
|
|
<select
|
|
id="fb-topic"
|
|
required
|
|
value={topic}
|
|
onChange={(e) => setTopic(e.target.value)}
|
|
className={`${fieldClass} cursor-pointer`}
|
|
>
|
|
{doc.topics.map((t) => (
|
|
<option key={t.id} value={t.id}>
|
|
{t.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="fb-order" className={labelClass}>
|
|
Order or job ID
|
|
</label>
|
|
<input
|
|
id="fb-order"
|
|
value={orderNo}
|
|
onChange={(e) => setOrderNo(e.target.value)}
|
|
className={fieldClass}
|
|
placeholder="Optional"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<p className={labelClass}>How was your experience?</p>
|
|
<div className="flex items-center gap-1">
|
|
{[1, 2, 3, 4, 5].map((stars) => (
|
|
<button
|
|
key={stars}
|
|
type="button"
|
|
aria-label={`${stars} star${stars === 1 ? '' : 's'}`}
|
|
onClick={() => setRating(rating === stars ? 0 : stars)}
|
|
className="p-0.5 cursor-pointer"
|
|
>
|
|
<Star
|
|
size={22}
|
|
className={
|
|
stars <= rating
|
|
? 'fill-amber-400 text-amber-400'
|
|
: 'text-gray-200'
|
|
}
|
|
/>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="fb-message" className={labelClass}>
|
|
Message *
|
|
</label>
|
|
<textarea
|
|
id="fb-message"
|
|
required
|
|
rows={6}
|
|
value={message}
|
|
onChange={(e) => setMessage(e.target.value)}
|
|
className={`${fieldClass} resize-y min-h-[140px]`}
|
|
placeholder="Tell us what happened or what we should improve."
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="w-full sm:w-auto px-6 py-3 bg-primary text-white text-[14px] font-semibold rounded-lg hover:brightness-95 cursor-pointer"
|
|
>
|
|
Send feedback
|
|
</button>
|
|
</form>
|
|
)}
|
|
|
|
<p className="text-[13px] text-gray-500 mt-8 pt-6 border-t border-gray-100">
|
|
For a return or payment issue, use{' '}
|
|
<Link href="/returns" className="text-primary font-semibold hover:underline">
|
|
Returns
|
|
</Link>{' '}
|
|
or{' '}
|
|
<Link href="/payments" className="text-primary font-semibold hover:underline">
|
|
Payments
|
|
</Link>
|
|
. Need answers first?{' '}
|
|
<Link href="/help" className="text-primary font-semibold hover:underline">
|
|
Help Center
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</main>
|
|
|
|
<Footer />
|
|
<FloatingButtons />
|
|
</div>
|
|
);
|
|
}
|