diff --git a/.auto-claude-status b/.auto-claude-status index 272c89b..c2d956d 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -3,23 +3,23 @@ "spec": "017-replace-in-memory-rate-limiter-with-persistent-sol", "state": "building", "subtasks": { - "completed": 5, + "completed": 7, "total": 12, "in_progress": 1, "failed": 0 }, "phase": { - "current": "Add New Persistent Rate Limiter", + "current": "Migrate Contact Form", "id": null, - "total": 4 + "total": 2 }, "workers": { "active": 0, "max": 1 }, "session": { - "number": 3, + "number": 5, "started_at": "2026-01-25T11:52:31.718294" }, - "last_update": "2026-01-25T11:56:14.044309" + "last_update": "2026-01-25T12:07:33.420389" } \ No newline at end of file diff --git a/src/components/contact/ContactForm.tsx b/src/components/contact/ContactForm.tsx index cd87b6c..d15be49 100644 --- a/src/components/contact/ContactForm.tsx +++ b/src/components/contact/ContactForm.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { useTranslations } from 'next-intl'; import { motion } from 'framer-motion'; -import { MapPin, Phone, Mail, Send, Loader2, CheckCircle2 } from 'lucide-react'; +import { MapPin, Phone, Mail, Send, Loader2, CheckCircle2, AlertTriangle } from 'lucide-react'; import Image from 'next/image'; interface FormData { @@ -24,6 +24,7 @@ export function ContactForm() { message: '' }); const [errors, setErrors] = useState>({}); + const [remainingAttempts, setRemainingAttempts] = useState(null); const validateForm = () => { const newErrors: Partial = {}; @@ -76,12 +77,19 @@ export function ContactForm() { const data = await response.json(); + // Parse rate limit headers + const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining'); + if (rateLimitRemaining !== null) { + setRemainingAttempts(parseInt(rateLimitRemaining, 10)); + } + if (response.ok) { setSubmitStatus('success'); setFormData({ name: '', email: '', message: '' }); } else if (response.status === 429) { // Rate limit exceeded setSubmitStatus('error'); + setRemainingAttempts(0); const retryAfter = data.retryAfter || 0; const minutes = Math.ceil(retryAfter / 60); const hours = Math.floor(minutes / 60); @@ -95,7 +103,7 @@ export function ContactForm() { } setErrorMessage( - `Too many requests. Please try again in ${timeMessage}.` + t('contactForm.rateLimit.error', { time: timeMessage }) ); } else { // Other errors (400, 500, etc.) @@ -314,6 +322,20 @@ export function ContactForm() { + {/* Rate limit warning - show when attempts are low */} + {remainingAttempts !== null && remainingAttempts > 0 && remainingAttempts <= 2 && ( + + +

+ {t('contactForm.rateLimit.warning', { count: remainingAttempts })} +

+
+ )} + {submitStatus === 'success' && (