Files
supabase/apps/studio/components/interfaces/Storage/BucketDataProtectionFields.tsx
T
Claude a2761e82a6 refactor(studio): Snapshots/Trash as Files sub-tabs + unit inputs
- Move Snapshots and Trash out of the Storage sidebar (which lists bucket
  *types*: Files/Analytics/Vectors) into tabs under Files, at
  /storage/files/snapshots and /storage/files/trash — they're recovery views
  over file buckets, not a separate bucket type. Tabs added in
  StorageBucketsLayout; sidebar items reverted in StorageMenuV2.
- Use the design-system 'Input with unit' pattern (InputGroup +
  InputGroupAddon/InputGroupText) for the lifecycle day inputs in the bucket
  data-protection section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
2026-07-27 11:53:05 +00:00

161 lines
5.7 KiB
TypeScript

import { useState } from 'react'
import {
DialogSection,
DialogSectionSeparator,
Input,
InputGroup,
InputGroupAddon,
InputGroupInput,
InputGroupText,
Label,
Switch,
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { type BucketProtection, getMockBucketProtection } from './StorageProtection.constants'
interface BucketDataProtectionFieldsProps {
/** Existing bucket name (Edit) to pre-populate current protection config. */
bucketName?: string
}
/**
* "Data protection" section for the create/edit bucket modals — object
* versioning + bucket snapshots with their lifecycle policies.
*
* Prototype: this manages its own local state and is not yet persisted through
* the bucket mutation, so the enable/lifecycle UX can be demoed end-to-end.
*/
export const BucketDataProtectionFields = ({ bucketName }: BucketDataProtectionFieldsProps) => {
const initial: BucketProtection = getMockBucketProtection(bucketName)
const [versioning, setVersioning] = useState(initial.versioning === 'enabled')
const [versionExpiryDays, setVersionExpiryDays] = useState(initial.versionExpiryDays ?? 30)
const [maxNoncurrentVersions, setMaxNoncurrentVersions] = useState(
initial.maxNoncurrentVersions ?? 100
)
const [snapshots, setSnapshots] = useState(initial.snapshots)
const [snapshotOnBackup, setSnapshotOnBackup] = useState(initial.snapshotOnDatabaseBackup)
const [snapshotExpiryDays, setSnapshotExpiryDays] = useState(initial.snapshotExpiryDays ?? 90)
return (
<>
<DialogSectionSeparator />
<DialogSection className="space-y-3">
<div className="flex items-center justify-between gap-x-6">
<div className="flex flex-col">
<Label htmlFor="object-versioning">Object versioning</Label>
<p className="text-sm text-foreground-lighter">
Keep previous versions when objects are overwritten or deleted
</p>
</div>
<Switch
id="object-versioning"
size="large"
checked={versioning}
onCheckedChange={setVersioning}
/>
</div>
{versioning && (
<div className="flex flex-col gap-y-3 border-l border-border pl-4 pt-1">
<div className="flex items-center justify-between gap-x-4">
<Label htmlFor="version-expiry" className="font-normal text-foreground-light">
Expire noncurrent versions after
</Label>
<InputGroup className="w-40">
<InputGroupInput
id="version-expiry"
type="number"
min={1}
value={versionExpiryDays}
onChange={(e) => setVersionExpiryDays(e.target.valueAsNumber)}
/>
<InputGroupAddon align="inline-end">
<InputGroupText>days</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>
<div className="flex items-center justify-between gap-x-4">
<Label htmlFor="max-versions" className="font-normal text-foreground-light">
Max noncurrent versions
</Label>
<Input
id="max-versions"
type="number"
min={1}
max={100}
className="w-40"
value={maxNoncurrentVersions}
onChange={(e) => setMaxNoncurrentVersions(e.target.valueAsNumber)}
/>
</div>
</div>
)}
</DialogSection>
<DialogSectionSeparator />
<DialogSection className="space-y-3">
<div className="flex items-center justify-between gap-x-6">
<div className="flex flex-col">
<Label htmlFor="bucket-snapshots">Bucket snapshots</Label>
<p className="text-sm text-foreground-lighter">
Capture point-in-time restore points for the whole bucket
</p>
</div>
<Switch
id="bucket-snapshots"
size="large"
checked={snapshots}
onCheckedChange={setSnapshots}
/>
</div>
{snapshots && (
<div className="flex flex-col gap-y-3 border-l border-border pl-4 pt-1">
<div className="flex items-center justify-between gap-x-4">
<Label htmlFor="snapshot-on-backup" className="font-normal text-foreground-light">
Snapshot before each database backup
</Label>
<Switch
id="snapshot-on-backup"
size="large"
checked={snapshotOnBackup}
onCheckedChange={setSnapshotOnBackup}
/>
</div>
<div className="flex items-center justify-between gap-x-4">
<Label htmlFor="snapshot-expiry" className="font-normal text-foreground-light">
Keep snapshots for
</Label>
<InputGroup className="w-40">
<InputGroupInput
id="snapshot-expiry"
type="number"
min={1}
value={snapshotExpiryDays}
onChange={(e) => setSnapshotExpiryDays(e.target.valueAsNumber)}
/>
<InputGroupAddon align="inline-end">
<InputGroupText>days</InputGroupText>
</InputGroupAddon>
</InputGroup>
</div>
</div>
)}
{(versioning || snapshots) && (
<Admonition
type="warning"
title="Retained versions and snapshots add to your storage bill"
description="Objects held by a snapshot can't be deleted until the snapshot expires."
/>
)}
</DialogSection>
</>
)
}