Skip to content

[#369] Close Blade Forms#386

Open
jesusthecreator017 wants to merge 5 commits intomainfrom
blade/close-forms
Open

[#369] Close Blade Forms#386
jesusthecreator017 wants to merge 5 commits intomainfrom
blade/close-forms

Conversation

@jesusthecreator017
Copy link
Contributor

@jesusthecreator017 jesusthecreator017 commented Mar 4, 2026

Why

Forms did NOT have the ability to close 🤯. This PR aims to add form closure to blade.

What

Closes: #369

Files Changed

  • packages/api/src/routers/forms.ts
  • packages/db/src/schemas/knight-hacks.ts
  • apps/blade/src/app/_components/admin/forms/editor/client.tsx
  • apps/blade/src/app/_components/forms/form-view-edit-client.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/forms/form-responses.tsx

Change Descriptions

  • Added isClosed to the FormsSchemas table (run pnpm db:push or use drizzle studio)
  • Added toggleFormsClosed api route
  • Added Form Settings dropdown to form editor
  • Added Is Closed form setting to form editor
  • Added "Form Closed" gate to the form responder
  • Adjusted Form-View-Edit-Client to only allow edit if the form is NOT closed
  • Added "Closed" Badge to dashboard form-responses

Test Plan

  • Created a test form to test the "Is Closed" functionality.
  • Tested "Is Closed" and "Allow Edit" flags together
  • Tested Multiple Forms with different edit settings

Images

Screenshot 2026-03-04 at 10 26 54 AM Screenshot 2026-03-04 at 10 27 14 AM

Checklist

  • Database: No schema changes, OR I have contacted the Development Lead to run db:push before merging
  • Environment Variables: No environment variables changed, OR I have contacted the Development Lead to modify them on Coolify BEFORE merging.

Summary by CodeRabbit

  • New Features

    • Administrators can toggle a "Form Closed" state; closed forms show a persistent "Closed" badge.
    • Form settings consolidated into a single dropdown with checkable options (Dues Only, Allow Multiple Responses, Allow Response Edit, Form Closed).
  • Bug Fixes

    • Closed forms block new submissions and edits; UI now shows a closed-state message and adjusts Edit/View actions accordingly.

Added isClosed state to Form Editor
Added "Form Closed" gate in form responder.
Added "Closed" tag to forms in dashboard.
Reformatted Form Options into Dropdown
Ran Format, Lint, and Typecheck
@jesusthecreator017 jesusthecreator017 added Feature New Feature or Request Blade Change modifies code in Blade app Database Change modifies code in the DB package API Change modifies code in the global API/tRPC package labels Mar 4, 2026
@jesusthecreator017 jesusthecreator017 self-assigned this Mar 4, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Warning

Rate limit exceeded

@jesusthecreator017 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 3 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 923808f3-b0fb-4741-8baa-0cda1a513914

📥 Commits

Reviewing files that changed from the base of the PR and between cd02c13 and 93e1ab9.

📒 Files selected for processing (1)
  • apps/blade/src/app/_components/forms/form-responder-client.tsx
📝 Walkthrough

Walkthrough

Adds a form-closure feature: new isClosed column in DB, admin UI to toggle closed state, API guards and mutation to toggle/reflect closure, and client-side guards/UI to show closed forms and prevent responses or edits when closed.

Changes

Cohort / File(s) Summary
Database Schema
packages/db/src/schemas/knight-hacks.ts
Added isClosed boolean column (notNull, default false) to FormsSchemas.
API Router & Endpoints
packages/api/src/routers/forms.ts
Added toggleFormClosed mutation; createResponse and editResponse now throw FORBIDDEN for closed forms; response projections include isClosed.
Admin Editor UI
apps/blade/src/app/_components/admin/forms/editor/client.tsx
Replaced multiple switches with a Cog dropdown containing checkable items (Dues Only, Allow Multiple Responses, Allow Response Edit, Form Closed); wired isClosed into init, autosave deps, and save payload.
Responder & View/Edit Clients
apps/blade/src/app/_components/forms/form-responder-client.tsx, apps/blade/src/app/_components/forms/form-view-edit-client.tsx
Responder short-circuits to a "Form Closed" UI when isClosed is true; allowEdit now requires form.allowEdit && !form.isClosed.
Dashboard Responses UI
apps/blade/src/app/_components/dashboard/member-dashboard/forms/form-responses.tsx
Renders a Closed badge when formResponse.isClosed; action label shows Edit only when allowEdit && !isClosed, otherwise View.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin (Editor)
    participant Server as API Server
    participant DB as Database
    participant Responder as Form Responder

    Admin->>Admin: Open form settings dropdown
    Admin->>Admin: Toggle "Form Closed"
    Admin->>Server: Save form { isClosed: true }
    Server->>DB: Update form.isClosed
    DB-->>Server: Confirmation
    Server-->>Admin: Return { isClosed: true }

    Note over Responder,Server: Later, user attempts to respond
    Responder->>Server: Fetch form (includes isClosed)
    Server->>DB: Query form by slug/id
    DB-->>Server: Return form (isClosed: true)
    Server-->>Responder: Return form data
    Responder->>Responder: Check isClosed guard
    Responder-->>Responder: Render "Form Closed" message, block submit
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Major, UI

Suggested reviewers

  • DVidal1205
🚥 Pre-merge checks | ✅ 7 | ❌ 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 (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title follows the required format with issue number [#369] and concise description, staying well under 72 characters.
Linked Issues check ✅ Passed All code changes comprehensively implement the stated objective: database schema addition (isClosed field), API mutation (toggleFormClosed), UI controls (Form Settings dropdown), responder gate, and badge display.
Out of Scope Changes check ✅ Passed All changes directly relate to the form-closing feature—no unrelated modifications or refactoring present outside the stated PR objectives.
No Hardcoded Secrets ✅ Passed No hardcoded secrets, API keys, passwords, tokens, or sensitive credentials detected in modified files. All sensitive data appropriately uses environment variables.
Validated Env Access ✅ Passed Modified files do not contain direct process.env usage, maintaining compliance with validated env access patterns.
No Typescript Escape Hatches ✅ Passed PR introduces no new TypeScript escape hatches; all new code is properly typed without any casts.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch blade/close-forms

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/blade/src/app/_components/forms/form-responder-client.tsx (1)

86-87: Short-circuit closed forms before non-essential query errors.

Line 110 adds the closed-state UI, but Line 78 can still return early with an unrelated existing-response error first. For closed forms, skip that query and prioritize a single closed-state render.

♻️ Suggested adjustment
   const existingResponseQuery = api.forms.getUserResponse.useQuery(
     { form: formIdGate },
-    { enabled: !!formIdGate },
+    { enabled: !!formIdGate && !formQuery.data?.isClosed },
   );
@@
-  // not found
-  if (existingResponseQuery.error)
-    return <div>Error Loading existing response</div>;
-
   const form = formQuery.data.formData as FORMS.FormType;
@@
   const isClosed = formQuery.data.isClosed;
+  if (isClosed) {
+    return (
+      <div className="flex min-h-screen items-center justify-center bg-primary/5 p-6">
+        <Card className="max-w-md p-8 text-center">
+          <XCircle className="mx-auto mb-4 h-16 w-16 text-destructive" />
+          <h1 className="mb-2 text-2xl font-bold">Form Closed</h1>
+          <p className="text-muted-foreground">
+            This form is no longer accepting responses.
+          </p>
+        </Card>
+      </div>
+    );
+  }
+
+  if (existingResponseQuery.error) return <div>Error loading form state</div>;

Based on learnings: "gating rendering should occur only when all required data fetches succeed... implement a unified loading/state or error handling that surfaces a single, coherent state once all data is ready or failed."

Also applies to: 110-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blade/src/app/_components/forms/form-responder-client.tsx` around lines
86 - 87, The component currently surfaces non-essential query errors (like
responseQuery failures) before rendering the closed-form UI; change the flow so
you derive isClosed from formQuery (guarding formQuery.data) and short-circuit
to render the closed-state block immediately when isClosed is true, and prevent
running responseQuery when the form is closed by disabling it (use the query's
enabled flag tied to !isClosed); update logic around formQuery, isClosed,
responseQuery and the closed-state render block so closed-state rendering takes
priority over unrelated query errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/blade/src/app/_components/admin/forms/editor/client.tsx`:
- Around line 579-581: The icon-only Button rendering the CogIcon in client.tsx
(the Button containing <CogIcon className="h-4 w-4" />) needs an accessible
name; add an aria-label (and optional title) such as aria-label="Form settings"
to the Button element so screen readers can identify the control and keep the
visual appearance unchanged.

In `@packages/api/src/routers/forms.ts`:
- Around line 1352-1358: The current toggleFormClosed implementation reads
form.isClosed then writes the inverted value, which can lose toggles under
concurrency; change the update to perform the flip atomically in the database by
issuing a single update on FormsSchemas that sets isClosed = NOT isClosed (or
equivalent DB boolean flip) and use returning() to get the new value instead of
separate read-then-write; apply the same change to the other toggle site
referenced (the block around the FormsSchemas update at 1367-1368) so both
locations use a single UPDATE ... SET isClosed = NOT isClosed RETURNING(*)
pattern and return the updated row.

---

Nitpick comments:
In `@apps/blade/src/app/_components/forms/form-responder-client.tsx`:
- Around line 86-87: The component currently surfaces non-essential query errors
(like responseQuery failures) before rendering the closed-form UI; change the
flow so you derive isClosed from formQuery (guarding formQuery.data) and
short-circuit to render the closed-state block immediately when isClosed is
true, and prevent running responseQuery when the form is closed by disabling it
(use the query's enabled flag tied to !isClosed); update logic around formQuery,
isClosed, responseQuery and the closed-state render block so closed-state
rendering takes priority over unrelated query errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 61b11d26-3f6e-4857-abe2-7c10ee03b3a7

📥 Commits

Reviewing files that changed from the base of the PR and between 9dabaf2 and 85bbea8.

📒 Files selected for processing (6)
  • apps/blade/src/app/_components/admin/forms/editor/client.tsx
  • apps/blade/src/app/_components/dashboard/member-dashboard/forms/form-responses.tsx
  • apps/blade/src/app/_components/forms/form-responder-client.tsx
  • apps/blade/src/app/_components/forms/form-view-edit-client.tsx
  • packages/api/src/routers/forms.ts
  • packages/db/src/schemas/knight-hacks.ts

@jesusthecreator017 jesusthecreator017 changed the title #369 Close Blade Forms [#369] Close Blade Forms Mar 4, 2026
Ran format, lint, and typecheck
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API Change modifies code in the global API/tRPC package Blade Change modifies code in Blade app Database Change modifies code in the DB package Feature New Feature or Request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ability to close Blade forms

1 participant