Skip to content

OUT-3662 | Auto-archive: workspace setting, schema & UI#1197

Merged
arpandhakal merged 18 commits intomainfrom
OUT-3662
May 8, 2026
Merged

OUT-3662 | Auto-archive: workspace setting, schema & UI#1197
arpandhakal merged 18 commits intomainfrom
OUT-3662

Conversation

@arpandhakal
Copy link
Copy Markdown
Collaborator

Summary

Part 1 of OUT-3496. Sets up persistence, configuration UI, and the API for the per-workspace auto-archive threshold. The Trigger.dev cron that consumes this setting is the second subtask (OUT-3663).

  • New WorkspaceSetting table — workspaceId unique, autoArchiveAfterDays Int @default(0). 0 = off; allowed non-zero values are 7, 14, 30, 60, 90.
  • New /api/workspace-settings route — GET (find-or-create) and PATCH (zod-validated to {0, 7, 14, 30, 60, 90}). IU-only via Resource.WorkspaceSetting.
  • Listing page renamed /manage-templates/configure-tasks-app (308 redirect kept for the listing). Detail route stays at /manage-templates/{templateId} per the design intent. Visible "Manage templates" copy on buttons/breadcrumbs unchanged.
  • New AutoArchiveSection — toggle + conditional days dropdown, owns the app-bridge "Save settings" primary CTA (replaces the old "Create template" CTA, which is now an inline "+ Add template" trigger in the Templates section header).

Test plan

  • Run prisma migrate deploy against the target environment and confirm WorkspaceSettings exists with autoArchiveAfterDays defaulting to 0.
  • Visit the configure tasks app page (as an IU) — section "Auto-archive" renders with toggle off, description text reads "Automatically archive completed tasks after 30 days".
  • Toggle on → dropdown appears showing 30 days, "Save settings" CTA appears in the parent app header.
  • Click Save settings → PATCH /api/workspace-settings is sent with { autoArchiveAfterDays: 30 }, row upserted, no error.
  • Change dropdown to other allowed values (7, 14, 60, 90), save → value persists across reload.
  • Toggle off → dropdown hides, save → row stores 0, reload reflects off state.
  • Manually PATCH with an invalid value (e.g. 5) → API returns 400.
  • Visit a stale /manage-templates URL → redirected to /configure-tasks-app. Stale /manage-templates/{templateId} URLs continue to work (no redirect, route still owns them).
  • Open a template detail from the listing — navigation lands on /manage-templates/{templateId} and breadcrumbs link back to /configure-tasks-app.
  • Confirm Client (non-IU) tokens cannot read or write the workspace setting.

🤖 Generated with Claude Code

@linear-code
Copy link
Copy Markdown

linear-code Bot commented Apr 28, 2026

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment May 8, 2026 11:03am

Request Review

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR introduces a per-workspace auto-archive threshold setting: a new WorkspaceSettings DB table, a GET/PATCH API route with Zod validation and IU-only access, and a configuration UI (AutoArchiveSection) living on the renamed /configure-tasks-app page (with a 308 redirect from /manage-templates).

  • P1getWorkspaceSettings uses a non-atomic find-then-create pattern; two concurrent first-time requests will collide on the unique constraint and return a 500. Use upsert instead (see inline comment on workspaceSettings.service.ts).

Confidence Score: 4/5

Safe to merge after fixing the race condition in the find-then-create GET path.

One P1 race condition in the service layer (find-then-create instead of upsert) that can cause a 500 on concurrent first access, capped at 4. All other changes — the migration, DTO schema, policy wiring, UI components, and route redirects — are clean and correct.

src/app/api/workspace-settings/workspaceSettings.service.ts — non-atomic find-then-create needs to be replaced with upsert.

Important Files Changed

