Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 6,162 Bytes
482dcd8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | import { useState } from "react";
import ReactDOM from "react-dom";
import { sendCustomTemplateRequest } from "../api/enterprise";
interface Props {
open: boolean;
onClose: () => void;
}
export default function DesignerTemplateRequestModal({ open, onClose }: Props) {
const [description, setDescription] = useState("");
const [altContact, setAltContact] = useState("");
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleClose = () => {
if (loading) return;
onClose();
setTimeout(() => {
setDescription("");
setAltContact("");
setSuccess(false);
setError(null);
}, 200);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!description.trim()) return;
setLoading(true);
setError(null);
try {
await sendCustomTemplateRequest({
description: description.trim(),
alternate_contact: altContact.trim() || undefined,
});
setSuccess(true);
setTimeout(() => {
handleClose();
}, 3000);
} catch {
setError("Failed to send request. Please try again.");
} finally {
setLoading(false);
}
};
if (!open) return null;
return ReactDOM.createPortal(
<div className="fixed inset-0 z-[130] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={handleClose} aria-hidden />
<div className="relative bg-white rounded-2xl shadow-2xl w-full max-w-lg p-6">
{success ? (
<div className="flex flex-col items-center py-8 text-center gap-4">
<div className="w-14 h-14 bg-green-100 rounded-full flex items-center justify-center">
<svg className="w-7 h-7 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900">Request Successfully Sent</h3>
<p className="text-sm text-gray-500 max-w-sm">
Thank you! Our design team will review your request and reach out with next steps shortly.
You'll hear back from us within 1–2 business days.
</p>
</div>
) : (
<>
<div className="flex items-start justify-between mb-5">
<div>
<h3 className="text-lg font-semibold text-gray-900">Request a Designer Template</h3>
<p className="text-sm text-gray-500 mt-1">
Tell us what you need and our design team will create a custom Designer Template tailored to your brand.
</p>
</div>
<button onClick={handleClose} className="ml-4 shrink-0 text-gray-400 hover:text-gray-600 transition">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Template Description <span className="text-red-400">*</span>
</label>
<textarea
required
rows={5}
maxLength={3000}
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Describe your ideal template or give links to references."
className="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm text-gray-900 placeholder-gray-400 resize-none focus:outline-none focus:ring-2 focus:ring-purple-300"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Alternate Contact <span className="text-gray-400 font-normal">(optional)</span>
</label>
<input
type="text"
maxLength={300}
value={altContact}
onChange={(e) => setAltContact(e.target.value)}
placeholder="Alternate email, phone, etc."
className="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-purple-300"
/>
</div>
{error && <p className="text-sm text-red-500">{error}</p>}
<div className="flex gap-3 pt-2">
<button
type="button"
onClick={handleClose}
className="flex-1 px-4 py-2.5 text-sm font-medium text-gray-600 border border-gray-200 rounded-xl hover:bg-gray-50 transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={loading || !description.trim()}
className="flex-1 px-4 py-2.5 text-sm font-medium text-white bg-purple-600 hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-colors flex items-center justify-center gap-2"
>
{loading && (
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z" />
</svg>
)}
{loading ? "Submitting…" : "Submit Request"}
</button>
</div>
</form>
</>
)}
</div>
</div>,
document.body
);
}
|