IMPLEMENT — MASOM CONTACT FORM WITH RESEND + CLOUDFLARE TURNSTILE + ADMIN CMS PROJECT: F:\xampp\htdocs\masom Repository: https://github.com/SrkJafferi/masonredesign.git Public Contact page: https://new.masom.com/contacts Admin: https://new.masom.com/admin IMPORTANT CONTEXT: The previous task accidentally implemented the Newsletter Subscribers module. DO NOT remove or rewrite that Newsletter work. This task is specifically for the PUBLIC CONTACT FORM at: /contacts The current Contact Form contains: - Your Name - Your E-mail - Message - Consent checkbox - Send Message button Current visible note says submitting opens the user's email app and nothing is stored on the website. That behavior now needs to be replaced with a real server-side form. ================================================== EXISTING ENVIRONMENT ================================================== The required environment configuration is already present in .env.local. Existing Resend configuration is already available. Cloudflare Turnstile variables are already configured: TURNSTILE_SECRET_KEY NEXT_PUBLIC_TURNSTILE_SITE_KEY DO NOT print, expose or hardcode secret values. ================================================== GOAL ================================================== Implement the /contacts form so that a successful submission: 1. Is validated server-side 2. Passes Cloudflare Turnstile verification 3. Is stored in Supabase/PostgreSQL 4. Sends an email notification through Resend 5. Appears in the Admin CMS 6. Can be reviewed and managed by MASOM administrators This should NOT open the user's local email application anymore. ================================================== 1. AUDIT CURRENT CONTACT FORM FIRST ================================================== Before implementation inspect the exact current Contact Form code. Determine: - component filename - current submit handler - whether it currently uses mailto: - whether any API/server action already exists - current field validation - current consent handling - whether honeypot/rate limiting exists - exact recipient currently mentioned in the UI Report the current behavior briefly before modifying it. Do NOT confuse this with the Newsletter form. ================================================== 2. PRESERVE CONTACT PAGE DESIGN ================================================== Do NOT redesign the Contact Form. Preserve: - heading - labels - input styling - textarea styling - spacing - button styling - typography - overall page layout Only add the required functional states and Turnstile cleanly. ================================================== 3. DATABASE TABLE ================================================== Create a proper migration for: contact_submissions Suggested schema: id uuid primary key default gen_random_uuid() name text not null email text not null message text not null consent boolean not null status text not null default 'new' source text not null default 'contacts-page' ip_hash text null user_agent text null submitted_at timestamptz not null default now() read_at timestamptz null replied_at timestamptz null created_at timestamptz not null default now() updated_at timestamptz not null default now() Use project conventions where appropriate. ================================================== 4. STATUS MODEL ================================================== Use a simple status model: new read replied archived Do not over-engineer this. Add a DB constraint so arbitrary values cannot be stored. ================================================== 5. RLS / PRIVACY ================================================== Contact messages contain personal information. RLS must be strict. Public/anon users: - must NOT SELECT contact submissions - must NOT UPDATE submissions - must NOT DELETE submissions Public form submission should happen through a protected server-side action. Admin users: - may SELECT - may update approved status fields Prefer no permanent DELETE initially. Do not expose the table publicly. ================================================== 6. CONTACT FORM SERVER ACTION ================================================== Create a dedicated server action such as: submitContactFormAction() or follow the project's existing naming convention. The browser must NOT directly insert into Supabase. The action must perform: - input validation - consent validation - honeypot validation - Turnstile verification - rate limiting - DB insertion - Resend notification ================================================== 7. VALIDATION ================================================== Reuse the project's existing schema strategy, preferably Zod if already used. Validate: NAME: - required - trim - sensible min/max length EMAIL: - required - valid email - trim - lowercase where appropriate - sensible max length MESSAGE: - required - trim - minimum sensible content length - reasonable maximum length CONSENT: - must be true Do not accept unexpected fields blindly. ================================================== 8. CLOUDFLARE TURNSTILE ================================================== Add Cloudflare Turnstile to the Contact Form. Client key: NEXT_PUBLIC_TURNSTILE_SITE_KEY Server verification key: TURNSTILE_SECRET_KEY The secret must remain server-only. Use the existing Turnstile implementation from the Donate form if the project already has one. Avoid creating duplicate Turnstile architecture. ================================================== 9. TURNSTILE CLIENT UX ================================================== Place Turnstile cleanly before the Send Message button or where it fits naturally. It must: - render responsively - not break mobile layout - generate a token - attach token to submission - reset after successful form submission - reset/recover appropriately after failed verification Keep the UI polished. ================================================== 10. TURNSTILE SERVER VERIFICATION ================================================== Server-side verification is mandatory. Never trust only the client widget. Verify the submitted token with Cloudflare before: - storing the message - sending the email If Turnstile fails: DO NOT insert into DB DO NOT send email Return a user-safe message such as: "Security verification failed. Please try again." Do not expose Turnstile internals. ================================================== 11. HONEYPOT ================================================== Add/reuse a hidden honeypot field. Bots filling it should be silently rejected or handled using the project's existing pattern. Do not make it visible to users. ================================================== 12. RATE LIMITING ================================================== Reuse the existing Donate form server-side rate-limiting pattern if available. Do NOT add a heavy infrastructure dependency. Rate-limit contact submissions reasonably by IP/request identity. Do not store raw IP permanently unless necessary. If tracking abuse information is needed, prefer a one-way hash. ================================================== 13. DATABASE FIRST / EMAIL SECOND ================================================== Important behavior: Once all validation and Turnstile checks pass: 1. Store the contact submission in DB 2. Attempt the Resend email notification The DB should remain the system of record. If Resend temporarily fails after DB insert: do NOT lose the submitted query. Return an appropriate response and log the email failure safely. Do not expose provider errors to the user. ================================================== 14. RESEND EMAIL NOTIFICATION ================================================== Use the existing server-side Resend setup. Do NOT create a second Resend client architecture if one already exists. Use the configured: RESEND_API_KEY RESEND_FROM_EMAIL For recipient: First inspect the current project's contact settings/env. The existing Contact Form UI currently references: secretary@masom.com If there is already a configurable contact notification email env var, use it. Otherwise introduce: CONTACT_NOTIFICATION_EMAIL with a safe fallback to: secretary@masom.com Do NOT hardcode multiple recipient definitions throughout the code. ================================================== 15. EMAIL CONTENT ================================================== Send a clean notification email. Suggested subject: New MASOM Website Contact Message Include: Name Email Message Submitted date/time Source Also include a clear Reply-To header using the submitter's email if supported by the existing Resend pattern. Do NOT put the user's email in the From header. ================================================== 16. EMAIL HTML ================================================== Use a professional lightweight HTML email. Example structure: MASOM New Contact Form Submission Name: ... Email: ... Message: ... Submitted: ... Also include a text fallback if the current Resend helper supports it. Do not add external tracking or unnecessary assets. ================================================== 17. REMOVE OLD MAILTO BEHAVIOR ================================================== The current form/message says: "Submitting opens your email app ... Nothing you type here is stored on this website." This will no longer be true. Remove/update that text. Replace with something accurate and concise, for example: "Your message will be securely submitted to the MASOM team." Do NOT claim the message was emailed if only DB storage succeeded. ================================================== 18. SUCCESS STATE ================================================== After successful DB submission: Show an inline confirmation such as: "Thank you. Your message has been submitted successfully." Do not use alert(). Clear: name email message consent and reset Turnstile. ================================================== 19. ERROR STATES ================================================== Provide friendly inline errors. Examples: Invalid email: "Please enter a valid email address." Consent: "Please agree before submitting." Turnstile: "Please complete the security verification." Generic: "We couldn't submit your message. Please try again." Do not show raw database/Resend/Turnstile errors. ================================================== 20. LOADING / DOUBLE SUBMISSION ================================================== During submission: - disable Send Message - show subtle pending feedback - prevent double submission Preserve current button design. ================================================== 21. ADMIN CMS MODULE ================================================== Add a new Admin CMS section. Preferred label: Contact Messages Route: /admin/contact-messages Do NOT use `/admin/contacts` if that may be confused with the public Contacts page architecture. Add it under the Content group. Suggested sidebar order: Banners Programs Announcements Calendar Newsletter Contact Messages Follow the existing Admin styling. ================================================== 22. ADMIN CONTACT MESSAGES PAGE ================================================== Page title: Contact Messages Subtitle: Messages submitted through the MASOM website contact form. Display a clean table. Suggested columns: Name Email Message Submitted Status Actions For desktop, truncate long message text. Allow full message viewing via: dialog drawer or detail panel Use whichever matches existing Admin patterns. ================================================== 23. MESSAGE DETAILS ================================================== Opening a message should show: Name Email Full message Status Submitted date/time Source Optionally: Mark as Read Mark as Replied Archive Do not expose internal IP hashes unless needed for troubleshooting. ================================================== 24. NEW / READ STATUS ================================================== New submissions should clearly show: New When an administrator opens/views the message, it may be marked: Read Do this explicitly and predictably. Do not mark messages read merely because the list page loaded. ================================================== 25. ADMIN ACTIONS ================================================== Support: Mark as New Mark as Read Mark as Replied Archive No permanent delete is necessary for the first version. Keep history. ================================================== 26. REPLY ACTION ================================================== A simple: Reply by Email action is useful. It can safely open: mailto: from the Admin only. This is acceptable as an Admin convenience. The PUBLIC form itself must NOT use mailto. ================================================== 27. SEARCH / FILTERING ================================================== Add lightweight search by: name email Add status filter: All New Read Replied Archived Keep implementation simple and performant. ================================================== 28. ADMIN COUNT BADGE ================================================== Add a sidebar badge for: new contact messages Prefer count where: status = 'new' Do not load the entire table just to compute a badge. Use a count query. ================================================== 29. DASHBOARD INTEGRATION ================================================== If the Admin Dashboard has Recent Activity and the project already logs CMS modules: add: contact or: contact-message to the approved activity module list. Log admin actions such as: Message marked read Message marked replied Message archived Do NOT log the full message body. If adding this requires updating the existing DB activity module constraint, use a tracked migration. ================================================== 30. CSV EXPORT ================================================== Optional but recommended: Add Admin-only: Export CSV Include: name email status source submitted_at message Ensure multiline messages are correctly escaped. Protect export using: requireAdmin() Do not make it public. ================================================== 31. ADMIN AUTHORIZATION ================================================== Every Admin Contact query/action must use: requireAdmin() or the current equivalent. Do not rely only on middleware/UI hiding. ================================================== 32. CACHE / FRESHNESS ================================================== A new Contact submission should appear in Admin on next load immediately. Admin status changes should revalidate: /admin/contact-messages and relevant sidebar/dashboard count surfaces. Do not globally disable site caching. ================================================== 33. NO PUBLIC DATA LEAK ================================================== Explicitly verify that anonymous/public clients cannot: SELECT UPDATE DELETE contact_submissions. This is mandatory. ================================================== 34. CONTACT SUBMISSION TESTS ================================================== TEST A: Valid: name email message consent valid Turnstile Expected: DB row created Resend notification attempted/sent Admin list contains row success UI TEST B: Turnstile missing Expected: no DB row no email TEST C: Invalid Turnstile Expected: no DB row no email TEST D: Consent unchecked Expected: no DB row TEST E: Invalid email Expected: no DB row TEST F: Honeypot filled Expected: blocked TEST G: Rate limit exceeded Expected: blocked safely TEST H: Email provider fails after DB insert Expected: contact submission remains stored safe user/admin handling no data loss ================================================== 35. RESEND TESTING ================================================== When testing Resend: Do not spam real recipients repeatedly. Send at most the minimum required test notification. Clearly identify it as a test if using the real MASOM inbox. Clean up test DB messages afterward if appropriate. ================================================== 36. TURNSTILE LOCAL TESTING ================================================== If Cloudflare Turnstile does not accept localhost with the configured site key: do not weaken production verification. Use the appropriate Cloudflare test behavior or test on the configured preview domain. Do NOT bypass Turnstile in production code. ================================================== 37. NEWSLETTER FEATURE ================================================== IMPORTANT: Do NOT modify or remove the Newsletter module that was implemented in the previous task. Newsletter and Contact Messages are separate systems: Newsletter: email subscriber list Contact Messages: name + email + message query Keep them separate. ================================================== 38. DONATE FORM ================================================== Do NOT break the existing Donate form. Reuse its patterns where useful: Resend Turnstile honeypot validation rate limiting But keep Contact submissions stored separately. ================================================== 39. SECURITY REVIEW ================================================== Verify no secret reaches the browser: TURNSTILE_SECRET_KEY RESEND_API_KEY SUPABASE_SERVICE_ROLE_KEY Only this may be browser-visible: NEXT_PUBLIC_TURNSTILE_SITE_KEY ================================================== 40. MIGRATIONS ================================================== All DB changes must be tracked. Expected migration(s): contact_submissions table RLS/policies/indexes activity module constraint change if required Do not make untracked production-only DB changes. ================================================== 41. VALIDATION ================================================== Run: npm run typecheck npm run lint npm run build ================================================== FINAL REPORT ================================================== MASOM CONTACT FORM + ADMIN CMS COMPLETE BEFORE: Old Contact behavior: Old mailto behavior removed: PASS/FAIL PUBLIC FORM: Server-side submission: PASS/FAIL Validation: PASS/FAIL Consent: PASS/FAIL Honeypot: PASS/FAIL Rate limiting: PASS/FAIL TURNSTILE: Widget rendered: PASS/FAIL Server verification: PASS/FAIL Missing/invalid token blocked: PASS/FAIL Secret server-only: PASS/FAIL DATABASE: contact_submissions migration: PASS/FAIL Submission storage: PASS/FAIL RLS: PASS/FAIL Anon SELECT blocked: PASS/FAIL Anon UPDATE blocked: PASS/FAIL Anon DELETE blocked: PASS/FAIL RESEND: Notification email: PASS/FAIL Correct recipient: PASS/FAIL Reply-To submitter: PASS/FAIL Email failure does not lose DB message: PASS/FAIL ADMIN: /admin/contact-messages: PASS/FAIL Table: PASS/FAIL Message details: PASS/FAIL Search: PASS/FAIL Status filter: PASS/FAIL New/Read/Replied/Archived actions: PASS/FAIL New-message badge count: PASS/FAIL requireAdmin protection: PASS/FAIL CSV export if implemented: PASS/FAIL REGRESSIONS: Newsletter module unchanged: PASS/FAIL Donate form unchanged: PASS/FAIL Public Contact design preserved: PASS/FAIL Admin shell preserved: PASS/FAIL QUALITY: TypeScript: PASS/FAIL Lint: PASS/FAIL Build: PASS/FAIL Also report: 1. Exact Contact Form behavior before implementation 2. Exact database migration filename(s) 3. Exact files created/changed 4. Exact Turnstile verification implementation 5. Exact Resend recipient/config used 6. Exact RLS policies 7. Whether any manual SQL/env step remains 8. Whether real test email was sent 9. Confirmation Newsletter work was not modified Do NOT commit. Do NOT push. STOP.