Skip to content

Conversation

judah-sotomayor
Copy link
Contributor

@judah-sotomayor judah-sotomayor commented Sep 26, 2025

Add the toggles necessary for automatic builds.
Addresses #1235

Summary by CodeRabbit

  • New Features

    • Added project settings toggles for Auto Publish on Rebuild and Rebuild on Software Update.
    • Introduced a customizable Toggle with optional on/off icons and edit-disable state.
    • New ToggleForm enables immediate form submission when toggles change.
    • Expanded translations for en-US, es-419, and fr-FR to cover new settings and status messages.
  • Refactor

    • Replaced legacy Public/Private toggle with the new Toggle across the app.
    • Streamlined project settings forms to use ToggleForm, reducing duplication.
    • Simplified Settings usage by consolidating props and internalizing endpoint handling.

Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Introduces a reusable Toggle component and a ToggleForm wrapper, replaces PublicPrivateToggle usages, adds project toggles for Auto Publish and Rebuild on Software Update (UI + server actions), includes these fields in project details, updates project Settings form to use ToggleForm, simplifies project page props, and adds related locale strings.

Changes

Cohort / File(s) Summary
Toggle component + wrapper
src/lib/components/settings/Toggle.svelte, src/lib/components/settings/ToggleForm.svelte
Refactors Toggle: formNamename, adds canEdit, onIcon, offIcon, disabled binding, computed className. Adds ToggleForm to auto-submit a bound boolean via SvelteKit enhance with success/error toasts.
Project Settings refactor
src/routes/(authenticated)/projects/[id=idNumber]/forms/Settings.svelte
Replaces manual forms and PublicPrivateToggle with ToggleForm for visibility, downloads, auto-rebuild, and auto-publish; centralizes endpoints; binds local state; keeps canEdit gating.
Server actions for project toggles
src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts
Adds toggleAutoPublishOnRebuild and toggleRebuildOnSoftwareUpdate actions updating corresponding Project fields; returns { form, ok: true } or 400 on invalid form.
Project details shape
src/lib/projects/sse.ts
Extends getProjectDetails select to include AutoPublishOnRebuild and RebuildOnSoftwareUpdate.
Route usage updates (Toggle)
src/routes/(authenticated)/admin/settings/organizations/edit/+page.svelte, src/routes/(authenticated)/organizations/[id=idNumber]/settings/products/+page.svelte, src/routes/(authenticated)/projects/new/[id=idNumber]/+page.svelte
Replace PublicPrivateToggle with Toggle; rename formNamename; pass onIcon/offIcon; add inputAttr where applicable.
Project page props simplification
src/routes/(authenticated)/projects/[id=idNumber]/+page.svelte
Removes publicEndpoint and downloadEndpoint props from Settings usage; passes only project and canEdit.
Locales
src/lib/locales/en-US.json, src/lib/locales/es-419.json, src/lib/locales/fr-FR.json
Adds auto-publish/rebuild titles, descriptions, and action messages; normalizes some arrays to single-line; updates/reuses auto-rebuild keys; no removals.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant T as ToggleForm (Svelte)
  participant S as +page.server.ts (Action)
  participant DB as Project Store

  U->>T: Click toggle
  T->>T: Update bound formVar
  T->>T: enhance() intercepts submit
  T->>S: POST /toggle... { name: bool }
  S->>DB: Update field
  DB-->>S: Success/Failure
  S-->>T: { ok: true/false, form }
  alt ok == true
    T->>U: Show on/off toast
  else ok == false
    T->>T: Revert formVar
    T->>U: Show error toast
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • chrisvire
  • 7dev7urandom

Poem

Flip goes the switch with a hop and a grin,
Toggling rebuilds as releases begin.
Auto-publish hums—what a tidy delight!
Forms dance themselves, submitting just right.
A bunny approves, ears up, nose bright—
Click, toast, and ship it tonight! 🐇✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Feature/automatic builds project options” references the addition of toggles for automatic build settings but uses a branch-style prefix and does not fully capture the automatic publish toggles also introduced. It is partially related to the main changeset and refers to a real aspect of the update, though its phrasing and scope are somewhat non-standard. Thus it meets the minimal criteria by describing a relevant feature addition.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
Contributor

