Commit Graph
5 Commits
Author SHA1 Message Date
Claude 93ee4b82b8 fix(storage): validate expiration inputs inline on change, simplify error
- Call trigger() after each field.onChange so zod validation runs
  immediately as the user types, surfacing errors via FormMessage
- Simplify the S3 cap error message to 'Cannot exceed 100 versions'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 14:05:54 +00:00
Claude 54c9f84cb3 fix(storage): enforce S3 max 100 versions via zod, remove native HTML validation
- Add S3_MAX_NONCURRENT_VERSIONS constant (100) and enforce it in
  superRefineBucketProtection independently of plan limits
- Remove HTML min attributes from both inputs so only Supabase inline
  errors (FormMessage via FormItemLayout) appear — no browser tooltips
- Plan-specific bounds still enforced as before; the S3 cap is checked
  first with a distinct error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 13:53:56 +00:00
Claude 98e9e12a05 Redesign expiration policy section with sentence builder and inline toggle
Replace the standard form-field layout with the design-handoff UI:

- Sentence builder paragraph that adapts to which fields are filled
  (both empty → 'retained indefinitely', one filled → describes that
  condition, both filled → sentence with inline toggle)
- Inline [both | either] pill toggle embedded in the sentence text
  when both conditions are set (maps to ExpirationMode 'and' / 'or')
- Compact number inputs with suffix labels ('days' / 'max'),
  right-aligned text, and em-dash placeholder
- Collapsible 'See how this works' section with a dynamic example
  that uses the actual input values and updates on toggle
- Both fields are now optional — empty means 'no limit' for that
  condition. Validation only checks min/max when a value is provided.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-11 10:27:06 +00:00
Claude 3a0530d2d7 Add AND/OR expiration toggle, move deleted-versions toggle into header, update terminology
Task A: Add ExpirationMode (and/or) toggle between retention-days and
max-versions fields in bucket settings. 'and' = single S3 lifecycle policy
(both conditions must be met), 'or' = two independent rules (either triggers).
Wired into form schema, create/edit bucket modals, and mock store.

Task B: Move 'Show deleted versions' toggle from breadcrumb bar into the
file explorer table header, right of the search input. Switch is on the left
of the label text.

Task C: Add noncurrent version rows beneath each deleted object in TrashList.
Nested rows have left indentation with a CornerDownRight icon and lighter
background. Added DeletedObjectVersion interface and mock data to
protection-mocks.ts. Updated all user-facing terminology from 'files' to
'versions' across Trash, TrashList, TrashSelectionBar, DeletedFilesList,
DeletedFilesHeaderSelection, DeletedFilePreviewPane, and bucket page tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-07 11:03:16 +00:00
Claude 7b1dd94c79 use zod schema validation for object-versioning fields, not manual setError
- Move min/max/required validation for version_expiry_days and
  max_noncurrent_versions into a shared superRefine (BucketDataProtectionFields.schema.ts),
  called from each modal's own superRefine, instead of imperative
  validateVersioningFields() + form.setError() calls in onSubmit. Errors
  now surface the same way as the rest of the form (FormItemLayout/FormMessage),
  matching Studio's established form-error convention.
- Switch the fields to the '' empty-sentinel + z.coerce.number() union
  pattern (per the react-hook-form skill) instead of z.string().optional(),
  keeping inputs fully controlled without special-casing undefined.
- Build each modal's schema dynamically via useMemo(() => schema(planLimits), ...)
  since the versioning bounds depend on the async-loaded org plan.
- Replace destructured useFormContext().watch() with useWatch({ control, name })
  in BucketDataProtectionFields, avoiding the ratcheted no-use-watch lint warning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-08-05 15:55:16 +00:00