finish the checkform

هذا الالتزام موجود في:
2025-10-28 15:30:04 +02:00
الأصل 954fc4c34a
التزام 1131dc8527
3 ملفات معدلة مع 751 إضافات و264 حذوفات

عرض الملف

@@ -1,11 +1,22 @@
import { X } from "lucide-react";
import { useState, type ChangeEvent } from "react"; import { useState, type ChangeEvent } from "react";
import { Upload, X, Trash2, XCircle, User } from "lucide-react";
interface KeywordConfirmationModalProps { interface KeywordConfirmationModalProps {
onClose: () => void; onClose: () => void;
initialStep?: number;
} }
interface FormData { interface FormData {
// Step 1: Logo & Socials
firstName: string;
socialProfiles: string[];
uploadedLogo: object;
// Step 2: Media Assets
notes: string;
tShirtSize: string;
uploadedFiles: Array<{ name: string; size: string; progress?: number }>;
// Step 3: Payment
nameOnCard: string; nameOnCard: string;
cardNumber: string; cardNumber: string;
expirationDate: string; expirationDate: string;
@@ -18,30 +29,23 @@ interface FormData {
} }
interface FormErrors { interface FormErrors {
firstName?: string;
uploadedLogo?: string;
tShirtSize?: string;
nameOnCard?: string; nameOnCard?: string;
cardNumber?: string; cardNumber?: string;
expirationDate?: string; expirationDate?: string;
securityCode?: string; securityCode?: string;
billingAddress?: string; billingAddress?: string;
} }
const US_STATES = [ const US_STATES = [
"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Alabama", "Alaska", "Arizona", "Arkansas"
"Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky",
"Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi",
"Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico",
"New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania",
"Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
"Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"
]; ];
const US_CITIES = [ const US_CITIES = [
"New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "New York", "Los Angeles", "Chicago"
"San Diego", "Dallas", "San Jose", "Austin", "Jacksonville", "Fort Worth", "Columbus",
"Charlotte", "San Francisco", "Indianapolis", "Seattle", "Denver", "Boston", "Nashville",
"Detroit", "Oklahoma City", "Portland", "Las Vegas", "Memphis", "Louisville", "Baltimore",
"Milwaukee", "Albuquerque", "Tucson", "Fresno", "Mesa", "Sacramento", "Atlanta", "Kansas City",
"Colorado Springs", "Miami", "Raleigh", "Omaha", "Long Beach", "Virginia Beach", "Oakland"
]; ];
const BeaconIcon = () => ( const BeaconIcon = () => (
@@ -79,6 +83,11 @@ const CreditCardIcon = () => (
<path d="M3 15.1465C3 14.7487 3.15804 14.3671 3.43934 14.0858C3.72064 13.8045 4.10218 13.6465 4.5 13.6465H6C6.39782 13.6465 6.77936 13.8045 7.06066 14.0858C7.34196 14.3671 7.5 14.7487 7.5 15.1465V16.6465C7.5 17.0443 7.34196 17.4258 7.06066 17.7071C6.77936 17.9884 6.39782 18.1465 6 18.1465H4.5C4.10218 18.1465 3.72064 17.9884 3.43934 17.7071C3.15804 17.4258 3 17.0443 3 16.6465V15.1465Z" fill="#65677D" /> <path d="M3 15.1465C3 14.7487 3.15804 14.3671 3.43934 14.0858C3.72064 13.8045 4.10218 13.6465 4.5 13.6465H6C6.39782 13.6465 6.77936 13.8045 7.06066 14.0858C7.34196 14.3671 7.5 14.7487 7.5 15.1465V16.6465C7.5 17.0443 7.34196 17.4258 7.06066 17.7071C6.77936 17.9884 6.39782 18.1465 6 18.1465H4.5C4.10218 18.1465 3.72064 17.9884 3.43934 17.7071C3.15804 17.4258 3 17.0443 3 16.6465V15.1465Z" fill="#65677D" />
</svg> </svg>
); );
const UploadIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30" fill="none">
<path d="M15.625 7.5C12.7575 7.5 10.9875 9.56875 10.4925 10.5587C10.3888 10.7664 10.2293 10.941 10.0319 11.0631C9.83454 11.1851 9.60708 11.2499 9.375 11.25C8.42875 11.25 6.9825 11.5437 5.80875 12.39C4.6975 13.19 3.75 14.5325 3.75 16.875C3.75 19.2175 4.6975 20.5612 5.80875 21.36C6.98375 22.2062 8.42875 22.5 9.375 22.5C9.70652 22.5 10.0245 22.6317 10.2589 22.8661C10.4933 23.1005 10.625 23.4185 10.625 23.75C10.625 24.0815 10.4933 24.3995 10.2589 24.6339C10.0245 24.8683 9.70652 25 9.375 25C8.02875 25 6.0375 24.6063 4.3475 23.39C2.59375 22.1262 1.25 20.0325 1.25 16.875C1.25 13.7175 2.59375 11.6238 4.3475 10.36C5.7525 9.34875 7.36375 8.90625 8.64125 8.785C9.68875 7.21625 12.0325 5 15.625 5C18.2875 5 20.1875 6.35 21.3838 7.9125C22.1588 8.92798 22.7066 10.0981 22.99 11.3438C23.9713 11.5025 25.1087 11.8812 26.1213 12.6112C27.615 13.685 28.75 15.4662 28.75 18.125C28.75 20.7825 27.615 22.5638 26.1213 23.64C24.6912 24.6688 23.0125 25 21.875 25C21.5435 25 21.2255 24.8683 20.9911 24.6339C20.7567 24.3995 20.625 24.0815 20.625 23.75C20.625 23.4185 20.7567 23.1005 20.9911 22.8661C21.2255 22.6317 21.5435 22.5 21.875 22.5C22.6125 22.5 23.7463 22.2687 24.66 21.61C25.51 20.9975 26.25 19.9675 26.25 18.125C26.25 16.2825 25.51 15.2513 24.66 14.6388C23.7475 13.9813 22.6125 13.75 21.875 13.75C21.5435 13.75 21.2255 13.6183 20.9911 13.3839C20.7567 13.1495 20.625 12.8315 20.625 12.5C20.625 11.8725 20.27 10.5712 19.3975 9.43125C18.5625 8.3375 17.3375 7.5 15.625 7.5ZM14.1163 12.8663C14.3507 12.6319 14.6685 12.5003 15 12.5003C15.3315 12.5003 15.6493 12.6319 15.8837 12.8663L19.6337 16.6163C19.8614 16.852 19.9874 17.1678 19.9846 17.4955C19.9817 17.8232 19.8503 18.1368 19.6185 18.3685C19.3868 18.6003 19.0732 18.7317 18.7455 18.7346C18.4178 18.7374 18.102 18.6114 17.8663 18.3837L16.25 16.7675V23.75C16.25 24.0815 16.1183 24.3995 15.8839 24.6339C15.6495 24.8683 15.3315 25 15 25C14.6685 25 14.3505 24.8683 14.1161 24.6339C13.8817 24.3995 13.75 24.0815 13.75 23.75V16.7675L12.1337 18.3837C11.898 18.6114 11.5822 18.7374 11.2545 18.7346C10.9268 18.7317 10.6132 18.6003 10.3815 18.3685C10.1497 18.1368 10.0183 17.8232 10.0154 17.4955C10.0126 17.1678 10.1386 16.852 10.3663 16.6163L14.1163 12.8663Z" fill="#65677D" />
</svg>
);
const DropdownArrow = () => ( const DropdownArrow = () => (
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -86,8 +95,51 @@ const DropdownArrow = () => (
</svg> </svg>
); );
export default function KeywordConfirmationModal({ onClose }: KeywordConfirmationModalProps) { const CompanyIcon = () => (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="2" width="8" height="16" rx="2" stroke="#65677D" strokeWidth="1.5" />
<path d="M6.24137 0.75H2.75C1.64543 0.75 0.75 1.64543 0.75 2.75V10.5632C0.75 11.6678 1.64543 12.5632 2.75 12.5632H7.1566" stroke="#65677D" strokeWidth="1.5" strokeLinecap="square" strokeLinejoin="round" />
<circle cx="13" cy="5" r="1" fill="#65677D" />
<circle cx="5" cy="9" r="1" fill="#65677D" />
<circle cx="13" cy="8" r="1" fill="#65677D" />
<circle cx="5" cy="13" r="1" fill="#65677D" />
<circle cx="13" cy="12" r="1" fill="#65677D" />
</svg>
);
const GlobeIcon = () => (
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.6252 9.81261C18.6252 8.26742 18.167 6.75693 17.3086 5.47215C16.4501 4.18738 15.2299 3.18602 13.8024 2.5947C12.3748 2.00338 10.8039 1.84866 9.28845 2.15012C7.77295 2.45157 6.38088 3.19565 5.28826 4.28826C4.19565 5.38087 3.45157 6.77295 3.15012 8.28844C2.84867 9.80394 3.00339 11.3748 3.5947 12.8024C4.18602 14.2299 5.18738 15.4501 6.47216 16.3086C7.75694 17.167 9.26742 17.6252 10.8126 17.6252M3 9.81261H10.0145" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" />
<path d="M10.8129 17.6252C9.0078 15.4057 7.95533 12.6696 7.80762 9.81261C7.95533 6.95556 9.0078 4.21952 10.8129 2C12.2278 3.74 13.1864 5.80538 13.602 8.0092" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M15.9094 17.4102C15.5378 18.2002 14.392 18.1313 14.118 17.3024L12.4086 12.1325C12.1593 11.3787 12.8753 10.6628 13.629 10.912L18.7989 12.6214C19.6278 12.8954 19.6968 14.0412 18.9068 14.4128L17.1831 15.2234C16.9796 15.3192 16.8158 15.4829 16.7201 15.6865L15.9094 17.4102Z" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
const HeartIcon = () => (
<svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.2917 3.83334C13.225 3.83334 11.5 6.38921 11.5 7.66667C11.5 6.38921 9.775 3.83334 6.70833 3.83334C3.64167 3.83334 2.875 6.38921 2.875 7.66667C2.875 14.375 11.5 19.1667 11.5 19.1667C11.5 19.1667 20.125 14.375 20.125 7.66667C20.125 6.38921 19.3583 3.83334 16.2917 3.83334Z" fill="white" stroke="white" strokeWidth="1.41667" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
// export default function KeywordConfirmationModal({ onClose }: KeywordConfirmationModalProps) {
export default function KeywordConfirmationModal({ onClose, initialStep = 1 }: KeywordConfirmationModalProps) {
const [formData, setFormData] = useState<FormData>({ const [formData, setFormData] = useState<FormData>({
// Step 1: Logo & Socials
firstName: "",
socialProfiles: [""],
uploadedLogo: {},
// Step 2: Media Assets
notes: "",
tShirtSize: "Medium (US M)",
uploadedFiles: initialStep === 2 ? [
{ name: "Document.docx", size: "234kb" },
{ name: "Resume.pdf", size: "4Mb", progress: 73 }
] : [],
// Step 3: Payment
nameOnCard: "", nameOnCard: "",
cardNumber: "", cardNumber: "",
expirationDate: "", expirationDate: "",
@@ -99,7 +151,58 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
sameAsShipping: true, sameAsShipping: true,
}); });
const [errors, setErrors] = useState<FormErrors>({}); const [errors, setErrors] = useState<FormErrors>({});
const [currentStep, setCurrentStep] = useState(initialStep);
// Step 1 & 2 helper functions
const addSocialProfile = () => {
setFormData(prev => ({ ...prev, socialProfiles: [...prev.socialProfiles, ""] }));
};
const updateSocialProfile = (index: number, value: string) => {
const updated = [...formData.socialProfiles];
updated[index] = value;
setFormData(prev => ({ ...prev, socialProfiles: updated }));
};
const removeFile = (index: number) => {
setFormData(prev => ({
...prev,
uploadedFiles: prev.uploadedFiles.filter((_, i) => i !== index)
}));
};
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>, logo: string) => {
const files = e.target.files;
if (files) {
const newFiles = Array.from(files).map((file) => ({
name: file.name,
size: `${(file.size / 1024).toFixed(0)}kb`,
progress: Math.random() > 0.5 ? 73 : undefined,
}));
setFormData(prev => {
if (logo) {
return {
...prev,
uploadedLogo: newFiles[0]
};
} else {
return {
...prev,
uploadedFiles: [
...(Array.isArray(prev.uploadedFiles) ? prev.uploadedFiles : []),
...newFiles
]
};
}
});
}
};
const formatCardNumber = (value: string) => { const formatCardNumber = (value: string) => {
const numbers = value.replace(/\s/g, ""); const numbers = value.replace(/\s/g, "");
@@ -134,7 +237,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
return sum % 10 === 0; return sum % 10 === 0;
}; };
const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => { const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target; const { name, value } = e.target;
let formattedValue = value; let formattedValue = value;
@@ -157,7 +260,44 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
setFormData((prev) => ({ ...prev, sameAsShipping: !prev.sameAsShipping })); setFormData((prev) => ({ ...prev, sameAsShipping: !prev.sameAsShipping }));
}; };
const validateForm = (): boolean => { // The approach used could be improved for clarity and consistency with the rest of your codebase.
// 1. You are mutating the `errors` object directly, which is not recommended in React,
// especially if errors is managed by useState, because it will not trigger a re-render.
// 2. It's better to create a new error object (like in your validateStep3)
// 3. The comment and the logic don't match - "firstName is optional" but then you require it.
// Recommended rewrite:
const validateStep1 = (): boolean => {
const newErrors: FormErrors = {};
if (!formData.firstName.trim()) {
newErrors.firstName = "Name is required";
}
if (!formData.uploadedLogo) {
newErrors.uploadedLogo = "Logo is required";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const validateStep2 = (): boolean => {
const newErrors: FormErrors = {};
if (!formData.tShirtSize.trim()) {
newErrors.tShirtSize = "T-shirt size is required";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const validateStep3 = (): boolean => {
// Step 3 validation - payment fields are required
const newErrors: FormErrors = {}; const newErrors: FormErrors = {};
if (!formData.nameOnCard.trim()) { if (!formData.nameOnCard.trim()) {
@@ -197,8 +337,24 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
return Object.keys(newErrors).length === 0; return Object.keys(newErrors).length === 0;
}; };
const nextStep = () => {
if (currentStep < 3) {
let canAdvance = false;
if (currentStep === 1) {
canAdvance = validateStep1();
} else if (currentStep === 2) {
canAdvance = validateStep2();
}
if (canAdvance) {
setCurrentStep(currentStep + 1);
}
}
};
const handleSubmit = () => { const handleSubmit = () => {
if (validateForm()) { if (validateStep3()) {
console.log("Form submitted:", formData); console.log("Form submitted:", formData);
onClose(); onClose();
} }
@@ -206,38 +362,356 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
return ( return (
<div className="w-full max-w-[723px] bg-white rounded-[20px] shadow-[0_19px_60px_0_rgba(105,109,114,0.54)] flex flex-col"> <div className="w-full max-w-[723px] bg-white rounded-[20px] shadow-[0_19px_60px_0_rgba(105,109,114,0.54)] flex flex-col">
<div className="flex h-[81px] flex-col justify-center items-end border-b border-grey-300 px-6 py-6"> {/* <div className="flex h-[81px] flex-col justify-center items-end border-b border-gray-300 px-6 py-6">
<div className="flex justify-between items-start w-full"> <div className="flex justify-between items-start w-full">
<div className="flex items-center gap-4 flex-1"> <div className="flex items-center gap-4 flex-1">
<div className="w-12 h-12 flex-shrink-0 rounded-[13px] border border-grey-300 shadow-[0_0_0_2px_#F6F6FA] flex items-center justify-center"> <div className="w-12 h-12 flex-shrink-0 rounded-[13px] border border-gray-300 shadow-[0_0_0_2px_#F6F6FA] flex items-center justify-center">
<BeaconIcon /> <BeaconIcon />
</div> </div>
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5 flex-1 min-w-0">
<h2 className="text-base font-bold text-[#272735] leading-[22px]">Keyword Confirmation</h2> <h2 className="text-[#272735] font-bold text-base leading-[22px]">
Let's Light the Beacons
</h2>
<p className="text-[#676D80] text-sm leading-5">
Alright, this is the part where we make it official. Like moving in together, but with backlinks.
</p>
</div> </div>
</div> </div>
<button <button
onClick={onClose} onClick={onClose}
className="w-7 h-7 flex items-center justify-center rounded hover:bg-grey-200 transition-colors" className="w-7 h-7 flex items-center justify-center rounded hover:bg-gray-200 transition-colors"
aria-label="Close modal" aria-label="Close modal"
> >
<X className="w-5 h-5 text-grey-800" /> <X className="w-5 h-5 text-[#65677D]" />
</button> </button>
</div> </div>
</div> */}
{/* Modal Header */}
<div className="flex flex-col">
<div className="flex justify-between items-start p-6 pb-5">
<div className="flex items-center gap-4 flex-1 pr-4">
<div className="w-12 h-12 flex-shrink-0 relative">
<div className="w-12 h-12 rounded-[13px] border border-gray-300 shadow-[0_0_0_2px_hsl(var(--gray-100))] absolute inset-0" />
<div className="absolute inset-0 flex items-center justify-center">
<BeaconIcon />
</div>
</div>
<div className="flex flex-col gap-0.5 flex-1 min-w-0">
<h2 className="text-[#272735] font-bold text-base leading-[22px]">
Let's Light the Beacons
</h2>
<p className="text-[#676D80] text-sm leading-5">
Alright, this is the part where we make it official. Like moving in together, but with backlinks.
</p>
</div>
</div>
<button
onClick={onClose}
className="w-7 h-7 flex-shrink-0 flex items-center justify-center bg-[#F2F4F7] hover:bg-gray-100 rounded-full transition-colors"
>
<X className="w-5 h-5 text-[#676D80]" />
</button>
</div>
{/* Step Indicators */}
<div className="flex flex-col justify-center items-center gap-2 pb-5 border-b border-gray-300 px-6">
<div className="flex items-center gap-2.5">
<div className={`flex items-center gap-1.5 px-3 py-1 pl-2 rounded-full ${currentStep >= 1 ? 'bg-[#E6ECFB]' : 'border border-[#DFE2EB]'}`}>
<div
className={`w-4 h-4 rounded-full flex items-center justify-center gap-2 ${currentStep >= 1 ? '' : 'bg-[#DFE1EB]'} pt-[5px] pr-[12px] pb-[5px] pl-[9px]`}
>
<span className={`text-[12px] w-4 h-4 rounded-full px-2 flex items-center justify-center font-medium leading-5 ${currentStep >= 1 ? 'text-white bg-[#4C60E5]' : 'text-[#65677D]'}`}>1</span>
</div>
<span className={`text-[12px] font-medium leading-5 ${currentStep >= 1 ? 'text-[#272735]' : 'text-[#787B91]'}`}>Logo & Socials</span>
</div>
<svg width="17" height="3" viewBox="0 0 17 3" fill="none">
<circle cx="1.5" cy="1.5" r="1.5" fill={currentStep >= 2 ? "#4C60E5" : "#C3C5D5"} />
<circle cx="8.5" cy="1.5" r="1.5" fill={currentStep >= 2 ? "#4C60E5" : "#C3C5D5"} />
<circle cx="15.5" cy="1.5" r="1.5" fill={currentStep >= 2 ? "#4C60E5" : "#C3C5D5"} />
</svg>
<div className={`flex items-center gap-1.5 px-3 py-1 pl-2 rounded-full ${currentStep >= 2 ? 'bg-[#E6ECFB]' : 'border border-[#DFE2EB]'}`}>
<div className={`w-4 h-4 rounded-full flex items-center justify-center gap-2 ${currentStep >= 1 ? '' : 'bg-[#DFE1EB]'} pt-[5px] pr-[12px] pb-[5px] pl-[9px]`}>
<span className={`text-[12px] w-4 h-4 rounded-full px-2 flex items-center justify-center font-medium leading-5 ${currentStep >= 2 ? 'text-white bg-[#4C60E5]' : 'text-[#65677D] bg-[#DFE1EB]'}`}>2</span>
</div>
<span className={`text-[12px] font-medium leading-5 ${currentStep >= 2 ? 'text-[#272735]' : 'text-[#787B91]'}`}>Media Assets</span>
</div>
<svg width="17" height="3" viewBox="0 0 17 3" fill="none">
<circle cx="1.5" cy="1.5" r="1.5" fill={currentStep >= 3 ? "#4C60E5" : "#C3C5D5"} />
<circle cx="8.5" cy="1.5" r="1.5" fill={currentStep >= 3 ? "#4C60E5" : "#C3C5D5"} />
<circle cx="15.5" cy="1.5" r="1.5" fill={currentStep >= 3 ? "#4C60E5" : "#C3C5D5"} />
</svg>
<div className={`flex items-center gap-1.5 px-3 py-1 pl-2 rounded-full ${currentStep >= 3 ? 'bg-[#E6ECFB]' : 'border border-[#DFE2EB]'}`}>
<div className={`w-4 h-4 rounded-full flex items-center justify-center gap-2 ${currentStep >= 1 ? '' : 'bg-[#DFE1EB]'} pt-[5px] pr-[12px] pb-[5px] pl-[9px]`}>
<span className={`text-[12px] w-4 h-4 rounded-full px-2 flex items-center justify-center font-medium leading-5 ${currentStep >= 3 ? 'text-white bg-[#4C60E5]' : 'text-[#65677D] bg-[#DFE1EB]'}`}>3</span>
</div>
<span className={`text-[12px] font-medium leading-5 ${currentStep >= 3 ? 'text-[#272735]' : 'text-[#787B91]'}`}>Payment</span>
</div>
</div>
</div>
</div> </div>
<div className="flex flex-col gap-9 px-6 py-5">
{/* Content */}
<div className="flex-1 overflow-y-auto px-6 py-5">
{currentStep === 1 && (
<div className="flex flex-col gap-5">
{/* Business Info Card */}
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-1.5 p-3 rounded-xl bg-white shadow-[0_1px_1px_0_rgba(5,23,84,0.02),0_1px_2.5px_0_rgba(5,11,18,0.12),0_0_0_1px_#F2F3F7]"> <div className="flex flex-col gap-1.5 p-3 rounded-xl bg-white shadow-[0_1px_1px_0_rgba(5,23,84,0.02),0_1px_2.5px_0_rgba(5,11,18,0.12),0_0_0_1px_#F2F3F7]">
<div className="text-body-s font-semibold text-grey-700">Your You're lighting Beacons for:</div> <div className="text-[#676D80] font-semibold text-xs leading-[18px]">Your Business</div>
<svg className="w-full h-px" viewBox="0 0 524 1">
<path d="M0 0.5H524" stroke="#E6E8EF" strokeDasharray="2 2" />
</svg>
<div className="flex items-center gap-2">
<div className="flex items-center gap-3 py-1.5">
<CompanyIcon />
<span className="text-gray-900 font-semibold text-sm leading-5">Beverly Hills Medical Spa</span>
</div>
<div className="w-1 h-1 rounded-full bg-gray-300" />
<div className="flex items-center gap-3 py-1.5">
<GlobeIcon />
<span className="text-[#4C60E5] font-semibold text-sm leading-5">www.bhmedicalspas.com</span>
</div>
</div>
</div>
</div>
{/* Section Header */}
<div className="flex items-center gap-1.5">
<div className="flex items-center gap-2 p-1 rounded-md border border-gray-300 bg-white">
<User className="w-4 h-4 text-[#65677D]" strokeWidth={1.2} />
</div>
<h3 className="text-[#272735] font-semibold text-base leading-[22px]">Let's Get To Know You</h3>
</div>
{/* Form Fields */}
<div className="flex flex-col gap-5">
{/* First Name */}
<div className="flex flex-col gap-1.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">First Name:</label>
</div>
<p className="text-[#65677D] text-[11px] leading-4">We like to know who to thank when you go viral.</p>
</div>
<input
type="text"
name="firstName"
value={formData.firstName}
onChange={handleInputChange}
required
placeholder="Enter name..."
className="h-8 px-2.5 rounded-lg border border-gray-300 bg-white shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06)] text-sm leading-5 placeholder:text-[#999BAD] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]"
/>
{errors.firstName && <p className="text-red-500 text-[11px] leading-4">{errors.firstName}</p>}
</div>
{/* Upload Logo */}
<div className="flex flex-col gap-2.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">Upload Your Logo:</label>
</div>
<p className="text-[#65677D] text-[11px] leading-4">We'll treat it like it belongs in the Louvre.</p>
</div>
<label className="h-[110px] flex flex-col items-center justify-center gap-1 rounded-lg border-[1.5px] border-dashed border-[#DFE1EB] bg-gray-50 cursor-pointer hover:bg-gray-100 transition-colors">
<input
type="file"
className="hidden"
accept="image/*"
onChange={e => handleFileUpload(e, 'logo')}
/>
<UploadIcon />
{!formData?.uploadedLogo?.name ?
<div className="text-center text-xs leading-[18px] px-2">
<span className="text-gray-900">Click to upload </span>
<span className="text-[#65677D]">or drag files here.<br />SVG, PNG, JPG, or GIF (max. 800 x 600px)</span>
</div>
:
<div>
{formData?.uploadedLogo?.name &&
<span className="text-gray-900 text-sm">
{formData?.uploadedLogo?.name}
</span>
}
</div>
}
</label>
{errors.uploadedLogo && <p className="text-red-500 text-[11px] leading-4">{errors.uploadedLogo}</p>}
</div>
{/* Social Media Profiles */}
<div className="flex flex-col gap-1.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">Social Media Profiles:</label>
<span className="text-[#787B91] font-semibold text-xs leading-[18px]">Optional</span>
</div>
<p className="text-[#65677D] text-[11px] leading-4">Yes, even that TikTok you said you'd update in 2022.</p>
</div>
{formData.socialProfiles.map((profile, index) => (
<input
type="text"
value={profile}
onChange={(e) => updateSocialProfile(index, e.target.value)}
placeholder="Enter name..."
className="h-8 px-2.5 rounded-lg border border-gray-300 bg-white shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06)] text-sm leading-5 placeholder:text-[#999BAD] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]"
/>
))}
<button
onClick={addSocialProfile}
className="text-[#4C60E5] font-semibold text-sm leading-5 text-left hover:text-primary-600 transition-colors"
>
+ Add another one
</button>
</div>
</div>
</div>
)}
{currentStep === 2 && (
<div className="flex flex-col gap-5">
{/* Section Header */}
<div className="flex items-center gap-1.5">
<div className="flex items-center gap-2 p-1 rounded-md border border-gray-300 bg-white">
<User className="w-4 h-4 text-[#65677D]" strokeWidth={1.2} />
</div>
<h3 className="text-[#272735] font-semibold text-base leading-[22px]">Let's Get To Know You</h3>
</div>
{/* Form Fields */}
<div className="flex flex-col gap-5">
{/* Image/Video Assets */}
<div className="flex flex-col gap-2.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">Image / Video Assets:</label>
<span className="text-[#787B91] font-semibold text-xs leading-[18px]">Optional</span>
</div>
<p className="text-[#65677D] text-[11px] leading-4">
Product shots, commercials, brand photoshoots, CEO karaoke footage - if it's got pixels, we want it.
</p>
</div>
<div className="flex items-start gap-2">
<div className="flex-1 h-8 px-2.5 flex items-center justify-between rounded-lg border border-gray-300 bg-white shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06)]">
<span className="text-gray-900 text-sm leading-5">Upload</span>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M2 4L6 8L10 4" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<label className="w-20 h-8 flex items-center justify-center rounded-lg border border-gray-300 bg-white shadow-[0_1px_1px_0_rgba(5,19,65,0.07),0_1px_4px_-2px_rgba(24,39,75,0.02),0_0_2px_0_#E0E0E0,0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_hsl(var(--gray-300))] text-gray-900 font-semibold text-sm leading-5 cursor-pointer hover:bg-gray-50 transition-colors">
<input type="file" className="hidden" multiple onChange={handleFileUpload} />
Upload
</label>
</div>
{/* Uploaded Files */}
{formData.uploadedFiles.length > 0 && (
<div className="flex flex-col gap-0">
{formData.uploadedFiles.map((file, index) => (
<div className={`${index === formData.uploadedFiles.length - 1 ? 'rounded-b-lg' : ''} ${index === 0 ? 'rounded-t-lg border' : 'border-x border-b'} border-gray-300 bg-gray-100 p-3 flex items-center justify-between relative overflow-hidden`}>
{file.progress && (
<div className="absolute bottom-0 left-0 h-1 bg-[#4C60E5] transition-all" style={{ width: `${file.progress}%` }} />
)}
<div className="flex items-center gap-2 relative z-10">
<svg width="24" height="20" viewBox="0 0 24 20" fill="none">
<path d="M6.67857 6.5V3.16071C6.67857 2.51967 7.19824 2 7.83929 2C8.48033 2 9 2.51967 9 3.16071V6.5C9 7.88071 7.88071 9 6.5 9C5.11929 9 4 7.88071 4 6.5V3.27273" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M11.9171 2H15C16.1046 2 17 2.89543 17 4V16C17 17.1046 16.1046 18 15 18H6C4.89543 18 4 17.1046 4 16V12.0465" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<span className="text-xs leading-[18px]">
<span className="text-gray-900">{file.name} </span>
<span className="text-[#787B91]">({file.size})</span>
</span>
</div>
<div className="flex items-center gap-1 relative z-10">
{file.progress && <span className="text-gray-900 text-xs leading-[18px]">{file.progress}%</span>}
<button
onClick={() => removeFile(index)}
className="hover:opacity-70 transition-opacity"
>
{file.progress ? (
<XCircle className="w-5 h-5 text-[#65677D]" strokeWidth={1.5} />
) : (
<Trash2 className="w-5 h-5 text-[#65677D]" strokeWidth={1.5} />
)}
</button>
</div>
</div>
))}
</div>
)}
</div>
{/* Anything You Want Us To Know */}
<div className="flex flex-col gap-1.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">Anything You Want Us To Know?</label>
<span className="text-[#787B91] font-semibold text-xs leading-[18px]">Optional</span>
</div>
<p className="text-[#65677D] text-[11px] leading-4">
Business goals? Secret sauce? Favorite pizza topping? We don't judge. Unless it's anchovies.
</p>
</div>
<textarea
name="notes"
value={formData.notes}
onChange={handleInputChange}
placeholder="Notes..."
rows={3}
className="px-2.5 py-2 rounded-lg border border-gray-300 bg-white shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06)] text-sm leading-5 placeholder:text-[#999BAD] focus:outline-none focus:ring-2 focus:ring-[#4C60E5] resize-none"
/>
</div>
{/* T-Shirt Size */}
<div className="flex flex-col gap-2.5">
<div className="flex flex-col gap-0">
<div className="flex justify-between items-center">
<label className="text-gray-900 font-semibold text-sm leading-5">T-Shirt Size:</label>
</div>
<p className="text-[#65677D] text-[11px] leading-4">
We send you merch. It's soft. It fits weirdly well. You'll love it.
</p>
</div>
<div className="h-8 px-2.5 flex items-center justify-between rounded-lg border border-gray-300 bg-white shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06)]">
<select
name="tShirtSize"
value={formData.tShirtSize}
onChange={handleInputChange}
className="flex-1 bg-transparent text-gray-900 text-sm leading-5 focus:outline-none appearance-none"
>
<option>Small (US S)</option>
<option>Medium (US M)</option>
<option>Large (US L)</option>
<option>X-Large (US XL)</option>
</select>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className="pointer-events-none">
<path d="M2 4L6 8L10 4" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
{errors.tShirtSize && <p className="text-red-500 text-[11px] leading-4">{errors.tShirtSize}</p>}
</div>
</div>
</div>
)}
{
currentStep === 3 && (
<div className="">
<div className="flex flex-col gap-1.5 p-3 rounded-xl bg-white shadow-[0_1px_1px_0_rgba(5,23,84,0.02),0_1px_2.5px_0_rgba(5,11,18,0.12),0_0_0_1px_#F2F3F7] mb-8">
<div className="text-body-s font-semibold text-[#787B91]">Your You're lighting Beacons for:</div>
<svg className="w-full h-0.5" viewBox="0 0 651 2" fill="none"> <svg className="w-full h-0.5" viewBox="0 0 651 2" fill="none">
<path d="M0 1.14648H651" stroke="#E6E8EF" strokeDasharray="2 2" /> <path d="M0 1.14648H651" stroke="#E6E8EF" strokeDasharray="2 2" />
</svg> </svg>
<div className="flex flex-col gap-0"> <div className="flex gap-3">
<div className="flex items-center gap-2 py-1.5"> <div className="flex items-center gap-2 py-1.5">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<ClickIcon /> <ClickIcon />
<span className="text-body-m font-semibold text-grey-900">Beverly Hills Medical SPA</span> <span className="text-body-m font-semibold text-gray-900">Beverly Hills Medical SPA</span>
<div className="flex h-[21px] px-2 items-center rounded-full bg-[#E3FDE3]"> <div className="flex h-[21px] px-2 items-center rounded-full bg-[#E3FDE3]">
<span className="text-body-s font-semibold text-[#228116]">Brand Visibility</span> <span className="text-body-s font-semibold text-[#228116]">Brand Visibility</span>
</div> </div>
@@ -246,37 +720,34 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex items-center gap-2 py-1.5"> <div className="flex items-center gap-2 py-1.5">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<ClickIcon /> <ClickIcon />
<span className="text-body-m font-semibold text-grey-900">LA Medical SPA</span> <span className="text-body-m font-semibold text-gray-900">LA Medical SPA</span>
</div>
<div className="flex h-[21px] px-2 items-center rounded-full bg-[#E3FDE3]">
<span className="text-body-s font-semibold text-[#228116]">Brand Visibility</span>
</div> </div>
</div> </div>
</div> </div>
<div className="flex justify-center items-center py-0.5 px-2 rounded-md bg-grey-100"> <div className="flex justify-start items-center w-fit py-0.5 px-2 rounded-md bg-gray-100">
<p className="text-[11px] leading-4 text-grey-800">Looking strong. Like, "top of Google and AI mentions" strong.</p> <p className="text-[11px] leading-4 text-[#65677D]">Looking strong. Like, "top of Google and AI mentions" strong.</p>
</div> </div>
</div> </div>
<div className="flex flex-col md:flex-row gap-5"> <div className="flex flex-col md:flex-row gap-5">
<div className="flex flex-col gap-5 flex-1 w-1/2"> <div className="flex flex-col gap-5 flex-1 w-1/2">
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<div className="flex p-1.5 items-center rounded-lg border-[1.5px] border-grey-300 bg-white"> <div className="flex p-1.5 items-center rounded-lg border-[1.5px] border-gray-300 bg-white">
<CreditCardIcon /> <CreditCardIcon />
</div> </div>
<div className="flex flex-col gap-0 flex-1"> <div className="flex flex-col gap-0 flex-1">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-body-m font-semibold text-grey-900">Credit Card Info</span> <span className="text-body-m font-semibold text-gray-900">Credit Card Info</span>
</div> </div>
<p className="text-[11px] leading-4 text-grey-800">We keep it secure. Scout's honor.</p> <p className="text-[11px] leading-4 text-[#65677D]">We keep it secure. Scout's honor.</p>
</div> </div>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Name On Card</label> <label className="text-body-m font-semibold text-gray-900">Name On Card</label>
</div> </div>
<input <input
type="text" type="text"
@@ -284,14 +755,14 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
value={formData.nameOnCard} value={formData.nameOnCard}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter name..." placeholder="Enter name..."
className={`h-8 px-2.5 rounded-lg border ${errors.nameOnCard ? 'border-red-500' : 'border-grey-300'} bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500`} className={`h-8 px-2.5 rounded-lg border ${errors.nameOnCard ? 'border-red-500' : 'border-gray-300'} bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]`}
/> />
{errors.nameOnCard && <span className="text-xs text-red-500">{errors.nameOnCard}</span>} {errors.nameOnCard && <span className="text-xs text-red-500">{errors.nameOnCard}</span>}
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Card Number</label> <label className="text-body-m font-semibold text-gray-900">Card Number</label>
</div> </div>
<input <input
type="text" type="text"
@@ -299,7 +770,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
value={formData.cardNumber} value={formData.cardNumber}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="4*** **** **** ****" placeholder="4*** **** **** ****"
className={`h-8 px-2.5 rounded-lg border ${errors.cardNumber ? 'border-red-500' : 'border-grey-300'} bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500`} className={`h-8 px-2.5 rounded-lg border ${errors.cardNumber ? 'border-red-500' : 'border-gray-300'} bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]`}
/> />
{errors.cardNumber && <span className="text-xs text-red-500">{errors.cardNumber}</span>} {errors.cardNumber && <span className="text-xs text-red-500">{errors.cardNumber}</span>}
</div> </div>
@@ -307,7 +778,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex justify-between gap-2.5"> <div className="flex justify-between gap-2.5">
<div className="flex flex-col gap-1.5 flex-1 w-[40%]"> <div className="flex flex-col gap-1.5 flex-1 w-[40%]">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Expiration Date</label> <label className="text-body-m font-semibold text-gray-900">Expiration Date</label>
</div> </div>
<input <input
type="text" type="text"
@@ -315,13 +786,13 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
value={formData.expirationDate} value={formData.expirationDate}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="MM/YYYY" placeholder="MM/YYYY"
className={`h-8 px-2.5 rounded-lg border ${errors.expirationDate ? 'border-red-500' : 'border-grey-300'} bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500`} className={`h-8 px-2.5 rounded-lg border ${errors.expirationDate ? 'border-red-500' : 'border-gray-300'} bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]`}
/> />
{errors.expirationDate && <span className="text-xs text-red-500">{errors.expirationDate}</span>} {errors.expirationDate && <span className="text-xs text-red-500">{errors.expirationDate}</span>}
</div> </div>
<div className="flex flex-col gap-1.5 flex-1 w-[40%]"> <div className="flex flex-col gap-1.5 flex-1 w-[40%]">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Security Code</label> <label className="text-body-m font-semibold text-gray-900">Security Code</label>
</div> </div>
<input <input
type="text" type="text"
@@ -329,7 +800,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
value={formData.securityCode} value={formData.securityCode}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="CVC" placeholder="CVC"
className={`h-8 px-2.5 rounded-lg border ${errors.securityCode ? 'border-red-500' : 'border-grey-300'} bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500`} className={`h-8 px-2.5 rounded-lg border ${errors.securityCode ? 'border-red-500' : 'border-gray-300'} bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]`}
/> />
{errors.securityCode && <span className="text-xs text-red-500">{errors.securityCode}</span>} {errors.securityCode && <span className="text-xs text-red-500">{errors.securityCode}</span>}
</div> </div>
@@ -338,15 +809,15 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex flex-col gap-5 flex-1 w-1/2"> <div className="flex flex-col gap-5 flex-1 w-1/2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<div className="flex p-1.5 items-center rounded-lg border-[1.5px] border-grey-300 bg-white"> <div className="flex p-1.5 items-center rounded-lg border-[1.5px] border-gray-300 bg-white">
<CreditCardIcon /> <CreditCardIcon />
</div> </div>
<span className="text-base font-semibold text-grey-950">Address</span> <span className="text-base font-semibold text-[#272735]">Address</span>
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Billing Address</label> <label className="text-body-m font-semibold text-gray-900">Billing Address</label>
</div> </div>
<input <input
type="text" type="text"
@@ -354,7 +825,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
value={formData.billingAddress} value={formData.billingAddress}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Street" placeholder="Street"
className={`h-8 px-2.5 rounded-lg border ${errors.billingAddress ? 'border-red-500' : 'border-grey-300'} bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500`} className={`h-8 px-2.5 rounded-lg border ${errors.billingAddress ? 'border-red-500' : 'border-gray-300'} bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]`}
/> />
{errors.billingAddress && <span className="text-xs text-red-500">{errors.billingAddress}</span>} {errors.billingAddress && <span className="text-xs text-red-500">{errors.billingAddress}</span>}
</div> </div>
@@ -362,14 +833,14 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex gap-2.5"> <div className="flex gap-2.5">
<div className="flex flex-col gap-1.5 flex-1 w-[30%]"> <div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">City</label> <label className="text-body-m font-semibold text-gray-900">City</label>
</div> </div>
<div className="relative"> <div className="relative">
<select <select
name="city" name="city"
value={formData.city} value={formData.city}
onChange={handleInputChange} onChange={handleInputChange}
className="h-8 w-full px-2.5 pr-4 text-xs rounded-lg border border-grey-300 bg-white text-body-m text-grey-900 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500 appearance-none" className="h-8 w-full px-2.5 pr-4 text-xs rounded-lg border border-gray-300 bg-white text-body-m text-gray-900 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5] appearance-none"
> >
<option value="">Select City</option> <option value="">Select City</option>
{US_CITIES.map((city) => { {US_CITIES.map((city) => {
@@ -383,14 +854,14 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
</div> </div>
<div className="flex flex-col gap-1.5 flex-1 w-[30%]"> <div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">State</label> <label className="text-body-m font-semibold text-gray-900">State</label>
</div> </div>
<div className="relative"> <div className="relative">
<select <select
name="state" name="state"
value={formData.state} value={formData.state}
onChange={handleInputChange} onChange={handleInputChange}
className="h-8 w-full px-2.5 pr-4 text-xs rounded-lg border border-grey-300 bg-white text-body-m text-grey-900 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500 appearance-none" className="h-8 w-full px-2.5 pr-4 text-xs rounded-lg border border-gray-300 bg-white text-body-m text-gray-900 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5] appearance-none"
> >
<option value="">Select State</option> <option value="">Select State</option>
{US_STATES.map((state) => { {US_STATES.map((state) => {
@@ -404,7 +875,7 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
</div> </div>
<div className="flex flex-col gap-1.5 flex-1 w-[30%]"> <div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Zip</label> <label className="text-body-m font-semibold text-gray-900">Zip</label>
</div> </div>
<input <input
type="text" type="text"
@@ -415,33 +886,28 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
setFormData((prev) => ({ ...prev, zipCode: value })); setFormData((prev) => ({ ...prev, zipCode: value }));
}} }}
placeholder="12345" placeholder="12345"
className="h-8 px-2.5 rounded-lg border border-grey-300 bg-white text-body-m text-grey-900 placeholder:text-grey-600 shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-primary-500" className="h-8 px-2.5 rounded-lg border border-gray-300 bg-white text-body-m text-gray-900 placeholder:text-[#999BAD] shadow-[0_0_2px_0_rgba(5,11,18,0.12),0_1px_4px_-2px_rgba(24,39,75,0.02),0_4px_4px_-2px_rgba(24,39,75,0.06),0_0_0_1px_#E9EAF1] focus:outline-none focus:ring-2 focus:ring-[#4C60E5]"
/> />
</div> </div>
</div> </div>
<div className="flex items-center gap-2 py-1"> <div className="flex items-center gap-2 py-1">
{/* <button
type="button"
onClick={handleCheckboxChange}
className="w-[19px] h-[19px] flex items-center justify-center"
>
<div className={`w-[15px] h-[15px] rounded border ${formData.sameAsShipping ? 'border-[#1166DB] bg-primary-500' : 'border-grey-400 bg-white'} shadow-[0_0.5px_1px_0_rgba(24,63,118,0.20)] flex items-center justify-center transition-colors`}>
{formData.sameAsShipping && (
<svg width="10" height="8" viewBox="0 0 10 8" fill="none">
<path d="M1.42397 3.84361L4.0675 6.78535L8.57697 1.50757" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)}
</div>
</button> */}
<input type="checkbox" id="sameAsShipping" name="sameAsShipping" checked={formData.sameAsShipping} onChange={handleCheckboxChange} /> <input type="checkbox" id="sameAsShipping" name="sameAsShipping" checked={formData.sameAsShipping} onChange={handleCheckboxChange} />
<label htmlFor="sameAsShipping" className="text-body-m font-semibold text-grey-900">Same as shipping?</label> <label htmlFor="sameAsShipping" className="text-body-m font-semibold text-gray-900">Same as shipping?</label>
<span className="text-body-m text-grey-700">(Because Merch.)</span> <span className="text-body-m text-[#787B91]">(Because Merch.)</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
)
}
</div>
<div>
{
currentStep === 3 ? (
<>
<div className="flex flex-col"> <div className="flex flex-col">
<svg className="w-full h-0.5" viewBox="0 0 723 2" fill="none"> <svg className="w-full h-0.5" viewBox="0 0 723 2" fill="none">
<path d="M0 1.14648H723" stroke="#E9EDF1" /> <path d="M0 1.14648H723" stroke="#E9EDF1" />
@@ -449,8 +915,12 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex px-6 py-4"> <div className="flex px-6 py-4">
<button <button
onClick={handleSubmit} onClick={handleSubmit}
className="flex-1 px-4 py-3 rounded-[11px] border border-white bg-gradient-to-b from-[rgba(255,255,255,0.16)] to-[rgba(255,255,255,0.00)] bg-[#126CFF] shadow-[0_1.428px_1.428px_0_rgba(0,0,0,0.08),0_1.428px_4.285px_0_rgba(25,36,77,0.30),0_0_0_1.428px_#005FFA] text-white text-body-m font-semibold hover:bg-[#0052CC] transition-colors active:scale-[0.98]" className="flex w-full items-center justify-center gap-2.5 px-4 py-3 rounded-[11px] bg-gradient-to-b from-[rgba(255,255,255,0.16)] to-[rgba(255,255,255,0.00)] shadow-[0_1.428px_1.428px_0_rgba(0,0,0,0.08),0_1.428px_4.285px_0_rgba(25,36,77,0.30),0_0_0_1.428px_#005FFA] text-white text-body-m font-semibold hover:bg-[#0052CC] transition-colors active:scale-[0.98]"
style={{ background: 'linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.00) 60.94%), var(--Primary-color-primary-500, #4C60E5)' }}
> >
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M13.4368 9.71511L13.531 3.13945C13.5389 2.58142 12.8417 2.32251 12.4836 2.7505L4.32605 12.4985C4.00566 12.8813 4.27302 13.4646 4.7722 13.4717L9.67645 13.5419C10.0038 13.5466 10.2654 13.8158 10.2607 14.1432L10.1626 20.9918C10.1545 21.5609 10.8752 21.8133 11.2239 21.3635L19.4383 10.7677C19.7403 10.3782 19.4626 9.81167 18.9698 9.81167L16.7423 9.81167" stroke="white" stroke-width="1.77847" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Let's Light This Thing Up Let's Light This Thing Up
</button> </button>
</div> </div>
@@ -458,12 +928,29 @@ export default function KeywordConfirmationModal({ onClose }: KeywordConfirmatio
<div className="flex px-5 pb-5"> <div className="flex px-5 pb-5">
<div className="flex flex-col p-3 rounded-xl bg-[#FAF7DD] w-full"> <div className="flex flex-col p-3 rounded-xl bg-[#FAF7DD] w-full">
<p className="text-body-s leading-[14px] text-grey-950"> <p className="text-body-s leading-[14px] text-[#272735]">
<span className="font-bold">Sidenote: </span> <span className="font-bold">Sidenote: </span>
<span className="" style={{ fontFamily: 'Inter'}}>You now have access to unlimited website analyses. If you run multiple businesses or manage clients, you can handle everything from your dashboard, separate campaigns, organized by brand. It's like project management but fun.</span> <span className="" style={{ fontFamily: 'Inter' }}>You now have access to unlimited website analyses. If you run multiple businesses or manage clients, you can handle everything from your dashboard, separate campaigns, organized by brand. It's like project management but fun.</span>
</p> </p>
</div> </div>
</div> </div>
</>
) :
<div className="flex flex-col border-t border-[#E9EDF1]">
<div className="pt-5 pb-4 px-6">
<button
onClick={nextStep}
className="w-full bg-[#4C60E5] h-[46px] flex items-center justify-center gap-2.5 rounded-[11px] border border-white bg-gradient-to-b from-[rgba(255,255,255,0.16)] to-[rgba(255,255,255,0)] shadow-[0_1.428px_1.428px_0_rgba(0,0,0,0.08),0_1.428px_4.285px_0_rgba(25,36,77,0.30),0_0_0_1.428px_hsl(var(--primary-600))] hover:bg-primary-600 transition-colors"
>
<HeartIcon />
<span className="text-white font-semibold text-xl leading-7">Looks Good!</span>
</button>
</div>
</div>
}
</div>
</div> </div>
); );
} }

عرض الملف

@@ -8,9 +8,9 @@ export function ActionBadge({ type }: ActionBadgeProps) {
if (type === "Add-Keyword") { if (type === "Add-Keyword") {
return ( return (
<button className="flex items-center justify-center gap-0.5 px-1.5 py-0.5 rounded-md border border-grey-300 bg-white shadow-[0_1px_1px_0_rgba(5,19,65,0.07),0_1px_4px_-2px_rgba(24,39,75,0.02),0_0_2px_0_#E0E0E0] h-7 hover:bg-grey-50 transition-colors shrink-0 w-1/11"> <button className="flex items-center justify-center gap-0.5 px-1.5 py-0.5 rounded-md border border-gray-300 bg-white shadow-[0_1px_1px_0_rgba(5,19,65,0.07),0_1px_4px_-2px_rgba(24,39,75,0.02),0_0_2px_0_#E0E0E0] h-7 hover:bg-gray-50 transition-colors shrink-0 w-1/11">
<Plus className="w-4 h-4 text-grey-800" strokeWidth={1.5} /> <Plus className="w-4 h-4 text-gray-800" strokeWidth={1.5} />
<span className="text-xs font-semibold text-grey-900 leading-[18px]">Add Keyword</span> <span className="text-xs font-semibold text-gray-900 leading-[18px] whitespace-nowrap">Add Keyword</span>
</button> </button>
); );
} }

عرض الملف

@@ -75,7 +75,7 @@ const Header = () => {
aria-label="Close overlay" aria-label="Close overlay"
/> />
<div className="fixed inset-0 flex items-center justify-center z-[60] p-4 pointer-events-none"> <div className="fixed inset-0 flex items-center justify-center z-[60] p-4 pointer-events-none">
<div className="pointer-events-auto overflow-y-auto max-h-full"> <div className="pointer-events-auto overflow-y-auto max-w-[700px] lg:">
<KeywordConfirmationModal onClose={() => setShowOverlay(false)} /> <KeywordConfirmationModal onClose={() => setShowOverlay(false)} />
</div> </div>
</div> </div>