210 lines
9.1 KiB
JavaScript
210 lines
9.1 KiB
JavaScript
import { useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
import { useBrand } from '../context/BrandContext';
|
|
import RegisterBusinessModal from '../components/RegisterBusinessModal';
|
|
import apiClient from '../api/client';
|
|
|
|
const NAV_CARDS = [
|
|
{ to: '/events', label: 'Events', icon: '⚡', desc: 'Manage order events and generate templates' },
|
|
{ to: '/templates', label: 'Templates', icon: '📄', desc: 'Review and configure approved templates' },
|
|
{ to: '/providers', label: 'Providers', icon: '🏢', desc: 'Save your SMS provider DLT details' },
|
|
];
|
|
|
|
function DeleteConfirmModal({ brandName, onCancel, onConfirm, deleting }) {
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 backdrop-blur-sm">
|
|
<div className="bg-white border border-gray-200 rounded-xl p-8 w-full max-w-md shadow-xl">
|
|
<div className="w-12 h-12 rounded-full bg-red-50 flex items-center justify-center mx-auto mb-4">
|
|
<span className="text-xl">🗑</span>
|
|
</div>
|
|
<h3 className="text-lg font-semibold text-gray-900 text-center mb-2">Delete Brand?</h3>
|
|
<p className="text-sm text-gray-500 text-center mb-6">
|
|
This will permanently delete <span className="text-gray-900 font-medium">{brandName}</span> and all associated events, templates, and images. This cannot be undone.
|
|
</p>
|
|
<div className="flex gap-3">
|
|
<button
|
|
onClick={onCancel}
|
|
disabled={deleting}
|
|
className="flex-1 py-2.5 rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium transition disabled:opacity-50"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={onConfirm}
|
|
disabled={deleting}
|
|
className="flex-1 py-2.5 rounded-lg bg-red-600 hover:bg-red-700 text-white text-sm font-medium transition shadow-sm disabled:opacity-50 flex items-center justify-center gap-2"
|
|
>
|
|
{deleting ? <><span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> Deleting…</> : 'Yes, Delete'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function Brand() {
|
|
const { brand, loading, refetch } = useBrand();
|
|
const [showModal, setShowModal] = useState(false);
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
const [deleting, setDeleting] = useState(false);
|
|
const [deleteError, setDeleteError] = useState('');
|
|
|
|
async function handleDelete() {
|
|
setDeleting(true);
|
|
setDeleteError('');
|
|
try {
|
|
await apiClient.delete('/api/brand');
|
|
setShowDeleteConfirm(false);
|
|
await refetch();
|
|
} catch (err) {
|
|
setDeleteError(err.response?.data?.error || 'Failed to delete brand');
|
|
setDeleting(false);
|
|
}
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
<div className="w-8 h-8 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// — WELCOME SCREEN —
|
|
if (!brand) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
<div className="text-center max-w-lg px-8">
|
|
<div className="w-16 h-16 rounded-2xl bg-indigo-600 flex items-center justify-center mx-auto mb-6 text-2xl font-bold text-white shadow-lg shadow-indigo-600/20">
|
|
S
|
|
</div>
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-3 tracking-tight">SMS Template Extension</h1>
|
|
<p className="text-gray-500 text-base mb-8 leading-relaxed">
|
|
Generate TRAI-compliant SMS templates for your Fynd store. We'll scrape your website and use AI to extract your brand context automatically.
|
|
</p>
|
|
<button
|
|
onClick={() => setShowModal(true)}
|
|
className="px-8 py-3 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white font-medium shadow-sm transition-all focus:ring-2 focus:ring-offset-2 focus:ring-indigo-600"
|
|
>
|
|
Register Your Business
|
|
</button>
|
|
</div>
|
|
{showModal && <RegisterBusinessModal onClose={() => { setShowModal(false); refetch(); }} />}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// — BRAND DETAIL PAGE —
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 p-8">
|
|
<div className="max-w-4xl mx-auto">
|
|
|
|
{/* Brand header card */}
|
|
<div className="rounded-xl bg-white border border-gray-200 shadow-sm p-8 mb-6">
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div className="flex-1 min-w-0">
|
|
<h1 className="text-3xl font-bold text-gray-900 tracking-tight truncate">{brand.brandName}</h1>
|
|
<p className="text-gray-500 mt-1 text-sm font-medium">{brand.domain}</p>
|
|
<div className="flex items-center gap-2 mt-4 flex-wrap">
|
|
<span className="text-xs px-2.5 py-1 rounded-md bg-indigo-50 border border-indigo-100 text-indigo-700 font-medium capitalize">
|
|
{brand.tone}
|
|
</span>
|
|
<span className="text-xs text-gray-400 font-medium tracking-wide">
|
|
Registered {new Date(brand.scrapedAt).toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' })}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={() => setShowDeleteConfirm(true)}
|
|
className="shrink-0 text-xs text-red-600 hover:text-red-700 hover:bg-red-50 border border-red-200 px-3 py-2 rounded-lg font-medium transition"
|
|
>
|
|
Delete Brand
|
|
</button>
|
|
</div>
|
|
|
|
{/* Taglines */}
|
|
{brand.taglines?.length > 0 && (
|
|
<div className="mt-6 pt-6 border-t border-gray-100">
|
|
<p className="text-xs text-gray-400 font-semibold uppercase tracking-wider mb-3">Taglines</p>
|
|
<div className="space-y-1.5">
|
|
{brand.taglines.map((t, i) => (
|
|
<p key={i} className="text-gray-700 text-sm">"{t}"</p>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Colors */}
|
|
{brand.colors?.length > 0 && (
|
|
<div className="mt-6">
|
|
<p className="text-xs text-gray-400 font-semibold uppercase tracking-wider mb-3">Brand Colors</p>
|
|
<div className="flex gap-2 flex-wrap">
|
|
{brand.colors.map((c, i) => (
|
|
<div key={i} className="flex items-center gap-2">
|
|
<div
|
|
className="w-6 h-6 rounded-md shadow-sm border border-gray-200"
|
|
style={{ backgroundColor: c }}
|
|
title={c}
|
|
/>
|
|
<span className="text-xs text-gray-500 font-mono">{c}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Brand images */}
|
|
{brand.relevantImagePaths?.length > 0 && (
|
|
<div className="rounded-xl bg-white border border-gray-200 shadow-sm p-6 mb-6">
|
|
<p className="text-xs text-gray-400 font-semibold uppercase tracking-wider mb-4">Brand Images</p>
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
|
|
{brand.relevantImagePaths.map((url, i) => (
|
|
<div key={i} className="group relative rounded-lg overflow-hidden border border-gray-200 aspect-video bg-gray-50">
|
|
<img
|
|
src={url}
|
|
alt={`brand image ${i + 1}`}
|
|
className="w-full h-full object-cover"
|
|
onError={e => { e.target.style.opacity = '0.3'; }}
|
|
/>
|
|
<div className="absolute inset-0 bg-gray-900/80 opacity-0 group-hover:opacity-100 transition-opacity flex items-end p-2">
|
|
<p className="text-xs text-white break-all leading-tight line-clamp-3 font-medium">{url}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Navigation cards */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
{NAV_CARDS.map(card => (
|
|
<Link
|
|
key={card.to}
|
|
to={card.to}
|
|
className="rounded-xl bg-white border border-gray-200 shadow-sm p-5 hover:border-indigo-300 hover:ring-1 hover:ring-indigo-300 transition-all group"
|
|
>
|
|
<div className="text-2xl mb-3 grayscale group-hover:grayscale-0 opacity-80 group-hover:opacity-100 transition-all">{card.icon}</div>
|
|
<p className="font-semibold text-gray-900 mb-1">{card.label}</p>
|
|
<p className="text-sm text-gray-500 leading-relaxed">{card.desc}</p>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{deleteError && (
|
|
<p className="mt-4 text-sm font-medium text-red-600 text-center">{deleteError}</p>
|
|
)}
|
|
</div>
|
|
|
|
{showDeleteConfirm && (
|
|
<DeleteConfirmModal
|
|
brandName={brand.brandName}
|
|
onCancel={() => setShowDeleteConfirm(false)}
|
|
onConfirm={handleDelete}
|
|
deleting={deleting}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|