Skip to content

Commit 289f62f

Browse files
Dirk Petersenclaude
andcommitted
fix: use Textual modal for tier change confirmation and clean console output
Replaced plain input() prompt with proper Textual TUI modal dialog for storage tier change confirmation. Fixed console output by removing rich markup tags that were displaying as literal text. Changes: - Added ScreenConfirmTierChange: Modal dialog with "Proceed"/"Cancel" buttons - Added TableStorageTierConfirmApp: Wrapper app to launch confirmation modal - Removed all rich markup tags ([bold], [green], [red], [yellow]) from console output in AWSBoto.change_storage_class() and Commands._change_storage_tier() - Replaced input() prompt with interactive Textual modal for better UX Now provides: - Clean console output without markup artifacts - Professional confirmation dialog matching Froster's UI style - Consistent user experience across all Textual TUI components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 08f3390 commit 289f62f

File tree

1 file changed

+112
-19
lines changed

1 file changed

+112
-19
lines changed

froster/froster.py

Lines changed: 112 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,15 +1923,15 @@ def change_storage_class(self, bucket_name, prefix, new_storage_class, current_s
19231923
# Validate that we're not moving FROM Glacier tiers
19241924
glacier_tiers = ['GLACIER', 'DEEP_ARCHIVE']
19251925
if current_storage_class in glacier_tiers:
1926-
log(f'\n[red]Error: Cannot change storage class from {current_storage_class}[/red]')
1926+
log(f'\nError: Cannot change storage class from {current_storage_class}')
19271927
log('Moving data FROM Glacier or Deep Archive is not allowed.')
19281928
log('Please restore the data first if you need to change its storage tier.\n')
19291929
return False, 0, 0, 0, 0
19301930

19311931
# Files that should remain in STANDARD tier
19321932
standard_files = ['Froster.allfiles.csv', '.froster.md5sum', '.froster-restored.md5sum']
19331933

1934-
log(f'\n[bold]CHANGING STORAGE TIER[/bold]')
1934+
log(f'\nCHANGING STORAGE TIER')
19351935
log(f' Bucket: {bucket_name}')
19361936
log(f' Prefix: {prefix}')
19371937
log(f' From: {current_storage_class}')
@@ -1982,10 +1982,10 @@ def change_storage_class(self, bucket_name, prefix, new_storage_class, current_s
19821982
log(f' Changed: {key}')
19831983

19841984
except Exception as e:
1985-
log(f' [yellow]Warning: Failed to change storage class for {key}: {e}[/yellow]')
1985+
log(f' Warning: Failed to change storage class for {key}: {e}')
19861986
skipped_objects += 1
19871987

1988-
log(f'\n [green]Storage class change completed[/green]')
1988+
log(f'\n Storage class change completed')
19891989
log(f' Total objects: {total_objects}')
19901990
log(f' Changed: {changed_objects}')
19911991
log(f' Skipped: {skipped_objects}')
@@ -6060,6 +6060,97 @@ async def load_data(self, searchstr):
60606060
return
60616061

60626062

6063+
class ScreenConfirmTierChange(ModalScreen[bool]):
6064+
"""Modal confirmation dialog for storage tier changes"""
6065+
6066+
DEFAULT_CSS = """
6067+
ScreenConfirmTierChange {
6068+
align: center middle;
6069+
}
6070+
6071+
ScreenConfirmTierChange > Vertical {
6072+
background: $secondary;
6073+
width: auto;
6074+
height: auto;
6075+
border: thick $primary;
6076+
padding: 2 4;
6077+
}
6078+
6079+
ScreenConfirmTierChange > Vertical > * {
6080+
width: auto;
6081+
height: auto;
6082+
}
6083+
6084+
ScreenConfirmTierChange > Vertical > Label {
6085+
padding-bottom: 1;
6086+
}
6087+
6088+
ScreenConfirmTierChange > Vertical > Horizontal {
6089+
align: center middle;
6090+
padding-top: 2;
6091+
}
6092+
6093+
ScreenConfirmTierChange Button {
6094+
margin: 0 2;
6095+
}
6096+
"""
6097+
6098+
def __init__(self, folder, current_tier, new_tier, object_count, total_size_gib):
6099+
super().__init__()
6100+
self.folder = folder
6101+
self.current_tier = current_tier
6102+
self.new_tier = new_tier
6103+
self.object_count = object_count
6104+
self.total_size_gib = total_size_gib
6105+
6106+
def compose(self) -> ComposeResult:
6107+
with Vertical():
6108+
yield Label("[bold]Confirm Storage Tier Change[/bold]")
6109+
yield Label("")
6110+
yield Label(f"Folder: {self.folder}")
6111+
yield Label(f"Current tier: {self.current_tier}")
6112+
yield Label(f"New tier: {self.new_tier}")
6113+
yield Label(f"Objects to change: {self.object_count}")
6114+
yield Label(f"Total size: {self.total_size_gib:.2f} GiB")
6115+
yield Label("")
6116+
yield Label("[yellow]This operation will change the storage class of all objects[/yellow]")
6117+
yield Label("[yellow](except metadata files which remain in STANDARD)[/yellow]")
6118+
6119+
with Horizontal():
6120+
yield Button("Proceed", id="yes", variant="primary")
6121+
yield Button("Cancel", id="no", variant="default")
6122+
6123+
def on_button_pressed(self, event: Button.Pressed) -> None:
6124+
self.dismiss(result=event.button.id == "yes")
6125+
6126+
6127+
class TableStorageTierConfirmApp(App[bool]):
6128+
"""Wrapper app to show the confirmation modal"""
6129+
6130+
def __init__(self, folder, current_tier, new_tier, object_count, total_size_gib):
6131+
super().__init__()
6132+
self.folder = folder
6133+
self.current_tier = current_tier
6134+
self.new_tier = new_tier
6135+
self.object_count = object_count
6136+
self.total_size_gib = total_size_gib
6137+
6138+
def on_mount(self) -> None:
6139+
self.push_screen(
6140+
ScreenConfirmTierChange(
6141+
self.folder,
6142+
self.current_tier,
6143+
self.new_tier,
6144+
self.object_count,
6145+
self.total_size_gib
6146+
),
6147+
self.handle_result
6148+
)
6149+
6150+
def handle_result(self, result: bool) -> None:
6151+
self.exit(result)
6152+
6153+
60636154
class TableStorageTierSelector(App[str]):
60646155
"""Interactive TUI for selecting AWS S3 storage tier with cost information"""
60656156

@@ -7318,7 +7409,7 @@ def _change_storage_tier(self, arch: Archiver, aws: AWSBoto, folders):
73187409
archive_info = arch.froster_archives_get_entry(folder)
73197410

73207411
if not archive_info:
7321-
log(f'\n[red]Error: Folder {folder} is not archived[/red]\n')
7412+
log(f'\nError: Folder {folder} is not archived\n')
73227413
return False
73237414

73247415
current_tier = archive_info['s3_storage_class']
@@ -7327,7 +7418,7 @@ def _change_storage_tier(self, arch: Archiver, aws: AWSBoto, folders):
73277418

73287419
# Check if folder is in Glacier/Deep Archive
73297420
if current_tier in ['GLACIER', 'DEEP_ARCHIVE']:
7330-
log(f'\n[red]Error: Cannot change storage tier from {current_tier}[/red]')
7421+
log(f'\nError: Cannot change storage tier from {current_tier}')
73317422
log('Moving data FROM Glacier or Deep Archive is not allowed.')
73327423
log('Please restore the data first if you need to change its storage tier.\n')
73337424
return False
@@ -7368,20 +7459,22 @@ def _change_storage_tier(self, arch: Archiver, aws: AWSBoto, folders):
73687459
new_tier = app.run()
73697460

73707461
if not new_tier:
7371-
log('\n[yellow]Storage tier change cancelled[/yellow]\n')
7462+
log('\nStorage tier change cancelled\n')
73727463
return False
73737464

7374-
# Confirm the change
7375-
log(f'\n[bold]Confirm Storage Tier Change[/bold]')
7376-
log(f' Folder: {folder}')
7377-
log(f' Current tier: {current_tier}')
7378-
log(f' New tier: {new_tier}')
7379-
log(f' Objects to change: {object_count}')
7380-
log(f' Total size: {total_size / (1024**3):.2f} GiB\n')
7465+
# Show confirmation modal
7466+
confirm_app = TableStorageTierConfirmApp(
7467+
folder=folder,
7468+
current_tier=current_tier,
7469+
new_tier=new_tier,
7470+
object_count=object_count,
7471+
total_size_gib=total_size / (1024**3)
7472+
)
7473+
7474+
confirmed = confirm_app.run()
73817475

7382-
response = input('Proceed with storage tier change? (yes/no): ')
7383-
if response.lower() not in ['yes', 'y']:
7384-
log('\n[yellow]Storage tier change cancelled[/yellow]\n')
7476+
if not confirmed:
7477+
log('\nStorage tier change cancelled\n')
73857478
return False
73867479

73877480
# Perform the storage class change
@@ -7393,14 +7486,14 @@ def _change_storage_tier(self, arch: Archiver, aws: AWSBoto, folders):
73937486
)
73947487

73957488
if not success:
7396-
log('\n[red]Storage tier change failed[/red]\n')
7489+
log('\nStorage tier change failed\n')
73977490
return False
73987491

73997492
# Update the local database
74007493
archive_info['s3_storage_class'] = new_tier
74017494
arch._archive_json_add_entry(key=folder.rstrip(os.path.sep), value=archive_info)
74027495

7403-
log(f'\n[green]Successfully changed storage tier[/green]')
7496+
log(f'\nSuccessfully changed storage tier')
74047497
log(f' Database updated: {arch.archive_json}\n')
74057498

74067499
return True

0 commit comments

Comments
 (0)