Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/(dashboard)/table-catalog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { ViewDialog, type ViewDialogMode } from "@/components/table-catalog/view
import { ViewDetailDialog } from "@/components/table-catalog/view-detail-dialog"
import { useDialog } from "@/lib/feedback/dialog"
import { useMessage } from "@/lib/feedback/message"
import { confirmTableBucketDisable } from "@/lib/table-catalog-disable"
import { copyToClipboard } from "@/lib/clipboard"
import { cn } from "@/lib/utils"
import { displayNamespace, resolveTableCatalogPrefix, TABLE_CATALOG_PREFIX } from "@/lib/table-catalog-paths"
Expand Down Expand Up @@ -101,6 +102,7 @@ export default function TableCatalogPage() {
getCatalogConfig,
getTableBucket,
enableTableBucket,
disableTableBucket,
listNamespaces,
dropNamespace,
listTables,
Expand Down Expand Up @@ -472,6 +474,19 @@ export default function TableCatalogPage() {
})
}

const handleDisableBucket = () => {
confirmTableBucketDisable({
bucket: selectedBucket,
canManage: canEnableBucket,
info: selectedInfo,
disable: disableTableBucket,
onDisabled: (bucket, info) => setBucketInfo((current) => ({ ...current, [bucket]: info })),
dialog,
message,
t,
})
}