@FyreByrd FyreByrd left a comment

Choose a reason for hiding this comment

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

Overall, this looks good; there are just a few things that were lost when refactoring to Toggle.svelte.

  1. Any time that we used PublicPrivateToggle should still have the lock icons, unless @chrisvire or @7dev7urandom say otherwise. Similarly, there are some other places (I haven't actually looked) where you could probably use the new Toggle or ToggleForm.
  2. We recently made some changes (see #1275 ) that disable the ability to modify the settings on a project if a user is allowed to view, but not edit, the project. Those changes have disappeared entirely. (VS Code shows that the canEdit variable in /projects/[id]/forms/Settings.svelte as being completely unused).
  3. You will want to run npm run check and npm run lint. Many of the issues that I have pointed out in the comments are in fact found by running those commands.

Generalize the PublicPrivateToggle component into Toggle.
Use Toggle in the project settings page.
This will enable its use in settings.
Paper over the repetitive `form`s in settings using a new ToggleForm component.
Several PublicPrivateToggle uses sstill existed, replace them with just Toggle.
Add the internationalizations for auto publish/rebuild toggle notifications.
Add the routes for AutoPublishOnRebuild and RebuildOnSoftwareUpdate.
Remove the parameterization of endpoints, as Settings is not a good
abstraction already.
Add the new toggles and their endpoints.
Correct the field used by the server for the toggleRebuild path.
Restore the lock icon in locations where PublicPrivateToggle was
replaced with Toggle.
Replace ActionData with { ok: boolean } type.
@judah-sotomayor judah-sotomayor force-pushed the feature/automatic-builds-project-options branch from e782bf5 to a206027 Compare October 2, 2025 17:07
@judah-sotomayor
Copy link
Contributor Author

Rebased onto main.

@judah-sotomayor judah-sotomayor marked this pull request as ready for review October 2, 2025 17:09
Copy link
Contributor

@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: 3

♻️ Duplicate comments (2)
src/lib/components/settings/ToggleForm.svelte (1)

50-61: Past review concern appears resolved.

The cast to { ok: boolean } on line 51 addresses the previous feedback about ActionData. The type is now inline as suggested.

However, verify the revert logic on line 60: when !ok, formVar is toggled back. Since formVar has already been updated by the Toggle's onchange (line 72-74), the revert correctly undoes the optimistic update. The toast messages on lines 54 and 56 use the current (new) state of formVar to decide which message to show, which aligns with the expected behavior.

src/routes/(authenticated)/projects/[id=idNumber]/forms/Settings.svelte (1)

18-18: Verify that canEdit disables the toggle inputs.

The past review comment indicates that canEdit should disable form inputs. In the current implementation, canEdit is passed to each ToggleForm, which forwards it to the underlying Toggle component.

Ensure that the Toggle component properly disables the input when canEdit is false. This behavior is critical to prevent unauthorized modifications.

Run the following script to verify the Toggle component's handling of canEdit:

#!/bin/bash
# Check if Toggle component properly handles canEdit/disabled state

ast-grep --pattern $'export let canEdit$$$'

ast-grep --pattern $'disabled={$_}'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2b3114 and a206027.

📒 Files selected for processing (12)
  • src/lib/components/settings/Toggle.svelte (2 hunks)
  • src/lib/components/settings/ToggleForm.svelte (1 hunks)
  • src/lib/locales/en-US.json (8 hunks)
  • src/lib/locales/es-419.json (8 hunks)
  • src/lib/locales/fr-FR.json (2 hunks)
  • src/lib/projects/sse.ts (1 hunks)
  • src/routes/(authenticated)/admin/settings/organizations/edit/+page.svelte (2 hunks)
  • src/routes/(authenticated)/organizations/[id=idNumber]/settings/products/+page.svelte (2 hunks)
  • src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts (2 hunks)
  • src/routes/(authenticated)/projects/[id=idNumber]/+page.svelte (1 hunks)
  • src/routes/(authenticated)/projects/[id=idNumber]/forms/Settings.svelte (1 hunks)
  • src/routes/(authenticated)/projects/new/[id=idNumber]/+page.svelte (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts (1)
src/lib/server/database/index.ts (1)
  • DatabaseWrites (68-71)
🪛 GitHub Actions: Scriptoria v2 Test and Deploy
src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts

[error] 321-321: eslint: Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*() sveltekit-sec/require-security-check

🪛 GitHub Check: typecheck-lint
src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts

[failure] 337-337:
Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*()


[failure] 321-321:
Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*()

🔇 Additional comments (20)
src/lib/components/settings/ToggleForm.svelte (1)

66-80: LGTM!

The Toggle integration is clean: checked is bound to formVar, and the inputAttr.onchange handler triggers form submission. All necessary props are forwarded correctly.

src/lib/projects/sse.ts (1)

30-31: LGTM!

The addition of AutoPublishOnRebuild and RebuildOnSoftwareUpdate to the project select correctly exposes the new fields needed for the automatic builds feature.

src/routes/(authenticated)/projects/[id=idNumber]/+page.svelte (1)

243-243: LGTM!

Simplifying the Settings component props by removing publicEndpoint and downloadEndpoint is a good encapsulation refactor. The endpoints are now managed internally within Settings.

src/lib/locales/fr-FR.json (2)

368-372: LGTM!

The French locale additions for auto-publish and auto-rebuild features are properly structured and align with the new toggle functionality.


397-399: LGTM!

The action message translations for auto-publish on/off/error states are consistent with the existing pattern for other toggle features.

src/routes/(authenticated)/admin/settings/organizations/edit/+page.svelte (1)

103-111: LGTM!

The migration from PublicPrivateToggle to Toggle with the updated props (name, onIcon, offIcon) is consistent with the broader refactoring effort across the codebase.

src/routes/(authenticated)/projects/[id=idNumber]/forms/Settings.svelte (1)

33-82: LGTM!

The four ToggleForm usages follow a consistent pattern for handling project settings toggles. Each correctly binds to its state variable, specifies appropriate endpoints, and provides clear success/error messages.

The addition of lock icons (mdi:lock-open-variant, mdi:lock) for the visibility toggle enhances the UI clarity.

src/routes/(authenticated)/organizations/[id=idNumber]/settings/products/+page.svelte (1)

36-47: LGTM!

The migration to the Toggle component with updated props (name, onIcon, offIcon) is consistent with the broader refactoring pattern across the application.

src/routes/(authenticated)/projects/new/[id=idNumber]/+page.svelte (1)

8-8: LGTM!

The import has been correctly updated to use the new Toggle component instead of PublicPrivateToggle.

src/lib/locales/es-419.json (3)

23-61: LGTM!

The reformatting of localePicker entries from multi-line to single-line arrays preserves semantic content while improving readability.


245-254: LGTM!

The reformatting of products_numArtifacts declarations and selectors is consistent with the localePicker changes.


368-373: LGTM!

The new translation keys for auto-publish and auto-rebuild features are well-defined and align with the PR objectives. The Spanish translations are appropriate.

Also applies to: 398-400

src/lib/locales/en-US.json (3)

23-61: LGTM!

The reformatting of localePicker entries is consistent with the changes in es-419.json and preserves semantic content.


245-254: LGTM!

The reformatting of products_numArtifacts is consistent with the corresponding changes in es-419.json.


368-373: LGTM!

The new translation keys for auto-publish and auto-rebuild features are well-defined and align with the PR objectives. The English translations are clear and appropriate.

Also applies to: 401-403

src/lib/components/settings/Toggle.svelte (5)

7-17: LGTM!

The Props interface has been correctly updated to reflect the new API:

  • formName renamed to name
  • New props canEdit, onIcon, offIcon, and className added

These changes support the component's enhanced flexibility for different use cases.


19-29: LGTM!

The destructuring correctly handles the new props with appropriate defaults:

  • canEdit = true (enables editing by default)
  • onIcon = '' and offIcon = '' (no icons by default)

32-32: LGTM!

The conditional cursor-not-allowed styling correctly addresses the past review comment, applying the disabled cursor only when canEdit is false while preserving any custom className passed in.


35-42: LGTM!

The input element has been correctly updated:

  • name binding replaces formName
  • disabled attribute correctly bound to !canEdit

This ensures the toggle is properly disabled when canEdit is false.


49-56: LGTM!

The icon rendering has been correctly updated to use dynamic IconContainer components with configurable onIcon and offIcon props, replacing the hardcoded icons. This provides the flexibility needed for different toggle use cases.

Comment on lines +321 to +336
async toggleAutoPublishOnRebuild(event) {
// permissions checked in auth
const form = await superValidate(
event.request,
valibot(
v.object({
autoPublishOnRebuild: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
AutoPublishOnRebuild: form.data.autoPublishOnRebuild
});
return { form, ok: true };
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: Missing authorization check.

The toggleAutoPublishOnRebuild action lacks an explicit authorization check. While the comment on line 322 states "permissions checked in auth," there is no call to event.locals.security.requireProjectWriteAccess() or similar. This is inconsistent with other actions in the file (e.g., toggleVisibility at line 277-283) and could allow unauthorized users to modify project settings.

Apply this diff to add the required security check:

  async toggleAutoPublishOnRebuild(event) {
-   // permissions checked in auth
+   event.locals.security.requireProjectWriteAccess(
+     await DatabaseReads.projects.findUnique({
+       where: { Id: parseInt(event.params.id) },
+       select: { OwnerId: true, OrganizationId: true }
+     })
+   );
    const form = await superValidate(
📝 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
async toggleAutoPublishOnRebuild(event) {
// permissions checked in auth
const form = await superValidate(
event.request,
valibot(
v.object({
autoPublishOnRebuild: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
AutoPublishOnRebuild: form.data.autoPublishOnRebuild
});
return { form, ok: true };
},
async toggleAutoPublishOnRebuild(event) {
event.locals.security.requireProjectWriteAccess(
await DatabaseReads.projects.findUnique({
where: { Id: parseInt(event.params.id) },
select: { OwnerId: true, OrganizationId: true }
})
);
const form = await superValidate(
event.request,
valibot(
v.object({
autoPublishOnRebuild: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
AutoPublishOnRebuild: form.data.autoPublishOnRebuild
});
return { form, ok: true };
},
🧰 Tools
🪛 GitHub Actions: Scriptoria v2 Test and Deploy

[error] 321-321: eslint: Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*() sveltekit-sec/require-security-check

🪛 GitHub Check: typecheck-lint

[failure] 321-321:
Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*()

🤖 Prompt for AI Agents
In src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts around
lines 321 to 336, the toggleAutoPublishOnRebuild action is missing an explicit
authorization check; add a call to
event.locals.security.requireProjectWriteAccess(parseInt(event.params.id)) (or
the existing project write access helper used elsewhere in this file)
immediately after validating the form and before performing
DatabaseWrites.projects.update so the action enforces the same project write
permissions as other actions in the file.

Comment on lines +337 to +352
async toggleRebuildOnSoftwareUpdate(event) {
// permissions checked in auth
const form = await superValidate(
event.request,
valibot(
v.object({
autoRebuildOnSoftwareUpdate: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
RebuildOnSoftwareUpdate: form.data.autoRebuildOnSoftwareUpdate
});
return { form, ok: true };
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: Missing authorization check.

The toggleRebuildOnSoftwareUpdate action also lacks an explicit authorization check. This mirrors the issue in toggleAutoPublishOnRebuild and must be fixed to prevent unauthorized access.

Apply this diff to add the required security check:

  async toggleRebuildOnSoftwareUpdate(event) {
-   // permissions checked in auth
+   event.locals.security.requireProjectWriteAccess(
+     await DatabaseReads.projects.findUnique({
+       where: { Id: parseInt(event.params.id) },
+       select: { OwnerId: true, OrganizationId: true }
+     })
+   );
    const form = await superValidate(
📝 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
async toggleRebuildOnSoftwareUpdate(event) {
// permissions checked in auth
const form = await superValidate(
event.request,
valibot(
v.object({
autoRebuildOnSoftwareUpdate: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
RebuildOnSoftwareUpdate: form.data.autoRebuildOnSoftwareUpdate
});
return { form, ok: true };
},
async toggleRebuildOnSoftwareUpdate(event) {
event.locals.security.requireProjectWriteAccess(
await DatabaseReads.projects.findUnique({
where: { Id: parseInt(event.params.id) },
select: { OwnerId: true, OrganizationId: true }
})
);
const form = await superValidate(
event.request,
valibot(
v.object({
autoRebuildOnSoftwareUpdate: v.boolean()
})
)
);
if (!form.valid) return fail(400, { form, ok: false });
await DatabaseWrites.projects.update(parseInt(event.params.id), {
RebuildOnSoftwareUpdate: form.data.autoRebuildOnSoftwareUpdate
});
return { form, ok: true };
},
🧰 Tools
🪛 GitHub Check: typecheck-lint

[failure] 337-337:
Missing call to requireAuthenticated() or similar security check. This must be of the form locals.security.require*()

🤖 Prompt for AI Agents
In src/routes/(authenticated)/projects/[id=idNumber]/+page.server.ts around
lines 337 to 352, the toggleRebuildOnSoftwareUpdate action is missing an
explicit authorization check; copy the same authorization check used in
toggleAutoPublishOnRebuild into the start of this action (before any DB update
or form validation result handling), verifying the current user has permission
to modify the project identified by parseInt(event.params.id) and returning an
appropriate 403/fail response if unauthorized, then proceed with existing
validation and the DatabaseWrites.projects.update call.

Comment on lines +87 to 96
<Toggle
title={{ key: 'project_public' }}
message={{ key: 'project_visibilityDescription' }}
className="py-2 md:max-w-xs"
formName="IsPublic"
name="IsPublic"
inputAttr={{ onchange: () => {} }}
bind:checked={$form.IsPublic}
onIcon="mdi:lock-open-variant"
offIcon="mdi:lock"
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove the empty onchange handler.

The inputAttr prop contains an empty onchange handler () => {} on line 92, which serves no purpose and should be removed.

Apply this diff to remove the unnecessary handler:

         <Toggle
           title={{ key: 'project_public' }}
           message={{ key: 'project_visibilityDescription' }}
           className="py-2 md:max-w-xs"
           name="IsPublic"
-          inputAttr={{ onchange: () => {} }}
           bind:checked={$form.IsPublic}
           onIcon="mdi:lock-open-variant"
           offIcon="mdi:lock"
         />
📝 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
<Toggle
title={{ key: 'project_public' }}
message={{ key: 'project_visibilityDescription' }}
className="py-2 md:max-w-xs"
formName="IsPublic"
name="IsPublic"
inputAttr={{ onchange: () => {} }}
bind:checked={$form.IsPublic}
onIcon="mdi:lock-open-variant"
offIcon="mdi:lock"
/>
<Toggle
title={{ key: 'project_public' }}
message={{ key: 'project_visibilityDescription' }}
className="py-2 md:max-w-xs"
name="IsPublic"
bind:checked={$form.IsPublic}
onIcon="mdi:lock-open-variant"
offIcon="mdi:lock"
/>
🤖 Prompt for AI Agents
In src/routes/(authenticated)/projects/new/[id=idNumber]/+page.svelte around
lines 87 to 96, the Toggle component's inputAttr includes an empty onchange
handler (() => {}); remove this unused onchange property from inputAttr so the
prop no longer passes a no-op handler—leave inputAttr out entirely or only
include meaningful attributes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants