|
124 | 124 | OnSave="@SaveKeyDetails" |
125 | 125 | OnTranslate="@TranslateKey" |
126 | 126 | OnDelete="@DeleteKey" |
127 | | - OnTypeChanged="@OnKeyTypeChanged" |
128 | | - IsSaving="_isSavingKey" /> |
| 127 | + OnTypeChanged="@OnKeyTypeChanged" /> |
129 | 128 | } |
130 | 129 | </MudDrawerContainer> |
131 | 130 | </MudDrawer> |
|
177 | 176 | private bool _isLoading = true; |
178 | 177 | private bool _isRefreshing; |
179 | 178 | private bool _isSaving; |
180 | | - private bool _isSavingKey; |
181 | 179 | private bool _hasUnsavedChanges; |
182 | 180 | private bool _drawerOpen; |
183 | 181 | private TranslationGridRow? _selectedRow; |
@@ -663,53 +661,98 @@ else |
663 | 661 | await RefreshData(); |
664 | 662 | } |
665 | 663 |
|
666 | | - private async Task SaveKeyDetails(TranslationGridRow row) |
| 664 | + private void SaveKeyDetails(TranslationGridRow row) |
667 | 665 | { |
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)) |
670 | 671 | { |
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 | + } |
679 | 674 |
|
680 | | - var result = await ResourceService.UpdateTranslationAsync( |
681 | | - ProjectId, row.KeyName, langCode, request); |
| 675 | + // Mark that we have unsaved changes |
| 676 | + _hasUnsavedChanges = true; |
682 | 677 |
|
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; |
689 | 681 |
|
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 | + } |
695 | 684 |
|
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 |
701 | 690 | { |
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 |
705 | 698 | { |
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); |
707 | 713 | } |
708 | 714 | } |
709 | 715 |
|
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) |
711 | 721 | { |
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 | + } |
713 | 756 | } |
714 | 757 |
|
715 | 758 | private async Task DeleteKey(TranslationGridRow row) |
|
0 commit comments