add keyword modal confirmation

هذا الالتزام موجود في:
2025-10-21 17:53:04 +03:00
الأصل e7f4317ef2
التزام 0d5b0a82fd
3 ملفات معدلة مع 491 إضافات و3 حذوفات

عرض الملف

@@ -0,0 +1,469 @@
import { X } from "lucide-react";
import { useState, type ChangeEvent } from "react";
interface KeywordConfirmationModalProps {
onClose: () => void;
}
interface FormData {
nameOnCard: string;
cardNumber: string;
expirationDate: string;
securityCode: string;
billingAddress: string;
city: string;
state: string;
zipCode: string;
sameAsShipping: boolean;
}
interface FormErrors {
nameOnCard?: string;
cardNumber?: string;
expirationDate?: string;
securityCode?: string;
billingAddress?: string;
}
const US_STATES = [
"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware",
"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 = [
"New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio",
"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 = () => (
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_12168_25468)">
<path d="M10 12.6465V10.1465H11.25V5.14648H10V3.89648L15 1.39648L20 3.89648V5.14648H18.75V10.1465H20V12.6465H18.425L10.55 17.1965L11.25 12.6465H10ZM16.25 10.1465V5.14648H13.75V10.1465H16.25ZM8.75 28.8965L8.8 28.5965L20.1875 22.0215L20.8375 26.2465L16.25 28.8965H8.75ZM10.0625 20.359L19.1375 15.1465L19.7875 19.359L9.2875 25.4215L10.0625 20.359Z" fill="#65677D" />
<path opacity="0.3" d="M0 2.14648L13.5 5.14648L12.5 10.1465L0 11.6465V2.14648Z" fill="url(#paint0_linear_12168_25468)" />
<path opacity="0.3" d="M30.5 2.14648L17 5.14648L18 10.1465L30.5 11.6465V2.14648Z" fill="url(#paint1_linear_12168_25468)" />
</g>
<defs>
<linearGradient id="paint0_linear_12168_25468" x1="11.5" y1="6.64648" x2="-8.55457e-08" y2="6.0436" gradientUnits="userSpaceOnUse">
<stop stopColor="#65677D" />
<stop offset="1" stopColor="#65677D" stopOpacity="0" />
</linearGradient>
<linearGradient id="paint1_linear_12168_25468" x1="19" y1="6.64648" x2="30.5" y2="6.0436" gradientUnits="userSpaceOnUse">
<stop stopColor="#65677D" />
<stop offset="1" stopColor="#65677D" stopOpacity="0" />
</linearGradient>
<clipPath id="clip0_12168_25468">
<rect width="30" height="30" fill="white" transform="translate(0 0.146484)" />
</clipPath>
</defs>
</svg>
);
const ClickIcon = () => (
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.2109 11.7136L13.2402 12.0359L12.3047 12.3455L15.7422 15.783L15.1367 16.3884L11.6992 12.9509L11.3896 13.8865L11.0674 14.8572L9.71973 10.366L14.2109 11.7136ZM8.81445 2.65308C9.75998 2.65308 10.6418 2.83287 11.4658 3.18823C12.3015 3.54868 13.0221 4.03413 13.6318 4.64429C14.2417 5.25466 14.7274 5.97488 15.0889 6.8103C15.4268 7.5914 15.603 8.42492 15.6191 9.31616L15.3496 9.23315C15.2983 7.51646 14.6645 6.03536 13.4521 4.823C12.1865 3.55754 10.6281 2.92163 8.81445 2.92163C7.00079 2.92171 5.44237 3.55738 4.17676 4.823C2.91114 6.08862 2.27547 7.64703 2.27539 9.46069C2.27539 11.2743 2.9113 12.8328 4.17676 14.0984C5.38912 15.3108 6.87022 15.9445 8.58691 15.9958L8.66992 16.2654C7.77813 16.248 6.94468 16.0711 6.16406 15.7341C5.32939 15.3737 4.6089 14.8884 3.99805 14.2781C3.38748 13.668 2.90187 12.9479 2.54199 12.113C2.18703 11.2894 2.00729 10.407 2.00684 9.46069C2.00639 8.51493 2.1854 7.63345 2.54102 6.8103C2.90198 5.97492 3.38819 5.25464 3.99805 4.64429C4.60765 4.03423 5.32756 3.54866 6.16309 3.18823C6.98702 2.83286 7.86899 2.65312 8.81445 2.65308ZM8.81445 5.6189C9.8596 5.6189 10.7349 5.96891 11.4727 6.67456C11.9888 7.16835 12.3341 7.73413 12.5146 8.38159L12.1943 8.28589C11.9735 7.64188 11.591 7.09569 11.0488 6.66089C10.4058 6.1453 9.65275 5.88808 8.81543 5.88745H8.81445C7.83651 5.88754 6.98385 6.24247 6.29004 6.93628C5.59623 7.63009 5.2413 8.48275 5.24121 9.46069C5.24121 10.2983 5.49843 11.052 6.01465 11.6951C6.44975 12.237 6.99597 12.6197 7.63965 12.8406L7.73535 13.1609C7.08789 12.9803 6.52211 12.635 6.02832 12.1189C5.32267 11.3812 4.97266 10.5058 4.97266 9.46069C4.97274 8.38423 5.34295 7.48884 6.09277 6.73901C6.8426 5.98919 7.73799 5.61898 8.81445 5.6189Z" fill="#65677D" stroke="#65677D" strokeWidth="1.215" />
</svg>
);
const CreditCardIcon = () => (
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 6.14648C0 5.35083 0.316071 4.58777 0.87868 4.02516C1.44129 3.46255 2.20435 3.14648 3 3.14648H21C21.7956 3.14648 22.5587 3.46255 23.1213 4.02516C23.6839 4.58777 24 5.35083 24 6.14648V18.1465C24 18.9421 23.6839 19.7052 23.1213 20.2678C22.5587 20.8304 21.7956 21.1465 21 21.1465H3C2.20435 21.1465 1.44129 20.8304 0.87868 20.2678C0.316071 19.7052 0 18.9421 0 18.1465V6.14648ZM3 4.64648C2.60218 4.64648 2.22064 4.80452 1.93934 5.08582C1.65804 5.36713 1.5 5.74866 1.5 6.14648V7.64648H22.5V6.14648C22.5 5.74866 22.342 5.36713 22.0607 5.08582C21.7794 4.80452 21.3978 4.64648 21 4.64648H3ZM22.5 10.6465H1.5V18.1465C1.5 18.5443 1.65804 18.9258 1.93934 19.2071C2.22064 19.4884 2.60218 19.6465 3 19.6465H21C21.3978 19.6465 21.7794 19.4884 22.0607 19.2071C22.342 18.9258 22.5 18.5443 22.5 18.1465V10.6465Z" 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>
);
const DropdownArrow = () => (
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 4.14648L6 8.14648L10 4.14648" stroke="#65677D" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
export default function KeywordConfirmationModal({ onClose }: KeywordConfirmationModalProps) {
const [formData, setFormData] = useState<FormData>({
nameOnCard: "",
cardNumber: "",
expirationDate: "",
securityCode: "",
billingAddress: "",
city: "",
state: "",
zipCode: "",
sameAsShipping: true,
});
const [errors, setErrors] = useState<FormErrors>({});
const formatCardNumber = (value: string) => {
const numbers = value.replace(/\s/g, "");
const groups = numbers.match(/.{1,4}/g);
return groups ? groups.join(" ") : numbers;
};
const formatExpirationDate = (value: string) => {
const numbers = value.replace(/\D/g, "");
if (numbers.length >= 2) {
return numbers.slice(0, 2) + "/" + numbers.slice(2, 6);
}
return numbers;
};
const validateCardNumber = (number: string): boolean => {
const cleaned = number.replace(/\s/g, "");
if (cleaned.length !== 16) return false;
if (!cleaned.startsWith("4")) return false;
let sum = 0;
let isEven = false;
for (let i = cleaned.length - 1; i >= 0; i--) {
let digit = parseInt(cleaned[i]);
if (isEven) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
};
const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
let formattedValue = value;
if (name === "cardNumber") {
formattedValue = formatCardNumber(value.replace(/\s/g, "").slice(0, 16));
} else if (name === "expirationDate") {
formattedValue = formatExpirationDate(value);
} else if (name === "securityCode") {
formattedValue = value.replace(/\D/g, "").slice(0, 3);
}
setFormData((prev) => ({ ...prev, [name]: formattedValue }));
if (errors[name as keyof FormErrors]) {
setErrors((prev) => ({ ...prev, [name]: undefined }));
}
};
const handleCheckboxChange = () => {
setFormData((prev) => ({ ...prev, sameAsShipping: !prev.sameAsShipping }));
};
const validateForm = (): boolean => {
const newErrors: FormErrors = {};
if (!formData.nameOnCard.trim()) {
newErrors.nameOnCard = "Name is required";
}
if (!formData.cardNumber) {
newErrors.cardNumber = "Card number is required";
} else if (!validateCardNumber(formData.cardNumber)) {
newErrors.cardNumber = "Invalid Visa card number";
}
if (!formData.expirationDate) {
newErrors.expirationDate = "Expiration date is required";
} else {
const [month, year] = formData.expirationDate.split("/");
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth() + 1;
if (parseInt(month) > 12 || parseInt(month) < 1) {
newErrors.expirationDate = "Invalid month";
} else if (parseInt(year) < currentYear || (parseInt(year) === currentYear && parseInt(month) < currentMonth)) {
newErrors.expirationDate = "Card expired";
}
}
if (!formData.securityCode) {
newErrors.securityCode = "CVC is required";
} else if (formData.securityCode.length !== 3) {
newErrors.securityCode = "CVC must be 3 digits";
}
if (!formData.billingAddress.trim()) {
newErrors.billingAddress = "Billing address is required";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = () => {
if (validateForm()) {
console.log("Form submitted:", formData);
onClose();
}
};
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="flex h-[81px] flex-col justify-center items-end border-b border-grey-300 px-6 py-6">
<div className="flex justify-between items-start w-full">
<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">
<BeaconIcon />
</div>
<div className="flex flex-col gap-0.5">
<h2 className="text-base font-bold text-[#272735] leading-[22px]">Keyword Confirmation</h2>
</div>
</div>
<button
onClick={onClose}
className="w-7 h-7 flex items-center justify-center rounded hover:bg-grey-200 transition-colors"
aria-label="Close modal"
>
<X className="w-5 h-5 text-grey-800" />
</button>
</div>
</div>
<div className="flex flex-col gap-9 px-6 py-5">
<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>
<svg className="w-full h-0.5" viewBox="0 0 651 2" fill="none">
<path d="M0 1.14648H651" stroke="#E6E8EF" strokeDasharray="2 2" />
</svg>
<div className="flex flex-col gap-0">
<div className="flex items-center gap-2 py-1.5">
<div className="flex items-center gap-2">
<ClickIcon />
<span className="text-body-m font-semibold text-grey-900">Beverly Hills Medical SPA</span>
<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 className="flex items-center gap-2 py-1.5">
<div className="flex items-center gap-2">
<ClickIcon />
<span className="text-body-m font-semibold text-grey-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 className="flex justify-center items-center py-0.5 px-2 rounded-md bg-grey-100">
<p className="text-[11px] leading-4 text-grey-800">Looking strong. Like, "top of Google and AI mentions" strong.</p>
</div>
</div>
<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 items-start gap-2">
<div className="flex p-1.5 items-center rounded-lg border-[1.5px] border-grey-300 bg-white">
<CreditCardIcon />
</div>
<div className="flex flex-col gap-0 flex-1">
<div className="flex justify-between items-center">
<span className="text-body-m font-semibold text-grey-900">Credit Card Info</span>
</div>
<p className="text-[11px] leading-4 text-grey-800">We keep it secure. Scout's honor.</p>
</div>
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Name On Card</label>
</div>
<input
type="text"
name="nameOnCard"
value={formData.nameOnCard}
onChange={handleInputChange}
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`}
/>
{errors.nameOnCard && <span className="text-xs text-red-500">{errors.nameOnCard}</span>}
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Card Number</label>
</div>
<input
type="text"
name="cardNumber"
value={formData.cardNumber}
onChange={handleInputChange}
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`}
/>
{errors.cardNumber && <span className="text-xs text-red-500">{errors.cardNumber}</span>}
</div>
<div className="flex justify-between gap-2.5">
<div className="flex flex-col gap-1.5 flex-1 w-[40%]">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Expiration Date</label>
</div>
<input
type="text"
name="expirationDate"
value={formData.expirationDate}
onChange={handleInputChange}
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`}
/>
{errors.expirationDate && <span className="text-xs text-red-500">{errors.expirationDate}</span>}
</div>
<div className="flex flex-col gap-1.5 flex-1 w-[40%]">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Security Code</label>
</div>
<input
type="text"
name="securityCode"
value={formData.securityCode}
onChange={handleInputChange}
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`}
/>
{errors.securityCode && <span className="text-xs text-red-500">{errors.securityCode}</span>}
</div>
</div>
</div>
<div className="flex flex-col gap-5 flex-1 w-1/2">
<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">
<CreditCardIcon />
</div>
<span className="text-base font-semibold text-grey-950">Address</span>
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Billing Address</label>
</div>
<input
type="text"
name="billingAddress"
value={formData.billingAddress}
onChange={handleInputChange}
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`}
/>
{errors.billingAddress && <span className="text-xs text-red-500">{errors.billingAddress}</span>}
</div>
<div className="flex gap-2.5">
<div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">City</label>
</div>
<div className="relative">
<select
name="city"
value={formData.city}
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"
>
<option value="">Select City</option>
{US_CITIES.map((city) => {
return <option key={city} value={city}>{city}</option>;
})}
</select>
<div className="absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none">
<DropdownArrow />
</div>
</div>
</div>
<div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">State</label>
</div>
<div className="relative">
<select
name="state"
value={formData.state}
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"
>
<option value="">Select State</option>
{US_STATES.map((state) => {
return <option key={state} value={state}>{state}</option>;
})}
</select>
<div className="absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none">
<DropdownArrow />
</div>
</div>
</div>
<div className="flex flex-col gap-1.5 flex-1 w-[30%]">
<div className="flex justify-between items-center">
<label className="text-body-m font-semibold text-grey-900">Zip</label>
</div>
<input
type="text"
name="zipCode"
value={formData.zipCode}
onChange={(e) => {
const value = e.target.value.replace(/\D/g, "").slice(0, 5);
setFormData((prev) => ({ ...prev, zipCode: value }));
}}
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"
/>
</div>
</div>
<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} />
<label htmlFor="sameAsShipping" className="text-body-m font-semibold text-grey-900">Same as shipping?</label>
<span className="text-body-m text-grey-700">(Because Merch.)</span>
</div>
</div>
</div>
</div>
<div className="flex flex-col">
<svg className="w-full h-0.5" viewBox="0 0 723 2" fill="none">
<path d="M0 1.14648H723" stroke="#E9EDF1" />
</svg>
<div className="flex px-6 py-4">
<button
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]"
>
Let's Light This Thing Up
</button>
</div>
</div>
<div className="flex px-5 pb-5">
<div className="flex flex-col p-3 rounded-xl bg-[#FAF7DD] w-full">
<p className="text-body-s leading-[14px] text-grey-950">
<span className="font-bold">Sidenote: </span>
<span>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>
</div>
</div>
</div>
);
}

عرض الملف

@@ -36,7 +36,7 @@ export function KeywordSection({ section }: KeywordSectionProps) {
{isExpanded && section.keywords.length > 0 && (
<>
<div className="grid grid-cols-12 gap-4 items-center px-6 pr-[52px] py-1.5 bg-grey-100 border-b border-grey-300">
<div className="grid grid-cols-12 gap-4 items-center px-6 pr-[52px] py-1.5 bg-grey-100 border-b border-grey-300 transition-all duration-300">
<div className="col-span-2">
<span className="text-xs font-semibold text-grey-800 leading-[18px] ">Keyword</span>
</div>
@@ -69,7 +69,6 @@ export function KeywordSection({ section }: KeywordSectionProps) {
</div>
<div className="flex items-center gap-0.5">
<span className="text-xs font-semibold text-grey-800 leading-[18px]">Beacons</span>
<MetricTooltip extraTooltipText="Save to projects" tooltipText="first to enable monitoring and signal for Keywords" />
</div>
</div>

عرض الملف

@@ -22,6 +22,7 @@ import Logo from "./Logo";
import Vector from "../../assets/icons/Vector.svg";
import Vector2 from "../../assets/icons/Vector2.svg";
import { LayoutDashboard, LayoutDashboardLight, BookOpen, BookOpenLight, CalendarDays, CalendarDaysLight, KeywordsIcon, KeywordsIconLight, AnalysisIcon, AnalysisIconLight } from '../icons/icons-header.tsx';
import KeywordConfirmationModal from "../KeywordConfirmationModal.tsx";
// const CalendarDays = () => {
// return (
@@ -57,12 +58,30 @@ const Header = () => {
useEffect(() => {
setIsMenuOpen(false)
}, [location.pathname]);
const [showOverlay, setShowOverlay] = useState(false);
return (
<>
{isHeaderVisible && (
<header className="pb-20">
{showOverlay && (
<>
<div
className="fixed inset-0 bg-[#000B24] opacity-65 z-[60]"
onClick={() => setShowOverlay(false)}
role="button"
aria-label="Close overlay"
/>
<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">
<KeywordConfirmationModal onClose={() => setShowOverlay(false)} />
</div>
</div>
</>
)}
<nav className="bg-[#E6ECFB] backdrop-blur-md shadow-sm fixed top-0 left-0 right-0 z-50">
<div className="container mx-auto px-4 py-3 flex justify-between items-center">
<div className="flex items-center justify-between w-full md:w-auto">
@@ -106,7 +125,8 @@ const Header = () => {
<div className="hidden md:flex items-center space-x-4 relative">
{/* Add Keyword Button */}
<button className="flex items-center space-x-2 px-4 py-2 bg-blue-600 text-white rounded-xl font-medium shadow-md hover:bg-blue-700 transition-colors duration-200 ">
<button className="flex items-center space-x-2 px-4 py-2 bg-blue-600 text-white rounded-xl font-medium shadow-md hover:bg-blue-700 transition-colors duration-200 "
onClick={_ => setShowOverlay(true)}>
<Plus size={18} />
<span>Add Keywords</span>
</button>