From aee4bd78db8d36e59e20a59d69537875e9f190b7 Mon Sep 17 00:00:00 2001 From: Stefan Hildebrandt <695494+hildebrandttk@users.noreply.github.com> Date: Mon, 15 Jun 2026 12:01:17 +0200 Subject: [PATCH] feat: add Edit contact button to email viewer contact sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking an email address in the viewer already shows a contact detail sidebar. An "Edit" button now appears there (for known contacts) that navigates directly to the contact edit form via the existing URL-param intent system (?contactId=…&view=edit), removing the need to open the Contacts page manually and search for the contact. --- app/(main)/[locale]/contacts/page.tsx | 3 +- .../__tests__/contact-sidebar-edit.test.tsx | 106 ++++++++++++++++++ components/email/email-viewer.tsx | 50 ++++++--- locales/cs/common.json | 20 +++- locales/da/common.json | 20 +++- locales/de/common.json | 20 +++- locales/en/common.json | 20 +++- locales/es/common.json | 20 +++- locales/fr/common.json | 20 +++- locales/hu/common.json | 20 +++- locales/it/common.json | 20 +++- locales/ja/common.json | 20 +++- locales/ko/common.json | 20 +++- locales/lv/common.json | 20 +++- locales/nl/common.json | 20 +++- locales/pl/common.json | 20 +++- locales/pt/common.json | 20 +++- locales/ru/common.json | 20 +++- locales/tr/common.json | 20 +++- locales/uk/common.json | 20 +++- locales/zh/common.json | 20 +++- 21 files changed, 484 insertions(+), 35 deletions(-) create mode 100644 components/email/__tests__/contact-sidebar-edit.test.tsx diff --git a/app/(main)/[locale]/contacts/page.tsx b/app/(main)/[locale]/contacts/page.tsx index aac399b4..6deb6a47 100644 --- a/app/(main)/[locale]/contacts/page.tsx +++ b/app/(main)/[locale]/contacts/page.tsx @@ -173,12 +173,13 @@ export default function ContactsPage() { const addEmail = searchParams.get('addEmail'); const addName = searchParams.get('addName'); const from = searchParams.get('from'); + const viewParam = searchParams.get('view'); if (!contactId && !addEmail && !from) return; intentAppliedRef.current = true; if (from === 'email') setReturnToEmail(true); if (contactId) { setSelectedContact(contactId); - setView('detail'); + setView(viewParam === 'edit' ? 'edit' : 'detail'); } else if (addEmail) { setCreatePrefill({ email: addEmail, name: addName ?? undefined }); setSelectedContact(null); diff --git a/components/email/__tests__/contact-sidebar-edit.test.tsx b/components/email/__tests__/contact-sidebar-edit.test.tsx new file mode 100644 index 00000000..d8433779 --- /dev/null +++ b/components/email/__tests__/contact-sidebar-edit.test.tsx @@ -0,0 +1,106 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ContactSidebarPanel } from '../email-viewer'; +import type { ContactCard } from '@/lib/jmap/types'; + +const contact: ContactCard = { + id: 'c1', + addressBookIds: {}, + name: { + components: [ + { kind: 'given', value: 'Alice' }, + { kind: 'surname', value: 'Smith' }, + ], + isOrdered: true, + }, + emails: { e0: { address: 'alice@example.com' } }, +}; + +const unknownEmail = 'unknown@example.com'; + +describe('ContactSidebarPanel', () => { + beforeEach(() => { + Object.assign(navigator, { + clipboard: { writeText: vi.fn().mockResolvedValue(undefined) }, + }); + }); + + it('shows Edit button when contact is known and onEditContact is provided', () => { + render( + , + ); + // useTranslations mock returns the key, so we look for the common.edit key + expect(screen.getByTitle('contact_sidebar.action_edit_title')).toBeInTheDocument(); + expect(screen.getByText('edit')).toBeInTheDocument(); + }); + + it('calls onEditContact when Edit button is clicked', () => { + const onEditContact = vi.fn(); + render( + , + ); + fireEvent.click(screen.getByTitle('contact_sidebar.action_edit_title')); + expect(onEditContact).toHaveBeenCalledOnce(); + }); + + it('does not show Edit button when contact is null', () => { + render( + , + ); + expect(screen.queryByTitle('contact_sidebar.action_edit_title')).not.toBeInTheDocument(); + }); + + it('does not show Edit button when onEditContact is not provided', () => { + render( + , + ); + expect(screen.queryByTitle('contact_sidebar.action_edit_title')).not.toBeInTheDocument(); + }); + + it('shows "not in contacts" message and Add button for unknown email', () => { + const onAddToContacts = vi.fn(); + render( + , + ); + expect(screen.getByText('contact_sidebar.not_in_contacts')).toBeInTheDocument(); + fireEvent.click(screen.getByText('contact_sidebar.add_to_contacts')); + expect(onAddToContacts).toHaveBeenCalledWith(unknownEmail, undefined); + }); + + it('calls onClose when the close button is clicked', () => { + const onClose = vi.fn(); + render( + , + ); + fireEvent.click(screen.getByLabelText('contact_sidebar.close')); + expect(onClose).toHaveBeenCalledOnce(); + }); +}); diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index f5338318..f5b4b986 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -609,19 +609,23 @@ function renderClickableRecipients( } // Contact sidebar panel that slides in from the right on desktop -function ContactSidebarPanel({ +export function ContactSidebarPanel({ email, contact, senderName, onClose, onAddToContacts, + onEditContact, }: { email: string; contact: ContactCard | null; senderName?: string; onClose: () => void; onAddToContacts?: (email: string, name?: string) => void; + onEditContact?: () => void; }) { + const t = useTranslations('email_viewer'); + const tCommon = useTranslations('common'); const name = contact ? getContactDisplayName(contact) : senderName || null; const primaryEmail = contact ? getContactPrimaryEmail(contact) : email; const emails = contact?.emails ? Object.values(contact.emails) : []; @@ -633,9 +637,9 @@ function ContactSidebarPanel({ const handleCopy = async (text: string) => { try { await navigator.clipboard.writeText(text); - toast.success("Copied!"); + toast.success(t('contact_sidebar.copied')); } catch { - toast.error("Failed to copy"); + toast.error(t('contact_sidebar.copy_failed')); } }; @@ -643,11 +647,11 @@ function ContactSidebarPanel({
{/* Header */}
-

Contact

+

{t('contact_sidebar.title')}

@@ -685,19 +689,29 @@ function ContactSidebarPanel({ - Email + {t('contact_sidebar.action_email')} + {contact && onEditContact && ( + + )}
{/* Details sections */} @@ -705,7 +719,7 @@ function ContactSidebarPanel({
{/* Emails */} {emails.length > 0 && ( - + {emails.map((e, i) => (
@@ -725,7 +739,7 @@ function ContactSidebarPanel({ {/* Phones */} {phones.length > 0 && ( - + {phones.map((p, i) => (
@@ -745,7 +759,7 @@ function ContactSidebarPanel({ {/* Organizations */} {orgs.length > 1 && ( - + {orgs.map((o, i) => (
{o.name} @@ -759,7 +773,7 @@ function ContactSidebarPanel({ {/* Addresses */} {addresses.length > 0 && ( - + {addresses.map((a, i) => (
{a.full || a.fullAddress @@ -774,7 +788,7 @@ function ContactSidebarPanel({ {/* Notes */} {notes.length > 0 && ( - + {notes.map((n, i) => (

{n.note}

))} @@ -787,7 +801,7 @@ function ContactSidebarPanel({ {!contact && (

- Not in your contacts + {t('contact_sidebar.not_in_contacts')}

{onAddToContacts && ( )}
@@ -5840,6 +5854,10 @@ export function EmailViewer({ return allRecipients.find(r => r.email.toLowerCase() === contactSidebarEmail.toLowerCase())?.name; })()} onClose={() => setContactSidebarEmail(null)} + onEditContact={sidebarContact ? () => { + router.push(`/contacts?contactId=${sidebarContact.id}&view=edit`); + setContactSidebarEmail(null); + } : undefined} onAddToContacts={(addr, name) => { const { createContact, addLocalContact, supportsSync } = useContactStore.getState(); const client = useAuthStore.getState().client; diff --git a/locales/cs/common.json b/locales/cs/common.json index 45d9a8c7..f5eabb81 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Zadejte nové datum/čas, např. 2026-05-04T15:30", "scheduled_actions_only": "Zobrazení Naplánováno podporuje pouze akce plánování", "undo_send_scheduled": "Zpráva je naplánována k odeslání", - "undo_send": "Vrátit odeslání" + "undo_send": "Vrátit odeslání", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Vyžádáno potvrzení o přečtení (kliknutím vypnete)", diff --git a/locales/da/common.json b/locales/da/common.json index 46d5230d..ff49fcbf 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Indtast en ny dato/tid, f.eks. 2026-05-04T15:30", "scheduled_actions_only": "Planlagt-visningen understøtter kun planlagte handlinger", "undo_send_scheduled": "Besked planlagt til afsendelse", - "undo_send": "Fortryd afsendelse" + "undo_send": "Fortryd afsendelse", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Læsekvittering anmodet (klik for at deaktivere)", diff --git a/locales/de/common.json b/locales/de/common.json index 3634c04f..4b953e1f 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Neues Datum/Uhrzeit eingeben, z. B. 2026-05-04T15:30", "scheduled_actions_only": "Die Geplant-Ansicht unterstützt nur Planungsaktionen", "undo_send_scheduled": "Nachricht ist zum Senden geplant", - "undo_send": "Senden rückgängig" + "undo_send": "Senden rückgängig", + "contact_sidebar": { + "title": "Kontakt", + "close": "Seitenleiste schließen", + "action_email": "E-Mail", + "action_email_title": "E-Mail senden", + "action_copy": "Kopieren", + "action_copy_title": "E-Mail-Adresse kopieren", + "action_edit_title": "Kontakt bearbeiten", + "section_emails": "E-Mail-Adressen", + "section_phones": "Telefonnummern", + "section_organizations": "Organisationen", + "section_addresses": "Adressen", + "section_notes": "Notizen", + "not_in_contacts": "Nicht in Ihren Kontakten", + "add_to_contacts": "Zu Kontakten hinzufügen", + "copied": "Kopiert!", + "copy_failed": "Kopieren fehlgeschlagen" + } }, "email_composer": { "read_receipt_on": "Lesebestätigung angefordert (klicken zum Deaktivieren)", diff --git a/locales/en/common.json b/locales/en/common.json index 8ab1f8b7..f69c0410 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Enter a new date/time, e.g. 2026-05-04T15:30", "scheduled_actions_only": "Scheduled view supports scheduled actions only", "undo_send_scheduled": "Message scheduled for sending", - "undo_send": "Undo send" + "undo_send": "Undo send", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Read receipt requested (click to disable)", diff --git a/locales/es/common.json b/locales/es/common.json index 9fce4e64..4dfdf335 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Introduce una nueva fecha/hora, p. ej. 2026-05-04T15:30", "scheduled_actions_only": "La vista Programados solo admite acciones de programación", "undo_send_scheduled": "Mensaje programado para envío", - "undo_send": "Deshacer envío" + "undo_send": "Deshacer envío", + "contact_sidebar": { + "title": "Contacto", + "close": "Cerrar panel", + "action_email": "Correo", + "action_email_title": "Enviar correo", + "action_copy": "Copiar", + "action_copy_title": "Copiar correo electrónico", + "action_edit_title": "Editar contacto", + "section_emails": "Correos electrónicos", + "section_phones": "Teléfonos", + "section_organizations": "Organizaciones", + "section_addresses": "Direcciones", + "section_notes": "Notas", + "not_in_contacts": "No está en sus contactos", + "add_to_contacts": "Añadir a contactos", + "copied": "¡Copiado!", + "copy_failed": "Error al copiar" + } }, "email_composer": { "read_receipt_on": "Confirmación de lectura solicitada (haz clic para desactivar)", diff --git a/locales/fr/common.json b/locales/fr/common.json index 4dce8ac6..8ad81ea7 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Saisissez une nouvelle date/heure, p. ex. 2026-05-04T15:30", "scheduled_actions_only": "La vue Planifiés prend uniquement en charge les actions de planification", "undo_send_scheduled": "Message planifié pour envoi", - "undo_send": "Annuler l’envoi" + "undo_send": "Annuler l’envoi", + "contact_sidebar": { + "title": "Contact", + "close": "Fermer le panneau", + "action_email": "E-mail", + "action_email_title": "Envoyer un e-mail", + "action_copy": "Copier", + "action_copy_title": "Copier l'adresse e-mail", + "action_edit_title": "Modifier le contact", + "section_emails": "E-mails", + "section_phones": "Téléphones", + "section_organizations": "Organisations", + "section_addresses": "Adresses", + "section_notes": "Notes", + "not_in_contacts": "Pas dans vos contacts", + "add_to_contacts": "Ajouter aux contacts", + "copied": "Copié !", + "copy_failed": "Échec de la copie" + } }, "email_composer": { "read_receipt_on": "Accusé de lecture demandé (cliquez pour désactiver)", diff --git a/locales/hu/common.json b/locales/hu/common.json index a7a0a08e..94054b36 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Adj meg egy új dátumot/időpontot, pl. 2026-05-04T15:30", "scheduled_actions_only": "Az ütemezett nézet csak ütemezett műveleteket támogat", "undo_send_scheduled": "Üzenet ütemezve a küldéshez", - "undo_send": "Küldés visszavonása" + "undo_send": "Küldés visszavonása", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Olvasási visszaigazolás kérve (kattints a letiltáshoz)", diff --git a/locales/it/common.json b/locales/it/common.json index 1aea1658..d5230f11 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Inserisci una nuova data/ora, ad es. 2026-05-04T15:30", "scheduled_actions_only": "La vista Programmate supporta solo azioni di programmazione", "undo_send_scheduled": "Messaggio programmato per l’invio", - "undo_send": "Annulla invio" + "undo_send": "Annulla invio", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Conferma di lettura richiesta (clicca per disattivare)", diff --git a/locales/ja/common.json b/locales/ja/common.json index a1bae1ee..dad6bfec 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "新しい日時を入力してください。例: 2026-05-04T15:30", "scheduled_actions_only": "予約済みビューでは予約アクションのみ利用できます", "undo_send_scheduled": "メッセージは送信予約されています", - "undo_send": "送信を取り消す" + "undo_send": "送信を取り消す", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "開封確認を要求中(クリックで無効化)", diff --git a/locales/ko/common.json b/locales/ko/common.json index c8d35250..f7276533 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "새 날짜/시간을 입력하세요. 예: 2026-05-04T15:30", "scheduled_actions_only": "예약됨 보기에서는 예약 작업만 사용할 수 있습니다", "undo_send_scheduled": "메시지가 보내기로 예약되었습니다", - "undo_send": "보내기 실행 취소" + "undo_send": "보내기 실행 취소", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "읽음 확인 요청됨 (클릭하여 해제)", diff --git a/locales/lv/common.json b/locales/lv/common.json index bafc37b8..b7e2cf33 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Ievadiet jaunu datumu/laiku, piem. 2026-05-04T15:30", "scheduled_actions_only": "Ieplānoto skatā ir pieejamas tikai ieplānošanas darbības", "undo_send_scheduled": "Ziņojums ir ieplānots nosūtīšanai", - "undo_send": "Atsaukt sūtīšanu" + "undo_send": "Atsaukt sūtīšanu", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Pieprasīts lasīšanas apstiprinājums (noklikšķiniet, lai atspējotu)", diff --git a/locales/nl/common.json b/locales/nl/common.json index ef9fb760..501f0fbd 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Voer een nieuwe datum/tijd in, bijv. 2026-05-04T15:30", "scheduled_actions_only": "De geplande weergave ondersteunt alleen geplande acties", "undo_send_scheduled": "Bericht gepland voor verzending", - "undo_send": "Verzenden ongedaan maken" + "undo_send": "Verzenden ongedaan maken", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Leesbevestiging aangevraagd (klik om uit te schakelen)", diff --git a/locales/pl/common.json b/locales/pl/common.json index 4ba6f76d..513c4b6c 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Wprowadź nową datę/godzinę, np. 2026-05-04T15:30", "scheduled_actions_only": "Widok Zaplanowane obsługuje tylko akcje planowania", "undo_send_scheduled": "Wiadomość zaplanowana do wysłania", - "undo_send": "Cofnij wysyłkę" + "undo_send": "Cofnij wysyłkę", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Zażądano potwierdzenia przeczytania (kliknij, aby wyłączyć)", diff --git a/locales/pt/common.json b/locales/pt/common.json index f07df2e2..1b399192 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Informe uma nova data/hora, ex. 2026-05-04T15:30", "scheduled_actions_only": "A visualização Agendados permite apenas ações de agendamento", "undo_send_scheduled": "Mensagem agendada para envio", - "undo_send": "Desfazer envio" + "undo_send": "Desfazer envio", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Confirmação de leitura solicitada (clique para desativar)", diff --git a/locales/ru/common.json b/locales/ru/common.json index 388a0480..6a4305e4 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Введите новую дату/время, например 2026-05-04T15:30", "scheduled_actions_only": "В представлении запланированных доступны только действия планирования", "undo_send_scheduled": "Сообщение запланировано к отправке", - "undo_send": "Отменить отправку" + "undo_send": "Отменить отправку", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Запрошено уведомление о прочтении (нажмите, чтобы отключить)", diff --git a/locales/tr/common.json b/locales/tr/common.json index e44217cc..a168af9c 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Yeni tarih/saat girin, ör. 2026-05-04T15:30", "scheduled_actions_only": "Zamanlandı görünümü yalnızca zamanlama işlemlerini destekler", "undo_send_scheduled": "İleti gönderim için zamanlandı", - "undo_send": "Göndermeyi geri al" + "undo_send": "Göndermeyi geri al", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Okundu bilgisi istendi (devre dışı bırakmak için tıklayın)", diff --git a/locales/uk/common.json b/locales/uk/common.json index 9b5443a5..f748603f 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "Введіть нову дату/час, напр. 2026-05-04T15:30", "scheduled_actions_only": "У поданні Заплановано доступні лише дії планування", "undo_send_scheduled": "Повідомлення заплановано до надсилання", - "undo_send": "Скасувати надсилання" + "undo_send": "Скасувати надсилання", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "Запитано сповіщення про прочитання (натисніть, щоб вимкнути)", diff --git a/locales/zh/common.json b/locales/zh/common.json index cf4bf5ea..c3d99032 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -544,7 +544,25 @@ "reschedule_prompt": "输入新的日期/时间,例如 2026-05-04T15:30", "scheduled_actions_only": "计划发送视图仅支持计划操作", "undo_send_scheduled": "邮件已计划发送", - "undo_send": "撤销发送" + "undo_send": "撤销发送", + "contact_sidebar": { + "title": "Contact", + "close": "Close sidebar", + "action_email": "Email", + "action_email_title": "Send email", + "action_copy": "Copy", + "action_copy_title": "Copy email", + "action_edit_title": "Edit contact", + "section_emails": "Emails", + "section_phones": "Phones", + "section_organizations": "Organizations", + "section_addresses": "Addresses", + "section_notes": "Notes", + "not_in_contacts": "Not in your contacts", + "add_to_contacts": "Add to contacts", + "copied": "Copied!", + "copy_failed": "Failed to copy" + } }, "email_composer": { "read_receipt_on": "已请求已读回执(点击以关闭)",