Skip to content

Commit 079b489

Browse files
committed
Fix Editor save flow consistency
- KeyDetailDrawer Apply button now stages changes instead of saving directly to DB - TranslateKey results are applied to drawer cells for review before staging - User must click main Save button to persist changes with optional snapshot - Renamed drawer button from Save to Apply to clarify behavior
1 parent 663992b commit 079b489

2 files changed

Lines changed: 84 additions & 47 deletions

File tree

cloud/src/LrmCloud.Web/Components/KeyDetailDrawer.razor

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,10 @@
197197
<MudButton Variant="Variant.Filled"
198198
Color="Color.Primary"
199199
Size="Size.Small"
200-
StartIcon="@Icons.Material.Filled.Save"
200+
StartIcon="@Icons.Material.Filled.Check"
201201
OnClick="@HandleSave"
202-
Disabled="@(!HasChanges || IsSaving)">
203-
@if (IsSaving)
204-
{
205-
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
206-
}
207-
Save
202+
Disabled="@(!HasChanges)">
203+
Apply
208204
</MudButton>
209205
</MudStack>
210206
</MudStack>
@@ -274,8 +270,6 @@
274270
[Parameter]
275271
public EventCallback<(TranslationGridRow Row, bool IsPlural)> OnTypeChanged { get; set; }
276272

277-
[Parameter]
278-
public bool IsSaving { get; set; }
279273

280274
[Inject]
281275
private ISnackbar Snackbar { get; set; } = default!;

cloud/src/LrmCloud.Web/Pages/Projects/Editor.razor

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ else
124124
OnSave="@SaveKeyDetails"
125125
OnTranslate="@TranslateKey"
126126
OnDelete="@DeleteKey"
127-
OnTypeChanged="@OnKeyTypeChanged"
128-
IsSaving="_isSavingKey" />
127+
OnTypeChanged="@OnKeyTypeChanged" />
129128
}
130129
</MudDrawerContainer>
131130
</MudDrawer>
@@ -177,7 +176,6 @@ else
177176
private bool _isLoading = true;
178177
private bool _isRefreshing;
179178
private bool _isSaving;
180-
private bool _isSavingKey;
181179
private bool _hasUnsavedChanges;
182180
private bool _drawerOpen;
183181
private TranslationGridRow? _selectedRow;
@@ -663,53 +661,98 @@ else
663661
await RefreshData();
664662
}
665663

666-
private async Task SaveKeyDetails(TranslationGridRow row)
664+
private void SaveKeyDetails(TranslationGridRow row)
667665
{
668-
_isSavingKey = true;
669-
try
666+
// Stage changes in the editor (don't save to DB yet)
667+
// User must click the main Save button to persist with optional snapshot
668+
669+
// Ensure row is tracked in edited rows
670+
if (!_editedRows.ContainsKey(row.KeyName))
670671
{
671-
foreach (var (langCode, cell) in row.Translations.Where(t => t.Value.IsDirty))
672-
{
673-
var request = new UpdateTranslationRequest
674-
{
675-
Value = cell.Value ?? "",
676-
PluralForm = cell.PluralForm,
677-
Status = "translated"
678-
};
672+
_editedRows[row.KeyName] = row;
673+
}
679674

680-
var result = await ResourceService.UpdateTranslationAsync(
681-
ProjectId, row.KeyName, langCode, request);
675+
// Mark that we have unsaved changes
676+
_hasUnsavedChanges = true;
682677

683-
if (result.IsSuccess)
684-
{
685-
cell.IsDirty = false;
686-
cell.OriginalValue = cell.Value;
687-
}
688-
}
678+
// Close the drawer
679+
_drawerOpen = false;
680+
_selectedRow = null;
689681

690-
// Remove from edited rows if no more dirty changes
691-
if (!row.Translations.Values.Any(t => t.IsDirty))
692-
{
693-
_editedRows.Remove(row.KeyName);
694-
}
682+
Snackbar.Add("Changes staged. Click Save to persist.", Severity.Info);
683+
}
695684

696-
Snackbar.Add("Translations saved", Severity.Success);
697-
_hasUnsavedChanges = _editedRows.Any(kv => kv.Value.Translations.Values.Any(t => t.IsDirty));
698-
_stats = await ResourceService.GetProjectStatsAsync(ProjectId);
699-
}
700-
catch (Exception ex)
685+
private async Task TranslateKey(TranslationGridRow row)
686+
{
687+
if (_project == null) return;
688+
689+
var parameters = new DialogParameters
701690
{
702-
Snackbar.Add($"Error saving: {ex.Message}", Severity.Error);
703-
}
704-
finally
691+
{ nameof(TranslateDialog.ProjectId), ProjectId },
692+
{ nameof(TranslateDialog.SourceLanguage), _project.DefaultLanguage },
693+
{ nameof(TranslateDialog.AvailableLanguages), _languages },
694+
{ nameof(TranslateDialog.KeysToTranslate), new List<string> { row.KeyName } }
695+
};
696+
697+
var options = new DialogOptions
705698
{
706-
_isSavingKey = false;
699+
MaxWidth = MaxWidth.Medium,
700+
FullWidth = true,
701+
CloseButton = true,
702+
BackdropClick = false
703+
};
704+
705+
var dialog = await DialogService.ShowAsync<TranslateDialog>("Translate", parameters, options);
706+
var result = await dialog.Result;
707+
708+
if (result is { Canceled: false, Data: TranslateResponseDto translationResult })
709+
{
710+
// Apply translations to the drawer row only (cells will update in place)
711+
// User must click "Apply" to stage changes, then "Save" to persist
712+
ApplyTranslationsToDrawerRow(row, translationResult);
707713
}
708714
}
709715

710-
private async Task TranslateKey(TranslationGridRow row)
716+
/// <summary>
717+
/// Applies translation results to the currently selected drawer row.
718+
/// Unlike ApplyTranslationsToUI, this does NOT stage to _editedRows - the user must click Apply.
719+
/// </summary>
720+
private void ApplyTranslationsToDrawerRow(TranslationGridRow row, TranslateResponseDto translationResult)
711721
{
712-
await OnTranslateSelected(new[] { row });
722+
var appliedCount = 0;
723+
724+
foreach (var translationRes in translationResult.Results.Where(r => r.Success && r.Key == row.KeyName))
725+
{
726+
// For non-plural keys, use language code directly
727+
// For plural keys, the API currently doesn't return plural form, so default to "other"
728+
var cellKey = row.IsPlural
729+
? TranslationGridRow.GetKey(translationRes.TargetLanguage, PluralForms.Other)
730+
: translationRes.TargetLanguage;
731+
732+
if (!row.Translations.TryGetValue(cellKey, out var cell))
733+
{
734+
// Create new cell for this language
735+
cell = new TranslationCell
736+
{
737+
LanguageCode = translationRes.TargetLanguage,
738+
PluralForm = row.IsPlural ? PluralForms.Other : "",
739+
Value = null,
740+
OriginalValue = null
741+
};
742+
row.Translations[cellKey] = cell;
743+
}
744+
745+
// Apply translated text and mark as dirty
746+
cell.Value = translationRes.TranslatedText;
747+
cell.IsDirty = true;
748+
appliedCount++;
749+
}
750+
751+
if (appliedCount > 0)
752+
{
753+
Snackbar.Add($"Applied {appliedCount} translations. Click Apply to stage changes.", Severity.Success);
754+
StateHasChanged();
755+
}
713756
}
714757

715758
private async Task DeleteKey(TranslationGridRow row)

0 commit comments

Comments
 (0)