Files
supabase/apps/studio/components/interfaces/Storage/BucketDataProtectionFields.schema.ts
T
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

73 lines
2.4 KiB
TypeScript

import { z } from 'zod'
import type { ExpirationMode, VersioningPlanLimits } from './StorageProtection.constants'
/**
* Shared zod fields for the object-versioning controls in the create/edit
* bucket modals. `''` is the in-form empty sentinel (per the react-hook-form
* convention) so the number inputs stay fully deletable/controlled; it's
* coerced to a real number on parse.
*/
const versioningNumberField = z.union([z.literal(''), z.coerce.number().int()])
export const bucketProtectionFormFields = {
enable_versioning: z.boolean().default(false),
version_expiry_days: versioningNumberField.default(''),
max_noncurrent_versions: versioningNumberField.default(''),
expiration_mode: z.enum(['and', 'or']).default('and'),
}
export interface BucketProtectionFormValues {
enable_versioning: boolean
version_expiry_days: '' | number
max_noncurrent_versions: '' | number
expiration_mode: ExpirationMode
}
/**
* Validates the versioning fields against the org's plan limits. Call this
* from the parent modal's `.superRefine` so errors surface through the usual
* `FormItemLayout`/`FormMessage` rendering.
*/
export const superRefineBucketProtection = (
data: BucketProtectionFormValues,
ctx: z.RefinementCtx,
planLimits: VersioningPlanLimits | null
) => {
if (!data.enable_versioning || !planLimits) return
if (data.version_expiry_days === '') {
ctx.addIssue({
path: ['version_expiry_days'],
code: z.ZodIssueCode.custom,
message: 'Please provide a retention period',
})
} else if (
data.version_expiry_days < planLimits.minRetentionDays ||
data.version_expiry_days > planLimits.maxRetentionDays
) {
ctx.addIssue({
path: ['version_expiry_days'],
code: z.ZodIssueCode.custom,
message: `Must be between ${planLimits.minRetentionDays} and ${planLimits.maxRetentionDays} days on your plan`,
})
}
if (data.max_noncurrent_versions === '') {
ctx.addIssue({
path: ['max_noncurrent_versions'],
code: z.ZodIssueCode.custom,
message: 'Please provide a value',
})
} else if (
data.max_noncurrent_versions < planLimits.minVersions ||
data.max_noncurrent_versions > planLimits.maxVersions
) {
ctx.addIssue({
path: ['max_noncurrent_versions'],
code: z.ZodIssueCode.custom,
message: `Must be between ${planLimits.minVersions} and ${planLimits.maxVersions} versions on your plan`,
})
}
}