const handleDeleteNamespace = React.useCallback(
(row: NamespaceSummary) => {
if (!canDeleteNamespace || !selectedBucket) return
Expand Down Expand Up @@ -925,6 +940,18 @@ export default function TableCatalogPage() {
) : selectedInfo ? (
<Badge variant="outline">{t("Not enabled")}</Badge>
) : null}
{selectedInfo?.enabled && selectedInfo.disableSupported ? (
<Button
variant="outline"
size="sm"
onClick={handleDisableBucket}
disabled={
!canEnableBucket || Boolean(bucketErrors[selectedBucket]) || bucketStatusLoading[selectedBucket]
}
>
{t("Disable table bucket")}
</Button>
) : null}
{selectedInfo?.enabled && activeTab === "tables" ? (
<Button
variant="default"
Expand Down
2 changes: 2 additions & 0 deletions docs/ui-review/register.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Use the resolved rows for the PR description:
| UI-030 | Bucket rule detail pages | Events, Replication, and Lifecycle rendered Add/Refresh in a second toolbar below the page header, separate from Back navigation. | Related page-level actions were split across two rows and displaced the table downward. | Added a tab header render seam and composed Back, Add, and Refresh together in the page header for all three scoped bucket pages. | `layout-followup-27` | Resolved |
| UI-031 | Top navigation account | The account artwork and its button trigger were both 32px high, so the circular avatar consumed the trigger's complete vertical space. | The avatar visually touched the trigger boundary and crowded adjacent top-navigation controls. | Reduced the artwork to 24px while retaining the 32px button target, leaving a visible 4px inset around the avatar. | `avatar-inset-28` | Resolved |
| UI-032 | Danger confirm buttons | `text-destructive-foreground` was never registered in `@theme inline`, so the solid `bg-destructive` confirm buttons in the SSE discard, stop-KMS, and force-delete dialogs emitted no CSS and inherited `text-popover-foreground` (black on red). Two other sites pushed `text-white` onto the tinted `destructive` Button variant, which removed its red label. | Destructive confirmations were unreadable in both themes: 4.2:1 in light mode and 2.8:1 in dark mode for the SSE dialogs, 1.2:1 for the shared `dialog.error` confirms and the object delete confirm. | Registered `--destructive-foreground` as a light label on the light-theme red and a dark label on the lightened dark-theme red (4.6:1 / 6.9:1), and routed every filled danger button through one shared `DANGER_BUTTON_CLASS` so the fill and its label can no longer drift apart. | `danger-button-32` pending | Resolved |
| UI-034 | Table bucket settings | Enabled table buckets had no Console action to disable an empty catalog. | Operators could not restore ordinary bucket behavior through the Console. | Expose a permission-checked action only when the server advertises support, confirm lifecycle consequences, and update state only after successful deletion. | `table-catalog-disable-34` | Resolved |

### Reconciled border rule

Expand Down Expand Up @@ -88,6 +89,7 @@ The 2026-07-11 follow-up reviewed 44 candidate files containing cards, dialogs,
- `avatar-inset-28` is a source-faithful static fixture for the exact 32px trigger and 32px-to-24px artwork change; source tests and component code remain authoritative.
- `danger-button-32` has no before/after capture yet. The implementation proof is `tests/lib/danger-button.test.ts` plus the compiled CSS, which emits no `.text-destructive-foreground` rule before the change and `color: var(--destructive-foreground)` after it. Capture light and dark dialogs for the SSE discard, stop-KMS, force-delete, object delete, and a `dialog.error` confirm before citing this key.
- `account-2fa-29` is a set of live runtime captures, not fixtures: a debug RustFS server with `RUSTFS_IAM_MASTER_KEY` set, this branch on `pnpm dev`, root identity, viewport 1585x1202 CSS px (the setup-dialog frame is 628 CSS px). It covers 2FA off/on, the setup QR, the recovery-code dismissal guard, the profile page, and the login second-factor step. It does **not** include a mobile viewport or a `main`-branch before-capture of the account menu; the responsive rules and the removed administrator gate are covered by source tests, which remain authoritative for those two.
- `table-catalog-disable-34` captures the running Next.js S3 Tables page at 1440x1000 with mocked catalog responses and an enabled bucket. The before capture uses `main`; the after capture uses the capability-gated disable action. Browser checks cover old-server compatibility, permissions, cancellation, conflict retry, disable and re-enable, and mobile/dark confirmation. These are Console checks with controlled responses; backend safety is verified separately.
- The full on-disk mapping is maintained in the screenshot manifest next to the image archive.

## Latest verification
Expand Down
13 changes: 13 additions & 0 deletions hooks/use-table-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface CatalogConfig {
export interface TableBucketInfo {
tableBucket: string
enabled: boolean
disableSupported: boolean
catalogType: string
warehouse: string
warehouseLocation: string
Expand Down Expand Up @@ -201,6 +202,7 @@ export function normalizeTableBucket(value: unknown, fallbackBucket = ""): Table
return {
tableBucket: stringValue(value, "table-bucket", "tableBucket") || fallbackBucket,
enabled: booleanValue(value, "enabled"),
disableSupported: booleanValue(value, "disable-supported", "disableSupported"),
catalogType: stringValue(value, "catalog-type", "catalogType"),
warehouse: stringValue(value, "warehouse") || fallbackBucket,
warehouseLocation: stringValue(value, "warehouse-location", "warehouseLocation"),
Expand Down Expand Up @@ -368,6 +370,16 @@ export function useTableCatalog(catalogPrefix: string = TABLE_CATALOG_PREFIX) {
[api, requestUrl],
)

const disableTableBucket = useCallback(
async (bucket: string) => {
const response = await api.delete(requestUrl(tableBucketCatalogPath(bucket, catalogPrefixRef.current)), {
suppress403Redirect: true,
})
return normalizeTableBucket(response, bucket)
},
[api, requestUrl],
)

const listNamespaces = useCallback(
async (bucket: string) => {
const items: NamespaceSummary[] = []
Expand Down Expand Up @@ -652,6 +664,7 @@ export function useTableCatalog(catalogPrefix: string = TABLE_CATALOG_PREFIX) {
getCatalogConfig,
getTableBucket,
enableTableBucket,
disableTableBucket,
listNamespaces,
createNamespace,
getNamespace,
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/ar-MA.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "تعطيل حاوية الجداول",
"Table bucket disabled": "تم تعطيل حاوية الجداول",
"Unable to disable table bucket.": "تعذر تعطيل حاوية الجداول.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "يمكن تعطيل كتالوج فارغ فقط. يتم الاحتفاظ بالكائنات الموجودة. راجع قواعد دورة حياة الحاوية قبل المتابعة: قد يُستأنف انتهاء الصلاحية ويحذف الكائنات.",
"Bucket is required": "الحاوية مطلوبة",
"Choose a suggested region or enter a new one.": "اختر منطقة مقترحة أو أدخل منطقة جديدة.",
"Compliance or Object Lock retention may prevent deletion.": "قد تمنع سياسة الاحتفاظ Compliance أو Object Lock الحذف.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Tabellen-Bucket deaktivieren",
"Table bucket disabled": "Tabellen-Bucket deaktiviert",
"Unable to disable table bucket.": "Tabellen-Bucket konnte nicht deaktiviert werden.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Nur ein leerer Katalog kann deaktiviert werden. Vorhandene Objekte bleiben erhalten. Prüfen Sie vor dem Fortfahren die Lebenszyklusregeln des Buckets: Die Ablaufverarbeitung kann wieder beginnen und Objekte löschen.",
"Bucket is required": "Bucket ist erforderlich",
"Choose a suggested region or enter a new one.": "Wählen Sie eine vorgeschlagene Region oder geben Sie eine neue ein.",
"Compliance or Object Lock retention may prevent deletion.": "Compliance- oder Object-Lock-Aufbewahrung kann das Löschen verhindern.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/en-US.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Disable table bucket",
"Table bucket disabled": "Table bucket disabled",
"Unable to disable table bucket.": "Unable to disable table bucket.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.",
"Bucket is required": "Bucket is required",
"Choose a suggested region or enter a new one.": "Choose a suggested region or enter a new one.",
"Compliance or Object Lock retention may prevent deletion.": "Compliance or Object Lock retention may prevent deletion.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/es-ES.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Desactivar bucket de tablas",
"Table bucket disabled": "Bucket de tablas desactivado",
"Unable to disable table bucket.": "No se pudo desactivar el bucket de tablas.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Solo se puede desactivar un catálogo vacío. Los objetos existentes se conservan. Revise las reglas del ciclo de vida del bucket antes de continuar: la caducidad puede reanudarse y eliminar objetos.",
"Bucket is required": "El bucket es obligatorio",
"Choose a suggested region or enter a new one.": "Elija una región sugerida o introduzca una nueva.",
"Compliance or Object Lock retention may prevent deletion.": "La retención de Compliance u Object Lock puede impedir la eliminación.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/fr-FR.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Désactiver le bucket de tables",
"Table bucket disabled": "Bucket de tables désactivé",
"Unable to disable table bucket.": "Impossible de désactiver le bucket de tables.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Seul un catalogue vide peut être désactivé. Les objets existants sont conservés. Vérifiez les règles de cycle de vie du bucket avant de continuer : l’expiration peut reprendre et supprimer des objets.",
"Bucket is required": "Le bucket est requis",
"Choose a suggested region or enter a new one.": "Choisissez une région suggérée ou saisissez-en une nouvelle.",
"Compliance or Object Lock retention may prevent deletion.": "La rétention Compliance ou Object Lock peut empêcher la suppression.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/id-ID.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Nonaktifkan bucket tabel",
"Table bucket disabled": "Bucket tabel dinonaktifkan",
"Unable to disable table bucket.": "Tidak dapat menonaktifkan bucket tabel.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Hanya katalog kosong yang dapat dinonaktifkan. Objek yang ada tetap disimpan. Tinjau aturan siklus hidup bucket sebelum melanjutkan: proses kedaluwarsa dapat dilanjutkan dan menghapus objek.",
"Bucket is required": "Bucket wajib diisi",
"Choose a suggested region or enter a new one.": "Pilih region yang disarankan atau masukkan yang baru.",
"Compliance or Object Lock retention may prevent deletion.": "Retensi Compliance atau Object Lock dapat mencegah penghapusan.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/it-IT.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Disattiva bucket delle tabelle",
"Table bucket disabled": "Bucket delle tabelle disattivato",
"Unable to disable table bucket.": "Impossibile disattivare il bucket delle tabelle.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "È possibile disattivare solo un catalogo vuoto. Gli oggetti esistenti vengono conservati. Controlla le regole del ciclo di vita del bucket prima di continuare: la scadenza può riprendere ed eliminare oggetti.",
"Bucket is required": "Il bucket è obbligatorio",
"Choose a suggested region or enter a new one.": "Scegli una regione suggerita o inseriscine una nuova.",
"Compliance or Object Lock retention may prevent deletion.": "La conservazione Compliance o Object Lock può impedire l'eliminazione.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/ja-JP.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "テーブルバケットを無効化",
"Table bucket disabled": "テーブルバケットを無効化しました",
"Unable to disable table bucket.": "テーブルバケットを無効化できませんでした。",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "空のカタログのみ無効化できます。既存のオブジェクトは保持されます。続行する前にバケットのライフサイクルルールを確認してください。有効期限処理が再開され、オブジェクトが削除される場合があります。",
"Bucket is required": "バケットは必須です",
"Choose a suggested region or enter a new one.": "候補のリージョンを選択するか、新しいリージョンを入力してください。",
"Compliance or Object Lock retention may prevent deletion.": "Compliance または Object Lock の保持設定により削除できない場合があります。",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/ko-KR.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "테이블 버킷 비활성화",
"Table bucket disabled": "테이블 버킷이 비활성화되었습니다",
"Unable to disable table bucket.": "테이블 버킷을 비활성화할 수 없습니다.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "비어 있는 카탈로그만 비활성화할 수 있습니다. 기존 객체는 보존됩니다. 계속하기 전에 버킷 수명 주기 규칙을 확인하세요. 만료 처리가 다시 시작되어 객체가 삭제될 수 있습니다.",
"Bucket is required": "버킷은 필수입니다",
"Choose a suggested region or enter a new one.": "추천 리전을 선택하거나 새 리전을 입력하세요.",
"Compliance or Object Lock retention may prevent deletion.": "Compliance 또는 Object Lock 보존 정책으로 인해 삭제되지 않을 수 있습니다.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/pt-BR.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Desativar bucket de tabelas",
"Table bucket disabled": "Bucket de tabelas desativado",
"Unable to disable table bucket.": "Não foi possível desativar o bucket de tabelas.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Somente um catálogo vazio pode ser desativado. Os objetos existentes são preservados. Revise as regras de ciclo de vida do bucket antes de continuar: a expiração pode ser retomada e excluir objetos.",
"Bucket is required": "O bucket é obrigatório",
"Choose a suggested region or enter a new one.": "Escolha uma região sugerida ou insira uma nova.",
"Compliance or Object Lock retention may prevent deletion.": "A retenção de Compliance ou Object Lock pode impedir a exclusão.",
Expand Down
4 changes: 4 additions & 0 deletions i18n/locales/ru-RU.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"Disable table bucket": "Отключить бакет таблиц",
"Table bucket disabled": "Бакет таблиц отключён",
"Unable to disable table bucket.": "Не удалось отключить бакет таблиц.",
"Only an empty catalog can be disabled. Existing objects are preserved. Review bucket lifecycle rules before continuing: expiration can resume and delete objects.": "Отключить можно только пустой каталог. Существующие объекты сохраняются. Перед продолжением проверьте правила жизненного цикла бакета: обработка срока хранения может возобновиться и удалить объекты.",
"Bucket is required": "Требуется бакет",
"Choose a suggested region or enter a new one.": "Выберите предложенный регион или введите новый.",
"Compliance or Object Lock retention may prevent deletion.": "Политика хранения Compliance или Object Lock может препятствовать удалению.",
Expand Down
Loading