Filename Overview
src/app/api/workspace-settings/workspaceSettings.service.ts New service with correct policy checks and upsert for writes, but the GET find-then-create path has a race condition that can cause a 500 on concurrent first-access.
src/app/api/workspace-settings/workspaceSettings.controller.ts Clean controller wiring: noStore on GET, Zod parse on PATCH, authentication on both paths.
src/app/configure-tasks-app/page.tsx Page renamed and extended to fetch workspace settings; missing res.ok guard on getWorkspaceSetting fetch.
src/app/configure-tasks-app/ui/AutoArchiveSection.tsx New client component managing toggle + days dropdown state and wiring to usePrimaryCta; logic is straightforward and correct.
prisma/migrations/20260428100247_add_workspace_settings_table/migration.sql Adds WorkspaceSettings table with unique index on workspaceId; schema and constraints look correct.
src/types/dto/workspaceSettings.dto.ts Clean Zod schema validating autoArchiveAfterDays against the allowed enum; shared between API and frontend.
next.config.js Adds permanent 308 redirect from /manage-templates to /configure-tasks-app; intentionally scoped to exact match only.
src/app/api/core/services/policies.service.ts Correctly registers WorkspaceSetting in the client policy with an empty allowed-actions array, blocking client token access.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Page as /configure-tasks-app (RSC)
    participant API as /api/workspace-settings
    participant DB

    Browser->>Page: GET (initial load)
    Page->>API: GET — find-or-create
    API->>DB: findUnique by workspaceId
    alt Row exists
        DB-->>API: WorkspaceSetting row
    else First access (race-prone path)
        DB-->>API: null
        API->>DB: create workspaceId
        DB-->>API: new row
    end
    API-->>Page: autoArchiveAfterDays
    Page-->>Browser: Renders AutoArchiveSection

    Browser->>API: PATCH autoArchiveAfterDays (Save settings)
    API->>DB: upsert workspaceSetting
    DB-->>API: updated row
    API-->>Browser: 200 OK
Loading

Comments Outside Diff (2)

  1. src/app/api/workspace-settings/workspaceSettings.service.ts, line 140-145 (link)

    P1 Race condition in find-then-create

    findUnique + create is not atomic. Two concurrent GET requests for a workspace that has no setting yet will both see null, both attempt create, and the second will throw a Prisma unique-constraint error, surfacing as a 500. Use upsert instead to make the initial materialisation idempotent:

    const workspaceSetting = await this.db.workspaceSetting.upsert({
      where: { workspaceId },
      create: { workspaceId },
      update: {},
    })
    return workspaceSetting
  2. src/app/configure-tasks-app/page.tsx, line 218-221 (link)

    P2 Missing res.ok check on workspace settings fetch

    getWorkspaceSetting calls res.json() unconditionally. If the endpoint returns an error status (e.g. invalid token, server error), the error body is silently passed to AutoArchiveSection as initial state, or throws with an unhelpful parse error. Every other data-fetcher in this file throws on non-OK responses. Add an equivalent guard before calling res.json().

Reviews (1): Last reviewed commit: "feat(OUT-3662): add workspace auto-archi..." | Re-trigger Greptile

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 28, 2026

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

@arpandhakal arpandhakal changed the title feat(OUT-3662): workspace auto-archive setting + configure-tasks-app page OUT-3662 | Auto-archive: workspace setting, schema & UI Apr 28, 2026
@arpandhakal arpandhakal changed the base branch from main to OUT-3606 April 29, 2026 04:06
@arpandhakal arpandhakal changed the base branch from OUT-3606 to OUT-3622 April 29, 2026 04:07
@arpandhakal arpandhakal changed the base branch from OUT-3622 to main April 29, 2026 08:47
priosshrsth
priosshrsth previously approved these changes May 7, 2026
@arpandhakal arpandhakal dismissed priosshrsth’s stale review May 7, 2026 06:35

The merge-base changed after approval.

priosshrsth
priosshrsth previously approved these changes May 7, 2026
priosshrsth
priosshrsth previously approved these changes May 8, 2026
Copy link
Copy Markdown
Collaborator

@priosshrsth priosshrsth left a comment

Choose a reason for hiding this comment

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

lgtm

arpandhakal and others added 18 commits May 8, 2026 16:47
… page

- new WorkspaceSetting table (workspaceId unique, autoArchiveAfterDays Int default 0)
- /api/workspace-settings GET (find-or-create) + PATCH (zod-validated to {0,7,14,30,60,90}); IU-only
- listing page /manage-templates renamed to /configure-tasks-app with permanent redirect; details page /manage-templates/{id} stays put
- new Auto-archive section on the listing page (toggle + days dropdown) wired through the app-bridge "Save settings" CTA
- Templates section gets an inline "+ Add template" trigger; in-page "Create template" primary CTA removed (auto-archive section now owns the primary CTA)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- replace custom Stack trigger with GhostBtn + GrayAddMediumIcon for the "Add template" affordance (the white-stroke PlusIcon was invisible on a light surface; this matches the in-app tertiary-button pattern used by "Create subtask")
- restyle the days dropdown: light EDEDF0 border (matches Autocomplete), no MUI focus ring, soft drop-shadow on the menu paper, no hover highlight on items, only the selected item has a subtle gray[100] background
- disable open/close and chevron-rotate animations on the dropdown
- drop the "Automatically archive completed tasks after 30 days" subtext from the toggle row

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… autoarchived in a cascading manner when their parent are elligible for autoarchived and the script runs
@arpandhakal arpandhakal merged commit c3a4f0b into main May 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants