Skip to content

fix: loading state not visible when submitting contact form (#352)#417

Closed
adityashirsatrao007 wants to merge 1 commit into
Sachinchaurasiya360:mainfrom
adityashirsatrao007:fix/loading-state-form-submit-352
Closed

fix: loading state not visible when submitting contact form (#352)#417
adityashirsatrao007 wants to merge 1 commit into
Sachinchaurasiya360:mainfrom
adityashirsatrao007:fix/loading-state-form-submit-352

Conversation

@adityashirsatrao007

@adityashirsatrao007 adityashirsatrao007 commented May 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #352 - Submit button shows no loading indicator when form is being submitted.

Changes

Frontend (ContactPage.tsx)

  • Added contact form with name, email, subject, message fields
  • Loading state with Loader2 spinner from lucide-react shown on submit button during submission
  • Success and error feedback messages
  • Uses existing Button component following project patterns

Backend

  • Created /api/contact POST endpoint with Zod validation
  • Added contactSubmission Prisma model
  • Registered route in server entry point

Database

  • New model: contactSubmission — run npx prisma migrate dev after merging to apply

Summary by CodeRabbit

  • New Features
    • Added a fully functional contact form (name, email, subject, message) with inline validation, loading spinner, disabled submit while sending, and success/error callouts.
    • Form submissions are persisted server-side and return clear success or failure responses.
    • Contact page now includes a "Social Media" section with Twitter/X and LinkedIn action buttons.

Review Change Stack

Copilot AI review requested due to automatic review settings May 23, 2026 02:10
@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Client ContactPage becomes a controlled form with loading/error/success state and submits POST /api/contact; server adds Zod validation and an Express POST route that persists submissions to a new Prisma contactSubmission model.

Changes

Contact Form Submission Feature

Layer / File(s) Summary
Contact validation schema and types
server/src/module/contact/contact.validation.ts
Zod-based contactSchema validates name, email, subject, and message; exports ContactInput type.
Contact submission database model
server/src/database/prisma/schema/base.prisma
Adds contactSubmission Prisma model with autogenerated id, required contact fields, createdAt defaulting to now(), and an index on createdAt.
Contact API endpoint and server integration
server/src/module/contact/contact.routes.ts, server/src/index.ts
Adds contactRouter with POST / validating via contactSchema.safeParse, creating prisma.contactSubmission on success (201), returning 400 on validation failure and 500 on server error; router mounted at /api/contact.
Contact form component with submission and state management
client/src/module/legal/ContactPage.tsx
ContactPage now uses controlled formData, loading, success, error; handleChange updates fields; async handleSubmit POSTs to /api/contact, shows inline success/error banners, and disables the submit Button showing a spinner while sending. Social area updated to include X/Twitter and LinkedIn action buttons.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser (ContactPage)
  participant FrontendHandler as ContactPage.handleSubmit
  participant API as POST /api/contact
  participant Validator as contactSchema.safeParse
  participant Database as prisma.contactSubmission

  Browser->>FrontendHandler: User clicks submit (formData)
  FrontendHandler->>API: POST formData (name,email,subject,message)
  API->>Validator: Validate request body
  alt Validation success
    Validator-->>API: Parsed ContactInput
    API->>Database: create(parsed data)
    Database-->>API: Record created
    API-->>FrontendHandler: 201 success
    FrontendHandler->>Browser: show success banner, clear form
  else Validation failure
    Validator-->>API: issues
    API-->>FrontendHandler: 400 with issues
    FrontendHandler->>Browser: show validation error banner
  else Server error
    API-->>FrontendHandler: 500 error
    FrontendHandler->>Browser: show generic error banner
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement, gssoc:approved

Poem

🐰 A contact form hops into place,
Buttons spin gently, a courteous pace,
From client to server the messages flow,
Zod checks the fields so inputs can go.
Prisma stores them safe and sound—hooray! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary fix: adding a loading state indicator for the contact form submit button, which directly addresses the linked issue.
Linked Issues check ✅ Passed The PR fully implements the required functionality: adds a loading spinner on the submit button during form submission, integrates frontend form with backend endpoint, and provides appropriate user feedback.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the loading state issue: frontend form implementation with loading indicator, backend validation and storage, and database schema for contact submissions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an end-to-end “Contact Us” flow (client form → public API endpoint → DB persistence) with validation.

Changes:

  • Added Zod validation schema for contact submissions.
  • Added public /api/contact Express route that stores submissions via Prisma.
  • Updated the client Contact page to submit a contact form and show success/error UI.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
server/src/module/contact/contact.validation.ts Introduces Zod schema/type for validating contact payloads.
server/src/module/contact/contact.routes.ts Adds POST endpoint writing validated submissions to the database.
server/src/index.ts Wires the new contact router at /api/contact.
server/src/database/prisma/schema/base.prisma Adds Prisma model to persist contact submissions.
client/src/module/legal/ContactPage.tsx Implements contact form UI + submission logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const [success, setSuccess] = useState(false);
const [error, setError] = useState("");

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};

const handleSubmit = async (e: React.FormEvent) => {
Comment on lines +8 to +19
contactRouter.post("/", async (req: Request, res: Response) => {
try {
const parsed = contactSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ message: "Validation failed", errors: parsed.error.issues });
}
await prisma.contactSubmission.create({ data: parsed.data });
return res.status(201).json({ message: "Message sent successfully" });
} catch {
return res.status(500).json({ message: "Failed to send message" });
}
});
Comment on lines +4 to +7
name: z.string().min(1, "Name is required").max(100),
email: z.string().email("Invalid email address"),
subject: z.string().min(1, "Subject is required").max(200),
message: z.string().min(1, "Message is required").max(5000),
Comment on lines +1253 to +1262
model contactSubmission {
id Int @id @default(autoincrement())
name String
email String
subject String
message String
createdAt DateTime @default(now())

@@index([createdAt])
}
Comment on lines +79 to +91
{error && (
<div className="flex items-center gap-2 rounded-lg bg-red-50 dark:bg-red-900/20 px-3 py-2 text-xs text-red-600 dark:text-red-400">
<AlertCircle className="h-3.5 w-3.5 flex-shrink-0" />
{error}
</div>
)}

{success && (
<div className="flex items-center gap-2 rounded-lg bg-emerald-50 dark:bg-emerald-900/20 px-3 py-2 text-xs text-emerald-600 dark:text-emerald-400">
<CheckCircle className="h-3.5 w-3.5 flex-shrink-0" />
Message sent! We'll get back to you within 24-48 hours.
</div>
)}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/module/legal/ContactPage.tsx`:
- Around line 57-76: The inputs for name (id/name="name"), email
(id/name="email"), subject (id/name="subject") and the textarea message
(id/name="message") lack client-side maxLength limits; add maxLength attributes
on each element to mirror the backend schema caps (use the exact character
limits from the server schema) so the HTML inputs enforce the same constraints
before submit; update the JSX for <input id="name">, <input id="email">, <input
id="subject"> and <textarea id="message"> to include the appropriate maxLength
values.
- Line 81: In ContactPage JSX, replace the Tailwind v3 utility "flex-shrink-0"
with the Tailwind v4 canonical "shrink-0" on icon elements (e.g., the
AlertCircle icon and the other icon used in the same component) so the className
uses "shrink-0" instead of "flex-shrink-0"; update each icon element's className
accordingly to ensure consistent v4 utility naming.

In `@server/src/module/contact/contact.routes.ts`:
- Around line 8-19: The POST route declared with contactRouter.post("/")
currently bypasses the required route-layer middleware chain; update the route
to include the standard middleware sequence (auth, role, usageLimit, validation)
before the async handler so it uses the existing auth and role middlewares,
invokes usageLimit with the appropriate action (e.g.,
usageLimit("contactSubmission")), and runs validation via contactSchema (or the
app's validation middleware) instead of only calling contactSchema.safeParse
inside the handler; keep the handler logic (creating prisma.contactSubmission)
but remove inline validation and ensure the route uses the shared middleware
names (auth, role, usageLimit, validation) to conform with the
server/src/**/*.routes.ts guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0b3ceabd-9aed-43c3-b1ee-7828a854a8ad

📥 Commits

Reviewing files that changed from the base of the PR and between cfee527 and 7e1c577.

📒 Files selected for processing (5)
  • client/src/module/legal/ContactPage.tsx
  • server/src/database/prisma/schema/base.prisma
  • server/src/index.ts
  • server/src/module/contact/contact.routes.ts
  • server/src/module/contact/contact.validation.ts

Comment thread client/src/module/legal/ContactPage.tsx Outdated
Comment on lines +57 to +76
<input id="name" name="name" value={formData.name} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
<div>
<label htmlFor="email" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Email</label>
<input id="email" name="email" type="email" value={formData.email} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
</div>

<div>
<label htmlFor="subject" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Subject</label>
<input id="subject" name="subject" value={formData.subject} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>

<div>
<label htmlFor="message" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Message</label>
<textarea id="message" name="message" rows={5} value={formData.message} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 resize-y" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add client-side maxLength to match backend schema constraints.

These fields can exceed server limits and fail only after submit. Mirror backend caps in the inputs/textarea for immediate feedback.

💡 Suggested patch
-              <input id="name" name="name" value={formData.name} onChange={handleChange} required
+              <input id="name" name="name" value={formData.name} onChange={handleChange} required maxLength={100}
                 className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
@@
-            <input id="subject" name="subject" value={formData.subject} onChange={handleChange} required
+            <input id="subject" name="subject" value={formData.subject} onChange={handleChange} required maxLength={200}
               className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
@@
-            <textarea id="message" name="message" rows={5} value={formData.message} onChange={handleChange} required
+            <textarea id="message" name="message" rows={5} value={formData.message} onChange={handleChange} required maxLength={5000}
               className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 resize-y" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input id="name" name="name" value={formData.name} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
<div>
<label htmlFor="email" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Email</label>
<input id="email" name="email" type="email" value={formData.email} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
</div>
<div>
<label htmlFor="subject" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Subject</label>
<input id="subject" name="subject" value={formData.subject} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
<div>
<label htmlFor="message" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Message</label>
<textarea id="message" name="message" rows={5} value={formData.message} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 resize-y" />
<input id="name" name="name" value={formData.name} onChange={handleChange} required maxLength={100}
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
<div>
<label htmlFor="email" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Email</label>
<input id="email" name="email" type="email" value={formData.email} onChange={handleChange} required
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
</div>
<div>
<label htmlFor="subject" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Subject</label>
<input id="subject" name="subject" value={formData.subject} onChange={handleChange} required maxLength={200}
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" />
</div>
<div>
<label htmlFor="message" className="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">Message</label>
<textarea id="message" name="message" rows={5} value={formData.message} onChange={handleChange} required maxLength={5000}
className="w-full rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 px-3 py-2 text-sm text-gray-900 dark:text-white placeholder-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 resize-y" />
</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/legal/ContactPage.tsx` around lines 57 - 76, The inputs for
name (id/name="name"), email (id/name="email"), subject (id/name="subject") and
the textarea message (id/name="message") lack client-side maxLength limits; add
maxLength attributes on each element to mirror the backend schema caps (use the
exact character limits from the server schema) so the HTML inputs enforce the
same constraints before submit; update the JSX for <input id="name">, <input
id="email">, <input id="subject"> and <textarea id="message"> to include the
appropriate maxLength values.


{error && (
<div className="flex items-center gap-2 rounded-lg bg-red-50 dark:bg-red-900/20 px-3 py-2 text-xs text-red-600 dark:text-red-400">
<AlertCircle className="h-3.5 w-3.5 flex-shrink-0" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Tailwind CSS v4.x, is flex-shrink-0deprecated/removed in favor ofshrink-0?

💡 Result:

In Tailwind CSS v4.x, flex-shrink-0 is replaced by (and thus effectively deprecated/removed in favor of) shrink-0. Evidence: The Tailwind v4 docs list shrink-0 as the utility for flex-shrink: 0 under the “flex-shrink” utilities page [1]. A TailwindLabs pull request for v4 migration explicitly maps legacy flex-shrink-0 to the new shrink-0 class [2]. The Tailwind v3 discussion also notes the direction of renaming flex utilities from flex-grow-*/flex-shrink-* to grow-*/shrink-* [3]. So if you’re targeting v4.x, use shrink-0 instead of flex-shrink-0.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
# Print surrounding lines for both mentioned line numbers
sed -n '60,110p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 3686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '60,110p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 3686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '60,110p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 3686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '60,110p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 3686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '60,110p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 3686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '70,100p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 2094


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '70,95p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 1929


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '70,95p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 1929


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '70,95p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 1929


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '75,90p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 1222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/legal/ContactPage.tsx"
sed -n '75,90p' "$FILE" | cat -n

Repository: Sachinchaurasiya360/InternHack

Length of output: 1222


Use Tailwind v4 canonical class shrink-0 instead of flex-shrink-0
client/src/module/legal/ContactPage.tsx uses flex-shrink-0 on both icons; switch to shrink-0 to match Tailwind v4 utility naming.

💡 Suggested patch
-              <AlertCircle className="h-3.5 w-3.5 flex-shrink-0" />
+              <AlertCircle className="h-3.5 w-3.5 shrink-0" />
@@
-              <CheckCircle className="h-3.5 w-3.5 flex-shrink-0" />
+              <CheckCircle className="h-3.5 w-3.5 shrink-0" />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/legal/ContactPage.tsx` at line 81, In ContactPage JSX,
replace the Tailwind v3 utility "flex-shrink-0" with the Tailwind v4 canonical
"shrink-0" on icon elements (e.g., the AlertCircle icon and the other icon used
in the same component) so the className uses "shrink-0" instead of
"flex-shrink-0"; update each icon element's className accordingly to ensure
consistent v4 utility naming.

Comment on lines +8 to +19
contactRouter.post("/", async (req: Request, res: Response) => {
try {
const parsed = contactSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ message: "Validation failed", errors: parsed.error.issues });
}
await prisma.contactSubmission.create({ data: parsed.data });
return res.status(201).json({ message: "Message sent successfully" });
} catch {
return res.status(500).json({ message: "Failed to send message" });
}
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Route declaration skips the required middleware chain.

POST / is defined without the route-layer chain (auth/role/usage-limit/validation) mandated for *.routes.ts. Please align this route with the standard middleware structure (or apply an explicit, standardized public-route exemption pattern).

As per coding guidelines, server/src/**/*.routes.ts routes must include a middleware chain: “Routes layer must contain Express router with middleware chain (auth, role, usage-limit, validation)” and enforce daily limits via usageLimit(action).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/module/contact/contact.routes.ts` around lines 8 - 19, The POST
route declared with contactRouter.post("/") currently bypasses the required
route-layer middleware chain; update the route to include the standard
middleware sequence (auth, role, usageLimit, validation) before the async
handler so it uses the existing auth and role middlewares, invokes usageLimit
with the appropriate action (e.g., usageLimit("contactSubmission")), and runs
validation via contactSchema (or the app's validation middleware) instead of
only calling contactSchema.safeParse inside the handler; keep the handler logic
(creating prisma.contactSubmission) but remove inline validation and ensure the
route uses the shared middleware names (auth, role, usageLimit, validation) to
conform with the server/src/**/*.routes.ts guidelines.

…aurasiya360#352)

Add full contact form with loading indicator:
- Contact form with name, email, subject, message fields
- Loading state with Loader2 spinner on submit button
- Error and success feedback messages
- Backend contact endpoint with schema validation
- ContactSubmission Prisma model
@adityashirsatrao007 adityashirsatrao007 force-pushed the fix/loading-state-form-submit-352 branch from 7e1c577 to d271e09 Compare May 23, 2026 02:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
client/src/module/legal/ContactPage.tsx (2)

185-196: ⚡ Quick win

Use the shared Button component for these new social action buttons.

These are new button-like CTAs but implemented as styled anchors. Use Button with asChild for consistency with the design system.

💡 Suggested patch
-        <div className="mt-10 flex flex-wrap justify-center gap-4">
-          <a href="https://x.com/internhack_xyz" target="_blank" rel="noopener noreferrer"
-            className="px-5 py-3 rounded-2xl border border-gray-200 dark:border-gray-900 bg-white/70 dark:bg-[`#070707`] backdrop-blur-sm text-sm font-medium text-lime-500 dark:text-lime-400 hover:border-lime-400/40 dark:hover:border-lime-500/40 hover:shadow-[0_0_16px_rgba(163,230,53,0.05)] hover:bg-lime-500/5 transition-all">
-            Twitter / X
-          </a>
-          <a href="https://www.linkedin.com/company/internhack" target="_blank" rel="noopener noreferrer"
-            className="px-5 py-3 rounded-2xl border border-gray-200 dark:border-gray-900 bg-white/70 dark:bg-[`#070707`] backdrop-blur-sm text-sm font-medium text-lime-500 dark:text-lime-400 hover:border-lime-400/40 dark:hover:border-lime-500/40 hover:shadow-[0_0_16px_rgba(163,230,53,0.05)] hover:bg-lime-500/5 transition-all">
-            LinkedIn
-          </a>
-          <a href="mailto:mrsachinchaurasiya@gmail.com"
-            className="px-5 py-3 rounded-2xl border border-gray-200 dark:border-gray-900 bg-white/70 dark:bg-[`#070707`] backdrop-blur-sm text-sm font-medium text-lime-500 dark:text-lime-400 hover:border-lime-400/40 dark:hover:border-lime-500/40 hover:shadow-[0_0_16px_rgba(163,230,53,0.05)] hover:bg-lime-500/5 transition-all">
-            Email Support
-          </a>
+        <div className="mt-10 flex flex-wrap justify-center gap-4">
+          <Button asChild variant="mono" size="lg">
+            <a href="https://x.com/internhack_xyz" target="_blank" rel="noopener noreferrer">
+              Twitter / X
+            </a>
+          </Button>
+          <Button asChild variant="mono" size="lg">
+            <a href="https://www.linkedin.com/company/internhack" target="_blank" rel="noopener noreferrer">
+              LinkedIn
+            </a>
+          </Button>
+          <Button asChild variant="mono" size="lg">
+            <a href="mailto:mrsachinchaurasiya@gmail.com">
+              Email Support
+            </a>
+          </Button>
         </div>

As per coding guidelines, client/src/**/*.tsx should use the reusable Button component for all new buttons and use asChild when composing with other elements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/legal/ContactPage.tsx` around lines 185 - 196, Replace the
plain anchor CTAs in ContactPage.tsx with the shared Button component using the
asChild prop so the anchors become children of Button (preserve href, target,
rel and the existing className/styling or map to Button props like variant/size
if available); import Button at the top if missing and wrap each <a ...>Twitter
/ X</a>, <a ...>LinkedIn</a>, and <a ...>Email Support</a> inside <Button
asChild>...</Button> to keep semantics and design-system consistency.

40-41: 🏗️ Heavy lift

Replace arbitrary Tailwind utility values with canonical v4 classes.

This file repeatedly uses arbitrary utilities (for example dark:bg-[#070707] and hover:shadow-[...]), which breaks the canonical Tailwind v4 class rule.

As per coding guidelines, client/src/**/*.{tsx,ts} must use canonical TailwindCSS v4 classes only.

Also applies to: 60-60, 114-115, 149-150, 165-166, 186-194

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/legal/ContactPage.tsx` around lines 40 - 41, In
ContactPage.tsx replace all arbitrary Tailwind utility values in JSX className
strings (e.g., dark:bg-[`#070707`], hover:shadow-[...], and any bracketed
color/box-shadow sizes) with canonical Tailwind v4 classes; find these on the
root <div className=...>, the CTA/button elements, form containers and card
elements inside the ContactPage component and swap to standard classes like
dark:bg-black, bg-slate-900, shadow-md/shadow-lg, ring-1/ring-2, and the nearest
pre-defined color/spacing utilities so no bracketed/arbitrary values remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/src/module/contact/contact.validation.ts`:
- Around line 4-7: The string fields in the contact schema (name, subject,
message, and email) allow whitespace-only input because .min(1) runs before
trimming; update each z.string() for name, subject, message (and email if
desired) to trim input before validations (e.g., use
z.string().trim().min(...).max(...) or z.string().transform(s =>
s.trim()).min(...).max(...)) so leading/trailing whitespace is removed prior to
the .min check and empty/whitespace-only values are rejected; ensure the same
change is applied to all relevant schema properties in contact.validation.ts.

---

Nitpick comments:
In `@client/src/module/legal/ContactPage.tsx`:
- Around line 185-196: Replace the plain anchor CTAs in ContactPage.tsx with the
shared Button component using the asChild prop so the anchors become children of
Button (preserve href, target, rel and the existing className/styling or map to
Button props like variant/size if available); import Button at the top if
missing and wrap each <a ...>Twitter / X</a>, <a ...>LinkedIn</a>, and <a
...>Email Support</a> inside <Button asChild>...</Button> to keep semantics and
design-system consistency.
- Around line 40-41: In ContactPage.tsx replace all arbitrary Tailwind utility
values in JSX className strings (e.g., dark:bg-[`#070707`], hover:shadow-[...],
and any bracketed color/box-shadow sizes) with canonical Tailwind v4 classes;
find these on the root <div className=...>, the CTA/button elements, form
containers and card elements inside the ContactPage component and swap to
standard classes like dark:bg-black, bg-slate-900, shadow-md/shadow-lg,
ring-1/ring-2, and the nearest pre-defined color/spacing utilities so no
bracketed/arbitrary values remain.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c69c6a4-7bb6-4a2c-9dc6-f4c5acec17d1

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1c577 and d271e09.

📒 Files selected for processing (5)
  • client/src/module/legal/ContactPage.tsx
  • server/src/database/prisma/schema/base.prisma
  • server/src/index.ts
  • server/src/module/contact/contact.routes.ts
  • server/src/module/contact/contact.validation.ts

Comment on lines +4 to +7
name: z.string().min(1, "Name is required").max(100),
email: z.string().email("Invalid email address"),
subject: z.string().min(1, "Subject is required").max(200),
message: z.string().min(1, "Message is required").max(5000),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Trim user-input strings before minimum-length checks.

Whitespace-only values currently satisfy .min(1) and get accepted as valid content.

💡 Suggested patch
 export const contactSchema = z.object({
-  name: z.string().min(1, "Name is required").max(100),
-  email: z.string().email("Invalid email address"),
-  subject: z.string().min(1, "Subject is required").max(200),
-  message: z.string().min(1, "Message is required").max(5000),
+  name: z.string().trim().min(1, "Name is required").max(100),
+  email: z.string().trim().email("Invalid email address"),
+  subject: z.string().trim().min(1, "Subject is required").max(200),
+  message: z.string().trim().min(1, "Message is required").max(5000),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: z.string().min(1, "Name is required").max(100),
email: z.string().email("Invalid email address"),
subject: z.string().min(1, "Subject is required").max(200),
message: z.string().min(1, "Message is required").max(5000),
export const contactSchema = z.object({
name: z.string().trim().min(1, "Name is required").max(100),
email: z.string().trim().email("Invalid email address"),
subject: z.string().trim().min(1, "Subject is required").max(200),
message: z.string().trim().min(1, "Message is required").max(5000),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/src/module/contact/contact.validation.ts` around lines 4 - 7, The
string fields in the contact schema (name, subject, message, and email) allow
whitespace-only input because .min(1) runs before trimming; update each
z.string() for name, subject, message (and email if desired) to trim input
before validations (e.g., use z.string().trim().min(...).max(...) or
z.string().transform(s => s.trim()).min(...).max(...)) so leading/trailing
whitespace is removed prior to the .min check and empty/whitespace-only values
are rejected; ensure the same change is applied to all relevant schema
properties in contact.validation.ts.

@Sachinchaurasiya360 Sachinchaurasiya360 added gssoc:approved Approved for GSSoC scoring type:feature New feature implementation enhancement New feature or request level:intermediate Requires moderate project understanding labels May 23, 2026
@Sachinchaurasiya360

Copy link
Copy Markdown
Owner

Merged with two fixes: (1) replaced fetch('/api/contact') with api.post('/contact') to use the shared axios instance with correct base URL and interceptors; (2) added missing Prisma migration file 20260523000000_add_contact_submission/migration.sql for the contactSubmission model.

Sachinchaurasiya360 added a commit that referenced this pull request May 23, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sachinchaurasiya360 added a commit that referenced this pull request May 23, 2026
…mission (#417)

- Replace bare fetch('/api/contact') with api.post('/contact') to use
  the shared axios instance (correct base URL, auth headers, interceptors)
- Add missing Prisma migration for contactSubmission model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request gssoc:approved Approved for GSSoC scoring level:intermediate Requires moderate project understanding type:feature New feature implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Loading state not visible when submitting form

3 participants